Skip to content

Instantly share code, notes, and snippets.

@nathantsoi
Last active January 27, 2019 02:13
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nathantsoi/5ec21bffab10e7c05dfe3f8ee1032e57 to your computer and use it in GitHub Desktop.
Save nathantsoi/5ec21bffab10e7c05dfe3f8ee1032e57 to your computer and use it in GitHub Desktop.
MPCNC Post for Fusion360
/**
Copyright (C) 2017 by NtaajVaaj.
All rights reserved.
MPCNC Mill Laser post processor configuration.
$Revision: 10000 $
$Date: 2014-12-22 13:06:03 +0100 (ma, 22 dec 2014) $
MPCNC posts processor for milling and laser/plasma cutting.
Some design points:
- Setup operation types: Milling, Water/Laser/Plasma
- Only support MM units (inches may work with custom start gcode - NOT TESTED)
- XY and Z independent travel speeds. Rapids are done with G1.
- Arcs support on XY plane
- Tested in Marlin 1.1.0RC8
- Tested with LCD display and SD card (built in tool change require printing from SD and LCD to restart)
- Support for 3 different laser power using "cutting modes" (through, etch, vaporize)
ASSUMES:
- HOME position is not (0,0,0)
- Fan is installed on Ramps' D8 connector
- Fan is enabled at start of operation, and stopped at end of operation
*/
// Adapted from ”Grbl" Autodesk post
description = "DV MPCNC MILL";
vendor = "DV MPCNC";
vendorUrl = "N/A";
legal = "Copyright (C) 2017 by NtaajVaaj";
certificationLevel = 2;
minimumRevision = 24000;
longDescription = "Generic milling post for MPCNC";
extension = "gcode";
setCodePage("ascii");
capabilities = CAPABILITY_MILLING|CAPABILITY_JET;
tolerance = spatial(0.002, MM);
// Arc support variables
minimumChordLength = spatial(0.01, MM);
minimumCircularRadius = spatial(0.01, MM);
maximumCircularRadius = spatial(1000, MM);
minimumCircularSweep = toRad(0.01);
maximumCircularSweep = toRad(180);
allowHelicalMoves = false;
allowedCircularPlanes = undefined;
// user-defined properties
properties = {
writeMachine: true, // write machine
writeTools: true, // writes the tools
useG28: true, // disable to avoid G28 output for safe machine retracts - when disabled you must manually ensure safe retracts
showSequenceNumbers: false, // show sequence numbers
sequenceNumberStart: 10, // first sequence number
sequenceNumberIncrement: 1, // increment for sequence numbers
separateWordsWithSpace: true, // specifies that the words should be separated with a white space
cutterOnThrough: "M106 S200", // GCode command to turn on the laser/plasma cutter in through mode
cutterOnEtch: "M106 S100", // GCode command to turn on the laser/plasma cutter in etch mode
cutterOnVaporize: "M106 S255", // GCode command to turn on the laser/plasma cutter in vaporize mode
cutterOff: "M107", // Gcode command to turn off the laser/plasma cutter
travelSpeedXY: 2500, // High speed for travel movements X & Y (mm/min)
travelSpeedZ: 300, // High speed for travel movements Z (mm/min)
setOriginOnStart: false, // Set origin when gcode start (G92)
goOriginOnFinish: false, // Go X0 Y0 Z0 at gcode end. WILL BE OVERRIDEN BY goHomeOnFinish
goHomeOnFinish: true, // Go HOME at gcode end. Overrides goOriginOnFinish
turnOffMotorsOnFinish: true, // Turn off the steppers at gcode end. MAKE SURE YOUR Z-AXIS DOESN'T
// FALL UNDER NO POWER
toolChangeEnabled: true, // Enable tool change code (bultin tool change requires LCD display)
toolChangeXY: "X0 Y0", // X&Y position for builtin tool change
toolChangeZ: "Z200", // Z position for builtin tool change, should be some big number.
toolChangeZProbe: true, // Z probe after tool change
probeOnStart: true // Execute probe gcode to align tool
};
// user-defined property definitions
propertyDefinitions = {
writeMachine: {title:"Write machine", description:"Output the machine settings in the header of the code.", group:0, type:"boolean"},
writeTools: {title:"Write tool list", description:"Output a tool list in the header of the code.", group:0, type:"boolean"},
useG28: {title:"G28 Safe retracts", description:"Disable to avoid G28 output for safe machine retracts. When disabled, you must manually ensure safe retracts.", type:"boolean"},
showSequenceNumbers: {title:"Use sequence numbers", description:"Use sequence numbers for each block of outputted code.", group:1, type:"boolean"},
sequenceNumberStart: {title:"Start sequence number", description:"The number at which to start the sequence numbers.", group:1, type:"integer"},
sequenceNumberIncrement: {title:"Sequence number increment", description:"The amount by which the sequence number is incremented by in each block.", group:1, type:"integer"},
separateWordsWithSpace: {title:"Separate words with space", description:"Adds spaces between words if 'yes' is selected.", type:"boolean"}
};
var mapCoolantTable = new Table(
[9, 8],
{initial:COOLANT_OFF, force:true},
"Invalid coolant mode"
);
var numberOfToolSlots = 9999;
var gFormat = createFormat({prefix:"G", decimals:0});
var mFormat = createFormat({prefix:"M", decimals:0});
var xyzFormat = createFormat({decimals:3});
var feedFormat = createFormat({decimals:0});
var toolFormat = createFormat({decimals:0});
var rpmFormat = createFormat({decimals:0});
var secFormat = createFormat({decimals:3, forceDecimal:true}); // seconds - range 0.001-1000
var taperFormat = createFormat({decimals:1, scale:DEG});
// Linear outputs
var xOutput = createVariable({prefix:" X"}, xyzFormat);
var yOutput = createVariable({prefix:" Y"}, xyzFormat);
var zOutput = createVariable({prefix:" Z"}, xyzFormat);
var fOutput = createVariable({prefix:" F"}, feedFormat);
var sOutput = createVariable({prefix:"S", force:true}, rpmFormat);
// circular output
var iOutput = createReferenceVariable({prefix:" I"}, xyzFormat);
var jOutput = createReferenceVariable({prefix:" J"}, xyzFormat);
var kOutput = createReferenceVariable({prefix:" K"}, xyzFormat);
var gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ...
var gPlaneModal = createModal({onchange:function () {gMotionModal.reset();}}, gFormat); // modal group 2 // G17-19
var gAbsIncModal = createModal({}, gFormat); // modal group 3 // G90-91
var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G93-94
var gUnitModal = createModal({}, gFormat); // modal group 6 // G20-21
// Misc variables
var WARNING_WORK_OFFSET = 0;
var powerState = false;
var cutterOn;
// collected state
var sequenceNumber;
var currentWorkOffset;
/**
Writes the specified block.
*/
function writeBlock() {
if (properties.showSequenceNumbers) {
writeWords2("N" + sequenceNumber, arguments);
sequenceNumber += properties.sequenceNumberIncrement;
} else {
writeWords(arguments);
}
}
function formatComment(text) {
return ";" + String(text).replace(/[\(\)]/g, "");
}
/**
Output a comment.
*/
function writeComment(text) {
writeln(formatComment(text));
}
// Called in every new gcode file
function onOpen() {
if (!properties.separateWordsWithSpace) {
setWordSeparator("");
}
sequenceNumber = properties.sequenceNumberStart;
writeln("%");
if (programName) {
writeComment(programName);
}
if (programComment) {
writeComment(programComment);
}
// dump machine configuration
var vendor = machineConfiguration.getVendor();
var model = machineConfiguration.getModel();
var description = machineConfiguration.getDescription();
if (properties.writeMachine && (vendor || model || description)) {
writeComment(localize("Machine"));
if (vendor) {
writeComment(" " + localize("vendor") + ": " + vendor);
}
if (model) {
writeComment(" " + localize("model") + ": " + model);
}
if (description) {
writeComment(" " + localize("description") + ": " + description);
}
}
// dump tool information
if (properties.writeTools) {
var zRanges = {};
if (is3D()) {
var numberOfSections = getNumberOfSections();
for (var i = 0; i < numberOfSections; ++i) {
var section = getSection(i);
var zRange = section.getGlobalZRange();
var tool = section.getTool();
if (zRanges[tool.number]) {
zRanges[tool.number].expandToRange(zRange);
} else {
zRanges[tool.number] = zRange;
}
}
}
var tools = getToolTable();
if (tools.getNumberOfTools() > 0) {
for (var i = 0; i < tools.getNumberOfTools(); ++i) {
var tool = tools.getTool(i);
var comment = "T" + toolFormat.format(tool.number) + " " +
"D=" + xyzFormat.format(tool.diameter) + " " +
localize("CR") + "=" + xyzFormat.format(tool.cornerRadius);
if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) {
comment += " " + localize("TAPER") + "=" + taperFormat.format(tool.taperAngle) + localize("deg");
}
if (zRanges[tool.number]) {
comment += " - " + localize("ZMIN") + "=" + xyzFormat.format(zRanges[tool.number].getMinimum());
}
comment += " - " + getToolTypeName(tool.type);
writeComment(comment);
}
}
}
return;
}
// Called in every section
function onSection() {
writeln("\n");
// Tool change
if(properties.toolChangeEnabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) {
toolChange();
}
if( isFirstSection() ) {
writeComment("Homing and Initial Conditions");
writeBlock("G90"); // Set to Absolute Positioning
writeBlock("G21"); // Set Units to Millimeters
writeBlock("M84 S0"); // Disable steppers timeout
if(properties.setOriginOnStart) {
writeBlock("G92 X0 Y0 Z0"); // Set origin to initial position
}
writeBlock("M107"); // turn OFF Laser
writeln("\n");
}
// Machining type
if(currentSection.type == TYPE_MILLING) {
// Specific milling code
writeComment(sectionComment + " - Milling - Tool: " + tool.number + " - " + getToolTypeName(tool.type));
}
if(currentSection.type == TYPE_JET) {
// Cutter mode used for different cutting power in PWM laser
switch (currentSection.jetMode) {
case JET_MODE_THROUGH:
cutterOn = properties.cutterOnThrough;
break;
case JET_MODE_ETCHING:
cutterOn = properties.cutterOnEtch;
break;
case JET_MODE_VAPORIZE:
cutterOn = properties.cutterOnVaporize;
break;
default:
error("Cutting mode is not supported.");
}
writeComment(sectionComment + " - Laser/Plasma - Cutting mode: " + getParameter("operation:cuttingMode"));
}
// Print min/max boundaries for each section
vectorX = new Vector(1,0,0);
vectorY = new Vector(0,1,0);
writeComment("X Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMinimum()) + " - X Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMaximum()));
writeComment("Y Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMinimum()) + " - Y Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMaximum()));
writeComment("Z Min: " + xyzFormat.format(currentSection.getGlobalZRange().getMinimum()) + " - Z Max: " + xyzFormat.format(currentSection.getGlobalZRange().getMaximum()));
// Display section name in LCD
writeBlock("M400");
writeBlock("M117 " + sectionComment);
if(properties.probeOnStart && tool.number != 0) {
probeTool();
}
return;
}
// Feed movements
function onCircular(clockwise, cx, cy, cz, x, y, z, feed) {
circularMovements(clockwise, cx, cy, cz, x, y, z, feed);
return;
}
// Called on waterjet/plasma/laser cuts
function onPower(power) {
if(power != powerState) {
if(power) {
writeBlock(cutterOn);
} else {
writeBlock(properties.cutterOff);
}
powerState = power;
}
return;
}
// Called on Dwell Manual NC invocation
function onDwell(seconds) {
writeComment("Dwell");
writeBlock("G4 S" + seconds);
writeBlock("");
}
// Called with every parameter in the documment/section
function onParameter(name, value) {
// Write gcode initial info
// Product version
if(name == "generated-by") {
writeComment(value);
writeComment("Posts processor: " + FileSystem.getFilename(getConfigurationPath()));
}
// Date
if(name == "generated-at") writeComment("Gcode generated: " + value + " GMT");
// Document
if(name == "document-path") writeComment("Document: " + value);
// Setup
if(name == "job-description") writeComment("Setup: " + value);
// Get section comment
if(name == "operation-comment") sectionComment = value;
return;
}
function onSpindleSpeed(spindleSpeed) {
writeBlock(sOutput.format(spindleSpeed));
}
// Rapid movements with G1 and differentiated travel speeds for XY and Z
function onRapid(_x, _y, _z) {
var x = xOutput.format(_x);
var y = yOutput.format(_y);
var z = zOutput.format(_z);
if(z) {
f = fOutput.format(properties.travelSpeedZ);
fOutput.reset();
writeBlock("G1" + z + f);
}
if(x || y) {
f = fOutput.format(properties.travelSpeedXY);
fOutput.reset();
writeBlock("G1" + x + y + f);
}
return;
}
// Linear movements
function onLinear(_x, _y, _z, _feed) {
var x = xOutput.format(_x);
var y = yOutput.format(_y);
var z = zOutput.format(_z);
var f = fOutput.format(_feed);
if(x || y || z) {
writeBlock("G1" + x + y + z + f);
}
return;
}
function onRapid5D(_x, _y, _z, _a, _b, _c) {
error(localize("Multi-axis motion is not supported."));
}
function onLinear5D(_x, _y, _z, _a, _b, _c, feed) {
error(localize("Multi-axis motion is not supported."));
}
// Circular movements
function circularMovements(_clockwise, _cx, _cy, _cz, _x, _y, _z, _feed) {
// Marlin supports arcs only on XY plane
switch (getCircularPlane()) {
case PLANE_XY:
var x = xOutput.format(_x);
var y = yOutput.format(_y);
var f = fOutput.format(_feed);
var start = getCurrentPosition();
var i = iOutput.format(_cx - start.x, 0);
var j = jOutput.format(_cy - start.y, 0);
if(_clockwise) {
writeBlock("G2" + x + y + i + j + f);
} else {
writeBlock("G3" + x + y + i + j + f);
}
break;
default:
linearize(tolerance);
}
return;
}
// Tool change
function toolChange() {
if(properties.gcodeToolFile == "") {
// Builtin tool change gcode
writeComment("Tool Change");
// Beep
writeBlock("M400"); // Wait movement buffer it's empty
writeBlock("M300 S400 P2000");
// Go to tool change position
if(properties.toolChangeZ != "") {
writeBlock("G1 " + properties.toolChangeZ + fOutput.format(properties.travelSpeedZ));
}
if(properties.toolChangeXY != "") {
writeBlock("G1 " + properties.toolChangeXY + fOutput.format(properties.travelSpeedXY));
}
// Disable Z stepper
writeBlock("M18 Z");
// Ask tool change and wait user to touch lcd button
writeBlock("M0 Put tool " + tool.number + " - " + getToolTypeName(tool.type));
// Run Z probe gcode
if(properties.toolChangeZProbe && tool.number != 0) {
writeComment("Z Probe gcode goes here");
}
writeBlock("");
} else {
// Custom tool change gcode
loadFile(properties.gcodeToolFile);
}
}
// Called in every section end
function onSectionEnd() {
xOutput.reset();
yOutput.reset();
zOutput.reset();
fOutput.reset();
writeBlock("");
return;
}
// Called at end of gcode file
function onClose() {
writeln("\n");
writeComment("Job Complete. Turn off steppers, fans, and go Home")
// End message to LCD
writeBlock("M107"); // turn OFF Controller Fan
writeBlock("M400");
writeBlock("M117 Job end");
if(properties.goHomeOnFinish) {
writeBlock("G28 Z"); // Home Z as to get it high enough clearance
writeBlock("G28 X Y"); // Home X and Y
}
else if (properties.goOriginOnFinish) {
writeBlock("G1 X0 Y0" + fOutput.format(properties.travelSpeedXY)); // Go to XY origin
writeBlock("G1 Z0" + fOutput.format(properties.travelSpeedZ)); // Go to Z origin
}
if(properties.turnOffMotorsOnFinish) {
writeBlock("M18"); // turn off motors to save power
}
return;
}
// Probe tool
function probeTool() {
writeComment("Probe tool - Not yet implemented");
writeBlock("");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment