Alright this what I'm thinking. Modifications from Line 327 to 364:
Here's my version of the MPCNC_Mill_Laser.cps which contains Z Probe code and pretty verbose comments and screen prompts. By the way, does anyone know if you can get g-code to generate a pause with a custom message like "Press button to begin cut" - that would be way better than the generic M00.
This is now updated with Guffy's M0 suggestion below, it's been tested and it works. The Z probe code is embedded and works with pauses for you to connect and remove your probe. Hopefully it is useful for other F360 nuts who like to probe at the start of a job and want to keep all their post processing in one place on the cloud.
Basic Instructions:
Paste the code below in a text editor. Edit the code as you like, especially line 346 to set your probe plate thickness. Save in a text file with a .cps file extension. In Fusion 360, under your list of projects in the left pane, under Libraries open Assets > CamPosts and upload the cps file. You should then be good to select it from in CAM > Actions > Post Process > My Cloud Posts.
Let me know if it should have any other changes.
Credit to the original authors. I think Ryan for original Z-probe code and MartinDB
https://github.com/martindb for mpcnc post processor.
/*
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)
*/
// user-defined properties
properties = {
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: true, // Set origin when gcode start (G92)
goOriginOnFinish: true, // Go X0 Y0 Z0 at gcode end
gcodeStartFile: "", // File with custom Gcode for header/start (in nc folder)
gcodeStopFile: "", // File with custom Gcode for footer/end (in nc folder)
gcodeToolFile: "", // File with custom Gcode for tool change (in nc folder)
gcodeProbeFile: "", // File with custom Gcode for tool probe (in nc folder)
toolChangeEnabled: true, // Enable tool change code (bultin tool change requires LCD display)
toolChangeXY: "X0 Y0", // X&Y position for builtin tool change
toolChangeZ: "Z30", // Z position for builtin tool change
toolChangeZProbe: true, // Z probe after tool change
probeOnStart: true // Execute probe gcode to align tool
};
// Internal properties
extension = "gcode";
setCodePage("ascii");
capabilities = CAPABILITY_MILLING | CAPABILITY_JET;
description = "MPCNC Milling and Laser and Z probe";
// Formats
var xyzFormat = createFormat({decimals:3});
var feedFormat = createFormat({decimals:0});
// 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);
// Circular outputs
var iOutput = createReferenceVariable({prefix:" I"}, xyzFormat);
var jOutput = createReferenceVariable({prefix:" J"}, xyzFormat);
var kOutput = createReferenceVariable({prefix:" K"}, xyzFormat);
// 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;
// Misc variables
var powerState = false;
var cutterOn;
// Called in every new gcode file
function onOpen() {
// See onSection
return;
}
// Called at end of gcode file
function onClose() {
// End message to LCD
writeln("M400");
writeln("M117 Job end");
if(properties.gcodeStopFile == "") {
if(properties.goOriginOnFinish) {
writeln("G1 X0 Y0" + fOutput.format(properties.travelSpeedXY)); // Go to XY origin
writeln("G1 Z0" + fOutput.format(properties.travelSpeedZ)); // Go to Z origin
}
} else {
loadFile(properties.gcodeStopFile);
}
return;
}
// Called in every section
function onSection() {
// Write Start gcode of the documment (after the "onParameters" with the global info)
if(isFirstSection()) {
writeln("");
if(properties.gcodeStartFile == "") {
writeln("G90"); // Set to Absolute Positioning
writeln("G21"); // Set Units to Millimeters
writeln("M84 S0"); // Disable steppers timeout
if(properties.setOriginOnStart) {
writeln("G92 X0 Y0 Z0"); // Set origin to initial position
}
writeln("");
} else {
loadFile(properties.gcodeStartFile);
}
if(properties.probeOnStart && tool.number != 0) {
probeTool();
}
}
// Tool change
if(properties.toolChangeEnabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) {
toolChange();
}
// 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
writeln("M400");
writeln("M117 " + sectionComment);
return;
}
// Called in every section end
function onSectionEnd() {
xOutput.reset();
yOutput.reset();
zOutput.reset();
fOutput.reset();
writeln("");
return;
}
// Rapid movements
function onRapid(x, y, z) {
rapidMovements(x, y, z);
return;
}
// Feed movements
function onLinear(x, y, z, feed) {
linearMovements(x, y, z, feed);
return;
}
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) {
writeln(cutterOn);
} else {
writeln(properties.cutterOff);
}
powerState = power;
}
return;
}
// Called on Dwell Manual NC invocation
function onDwell(seconds) {
writeComment("Dwell");
writeln("G4 S" + seconds);
writeln("");
}
// 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;
}
// Output a comment
function writeComment(text) {
writeln(";" + String(text).replace(/[\(\)]/g, ""));
return;
}
// Rapid movements with G1 and differentiated travel speeds for XY and Z
function rapidMovements(_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();
writeln("G1" + z + f);
}
if(x || y) {
f = fOutput.format(properties.travelSpeedXY);
fOutput.reset();
writeln("G1" + x + y + f);
}
return;
}
// Linear movements
function linearMovements(_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) {
writeln("G1" + x + y + z + f);
}
return;
}
// 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) {
writeln("G2" + x + y + i + j + f);
} else {
writeln("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
writeln("M400"); // Wait movement buffer it's empty
writeln("M300 S400 P2000");
// Go to tool change position
if(properties.toolChangeZ != "") {
writeln("G1 " + properties.toolChangeZ + fOutput.format(properties.travelSpeedZ));
}
if(properties.toolChangeXY != "") {
writeln("G1 " + properties.toolChangeXY + fOutput.format(properties.travelSpeedXY));
}
// Disable Z stepper
writeln("M18 Z");
// Ask tool change and wait user to touch lcd button
writeln("M0 Put tool " + tool.number + " - " + getToolTypeName(tool.type));
// Run Z probe gcode
if(properties.toolChangeZProbe && tool.number != 0) {
writeComment("Z Probe gcode goes here");
}
writeln("");
} else {
// Custom tool change gcode
loadFile(properties.gcodeToolFile);
}
}
// Probe tool
function probeTool() {
if(properties.gcodeProbeFile == "") {
writeln("");
writeComment("########################################");
writeComment("Z Probing Code - be sure to enter your plate thickness below");
writeln("M117 Raising Z for travel");
writeln("G4 S2; wait in seconds");
writeln("G00 Z5.0000 F500 ; Raise Z 5mm at 500mm/min=8.3mm/s to clear clamps and screws");
writeln("");
writeln("M117 About to home X and Y");
writeln("G4 S2; wait in seconds");
writeln("G28 X Y ; Home in order");
writeln("");
writeln("M0 Click when probe is on.");
writeln("G28 Z; Home Z");
writeln("");
writeln("M117 Compensating for plate thickness");
writeln("G4 S2; wait in seconds");
writeln("G92 Z1.1; Account for probe thickness (set your thickness here)");
writeln("");
writeln("G00 Z5.0000 F500 ; Raise bit off surface");
writeln("");
writeln("M0 Click when probe removed.");
writeln("");
writeln("M117 Tool should be home,5mm up.");
writeln("G4 S10; wait in seconds");
writeln("");
writeln("M0 Ready to Cut?");
writeComment("########################################");
writeln("");
} else {
loadFile(properties.gcodeProbeFile);
}
}
// Test if file exist/can read and load it
function loadFile(_file) {
var folder = FileSystem.getFolderPath(getOutputPath()) + PATH_SEPARATOR;
if(FileSystem.isFile( folder + _file)) {
var txt = loadText( folder + _file, "utf-8");
if ( txt.length > 0 ) {
writeComment("Start custom gcode " + folder + _file);
write(txt);
writeComment("End custom gcode " + folder + _file);
writeln("");
}
} else {
writeComment("Can't open file " + folder + _file);
error("Can't open file " + folder + _file);
}
}
/*
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)
*/
// user-defined properties
properties = {
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: true, // Set origin when gcode start (G92)
goOriginOnFinish: true, // Go X0 Y0 Z0 at gcode end
gcodeStartFile: "", // File with custom Gcode for header/start (in nc folder)
gcodeStopFile: "", // File with custom Gcode for footer/end (in nc folder)
gcodeToolFile: "", // File with custom Gcode for tool change (in nc folder)
gcodeProbeFile: "", // File with custom Gcode for tool probe (in nc folder)
toolChangeEnabled: true, // Enable tool change code (bultin tool change requires LCD display)
toolChangeXY: "X0 Y0", // X&Y position for builtin tool change
toolChangeZ: "Z30", // Z position for builtin tool change
toolChangeZProbe: true, // Z probe after tool change
probeOnStart: true // Execute probe gcode to align tool
};
// Internal properties
extension = "gcode";
setCodePage("ascii");
capabilities = CAPABILITY_MILLING | CAPABILITY_JET;
description = "MPCNC Milling and Laser and Z probe";
// Formats
var xyzFormat = createFormat({decimals:3});
var feedFormat = createFormat({decimals:0});
// 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);
// Circular outputs
var iOutput = createReferenceVariable({prefix:" I"}, xyzFormat);
var jOutput = createReferenceVariable({prefix:" J"}, xyzFormat);
var kOutput = createReferenceVariable({prefix:" K"}, xyzFormat);
// 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;
// Misc variables
var powerState = false;
var cutterOn;
// Called in every new gcode file
function onOpen() {
// See onSection
return;
}
// Called at end of gcode file
function onClose() {
// End message to LCD
writeln("M400");
writeln("M117 Job end");
if(properties.gcodeStopFile == "") {
if(properties.goOriginOnFinish) {
writeln("G1 X0 Y0" + fOutput.format(properties.travelSpeedXY)); // Go to XY origin
writeln("G1 Z0" + fOutput.format(properties.travelSpeedZ)); // Go to Z origin
}
} else {
loadFile(properties.gcodeStopFile);
}
return;
}
// Called in every section
function onSection() {
// Write Start gcode of the documment (after the "onParameters" with the global info)
if(isFirstSection()) {
writeln("");
if(properties.gcodeStartFile == "") {
writeln("G90"); // Set to Absolute Positioning
writeln("G21"); // Set Units to Millimeters
writeln("M84 S0"); // Disable steppers timeout
if(properties.setOriginOnStart) {
writeln("G92 X0 Y0 Z0"); // Set origin to initial position
}
writeln("");
} else {
loadFile(properties.gcodeStartFile);
}
if(properties.probeOnStart && tool.number != 0) {
probeTool();
}
}
// Tool change
if(properties.toolChangeEnabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) {
toolChange();
}
// 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
writeln("M400");
writeln("M117 " + sectionComment);
return;
}
// Called in every section end
function onSectionEnd() {
xOutput.reset();
yOutput.reset();
zOutput.reset();
fOutput.reset();
writeln("");
return;
}
// Rapid movements
function onRapid(x, y, z) {
rapidMovements(x, y, z);
return;
}
// Feed movements
function onLinear(x, y, z, feed) {
linearMovements(x, y, z, feed);
return;
}
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) {
writeln(cutterOn);
} else {
writeln(properties.cutterOff);
}
powerState = power;
}
return;
}
// Called on Dwell Manual NC invocation
function onDwell(seconds) {
writeComment("Dwell");
writeln("G4 S" + seconds);
writeln("");
}
// 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;
}
// Output a comment
function writeComment(text) {
writeln(";" + String(text).replace(/[\(\)]/g, ""));
return;
}
// Rapid movements with G1 and differentiated travel speeds for XY and Z
function rapidMovements(_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();
writeln("G1" + z + f);
}
if(x || y) {
f = fOutput.format(properties.travelSpeedXY);
fOutput.reset();
writeln("G1" + x + y + f);
}
return;
}
// Linear movements
function linearMovements(_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) {
writeln("G1" + x + y + z + f);
}
return;
}
// 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) {
writeln("G2" + x + y + i + j + f);
} else {
writeln("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
writeln("M400"); // Wait movement buffer it's empty
writeln("M300 S400 P2000");
// Go to tool change position
if(properties.toolChangeZ != "") {
writeln("G1 " + properties.toolChangeZ + fOutput.format(properties.travelSpeedZ));
}
if(properties.toolChangeXY != "") {
writeln("G1 " + properties.toolChangeXY + fOutput.format(properties.travelSpeedXY));
}
// Disable Z stepper
writeln("M18 Z");
// Ask tool change and wait user to touch lcd button
writeln("M0 Put tool " + tool.number + " - " + getToolTypeName(tool.type));
// Run Z probe gcode
if(properties.toolChangeZProbe && tool.number != 0) {
writeComment("Z Probe gcode goes here");
}
writeln("");
} else {
// Custom tool change gcode
loadFile(properties.gcodeToolFile);
}
}
// Probe tool
function probeTool() {
if(properties.gcodeProbeFile == "") {
writeln("");
writeComment("########################################");
writeComment("Z Probing Code - be sure to enter your plate thickness below");
writeln("M117 Raising Z for travel");
writeln("G4 S2; wait in seconds");
writeln("G00 Z5.0000 F500 ; Raise Z 5mm at 500mm/min=8.3mm/s to clear clamps and screws");
writeln("");
writeln("M117 About to home X and Y");
writeln("G4 S2; wait in seconds");
writeln("G28 X Y ; Home in order");
writeln("");
writeln("M117 Place probe on bit!");
writeln("G4 S10; wait in seconds");
writeln("M00 ; pause for LCD button press");
writeln("G28 Z; Home Z");
writeln("");
writeln("M117 Compensating for plate thickness");
writeln("G4 S2; wait in seconds");
writeln("G92 Z1.1; Account for probe thickness (set your thickness here)");
writeln("");
writeln("G00 Z5.0000 F500 ; Raise bit off surface");
writeln("");
writeln("M117 Detach Probe Now");
writeln("G4 S20; wait in seconds");
writeln("");
writeln("M117 Bit should be home and 5mm up.");
writeln("G4 S10; wait in seconds");
writeln("");
writeln("M117 M00: Wait for press About to cut");
writeln("G4 S2; wait in seconds");
writeln("M00 ; pause for LCD button press");
writeComment("########################################");
writeln("");
} else {
loadFile(properties.gcodeProbeFile);
}
}
// Test if file exist/can read and load it
function loadFile(_file) {
var folder = FileSystem.getFolderPath(getOutputPath()) + PATH_SEPARATOR;
if(FileSystem.isFile( folder + _file)) {
var txt = loadText( folder + _file, "utf-8");
if ( txt.length > 0 ) {
writeComment("Start custom gcode " + folder + _file);
write(txt);
writeComment("End custom gcode " + folder + _file);
writeln("");
}
} else {
writeComment("Can't open file " + folder + _file);
error("Can't open file " + folder + _file);
}
}