Skip to content

Instantly share code, notes, and snippets.

@robbertkl
Last active February 22, 2020 16:36
Show Gist options
  • Save robbertkl/f1b37189fd6cd6096c71bd638108cf40 to your computer and use it in GitHub Desktop.
Save robbertkl/f1b37189fd6cd6096c71bd638108cf40 to your computer and use it in GitHub Desktop.
MPCNC milling post processor for Autodesk Fusion 360
/**
* Global info
*/
description = "MPCNC";
longDescription = "Basic MPCNC milling post processor.";
vendor = "V1 Engineering";
vendorUrl = "https://www.v1engineering.com";
extension = "gcode";
setCodePage("ascii");
capabilities = CAPABILITY_MILLING;
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 = {};
propertyDefinitions = {};
var PROPERTY = {};
var propertyNumber = 100;
function newProperty(id, value, definition) {
// Use numerical names to get a pretty ordered list
var name = "_" + (propertyNumber++) + "_" + id.toLowerCase();
PROPERTY[id] = name;
properties[name] = value;
propertyDefinitions[name] = definition;
}
var TARGET = {
SDCARD: "sdcard",
REPETIER: "repetier",
};
newProperty("TARGET", TARGET.SDCARD, {
title: "Target",
type: "enum",
values: [
{id: TARGET.SDCARD, title: "SD card"},
{id: TARGET.REPETIER, title: "Repetier-Host"},
],
});
var ORIGIN = {
MACHINE: "machine",
START: "start",
MANUAL: "manual",
};
newProperty("ORIGIN", ORIGIN.START, {
title: "Origin",
type: "enum",
values: [
{id: ORIGIN.MACHINE, title: "Machine (homing)"},
{id: ORIGIN.START, title: "Initial position"},
{id: ORIGIN.MANUAL, title: "Manual (pre-calibrated)"},
],
});
newProperty("MANAGE_SPEED", true, {
title: "Spindle speed changes",
description: "Pause program to prompt user to change spindle speed.",
type: "boolean"
});
newProperty("TOOLCHANGE_X", 50, {
title: "Tool changing X",
description: "X location for tool changes.",
type: "integer",
});
newProperty("TOOLCHANGE_Y", 50, {
title: "Tool changing Y",
description: "Y location for tool changes.",
type: "integer",
});
newProperty("TOOLCHANGE_Z", 20, {
title: "Tool changing Z",
description: "Z location for tool changes.",
type: "integer",
});
newProperty("RAPID_XY", 2500, {
title: "Travel speed XY (mm/min)",
description: "Speed for rapid movements in X or Y directions.",
type: "integer",
});
newProperty("RAPID_Z", 300, {
title: "Travel speed Z (mm/min)",
description: "Speed for rapid movements in Z direction.",
type: "integer",
});
/**
* Helpers
*/
var isSpindleOn = null;
var spindleSpeed = null;
var didAutoHome = false;
var displayMessage = null;
var xyzFormat = createFormat({decimals: 3});
var feedFormat = createFormat({decimals: 0});
var sFormat = createFormat({prefix: " S", decimals: 0});
var pFormat = createFormat({prefix: " P", decimals: 0});
var xVariable = createVariable({prefix:" X"}, xyzFormat);
var yVariable = createVariable({prefix:" Y"}, xyzFormat);
var zVariable = createVariable({prefix:" Z"}, xyzFormat);
var fVariable = createVariable({prefix:" F"}, feedFormat);
var iVariable = createReferenceVariable({prefix:" I"}, xyzFormat);
var jVariable = createReferenceVariable({prefix:" J"}, xyzFormat);
var kVariable = createReferenceVariable({prefix:" K"}, xyzFormat);
function writeComment(text) {
writeln("; " + text);
}
function writeHeader(title) {
writeln("");
writeComment("====== " + title + " ======");
}
function display(message) {
if (properties[PROPERTY.TARGET] == TARGET.REPETIER) {
// Repetier-Host does not handle M117 well, so skip it completely
return;
}
writeln("M117 " + message);
// Store display message to repeat it after a user prompt
displayMessage = message;
}
function beep(comment) {
writeln("M300 P30" + (comment ? " ; " + comment : ""));
}
function multiBeep(count, pause, comment) {
for (var i = 0; i < count; i++) {
if (i > 0) writeln("G4" + pFormat.format(pause));
beep(comment);
comment = null;
}
}
function multiMultiBeep(count, pause, count2, pause2, comment) {
for (var i = 0; i < count2; i++) {
if (i > 0) writeln("G4" + pFormat.format(pause2));
multiBeep(count, pause, comment);
comment = null;
}
}
function promptUser(message) {
writeln("M400 ; finish moves");
if (properties[PROPERTY.TARGET] == TARGET.REPETIER) {
// @pause does some movements, save/restore origin to prevent this
var x = xVariable.getCurrent();
var y = yVariable.getCurrent();
var z = zVariable.getCurrent();
writeln("G92 X0 Y0 Z0 ; reset origin for @pause");
xVariable.reset();
yVariable.reset();
zVariable.reset();
writeln("@pause " + message);
x = xVariable.format(x);
y = yVariable.format(y);
z = zVariable.format(z);
if (x || y || z) writeln("G92" + x + y + z + " ; restore after @pause");
fVariable.reset();
} else {
beep("short beep to inform user")
writeln("M0 " + message);
if (displayMessage) display(displayMessage);
}
}
function ensureSpindleOn(speed) {
speed = clamp(5000, speed, 25000);
if (properties[PROPERTY.MANAGE_SPEED] && (spindleSpeed === null || Math.abs(spindleSpeed - speed) >= 1000)) {
ensureSpindleOff();
var kressSetting = Math.round((speed - 500) / 4000);
promptUser("Set speed " + kressSetting + " (" + speed + " RPM)");
writeln("G4 S1 ; wait after click");
}
spindleSpeed = speed;
if (isSpindleOn === true) return;
writeln("M400 ; finish moves");
if (properties[PROPERTY.TARGET] == TARGET.REPETIER) {
writeln("@sound");
} else {
multiMultiBeep(3, 100, 3, 200, "sound warning alarm");
}
writeln("G4 S1 ; wait after alarm");
writeln("M106 ; turn on spindle");
writeln("G4 S3 ; wait for spindle to ramp up");
isSpindleOn = true;
}
function ensureSpindleOff(skipWait) {
if (isSpindleOn === false) return;
writeln("M400 ; finish moves");
writeln("M107 ; turn off spindle");
if (!skipWait) {
// We need a second of time for each 5000 RPM
var waitTime = spindleSpeed ? Math.round(spindleSpeed / 5000) : 0;
writeln("G4" + sFormat.format(waitTime) + " ; wait for spindle to ramp down");
}
isSpindleOn = false;
}
function autoHome() {
display("Auto homing");
ensureSpindleOff();
writeln("G28 ; auto home to touch plate");
xVariable.reset();
yVariable.reset();
zVariable.reset();
var z = zVariable.format(5);
var f = fVariable.format(properties[PROPERTY.RAPID_Z]);
writeln("G1" + z + f + " ; back off a bit");
fVariable.reset();
didAutoHome = true;
}
function moveForToolChange() {
// It's only safe to move when we've already homed before
if (didAutoHome) {
onRapid(properties[PROPERTY.TOOLCHANGE_X], properties[PROPERTY.TOOLCHANGE_Y], properties[PROPERTY.TOOLCHANGE_Z]);
}
}
function toolChange(tool) {
writeHeader("Tool change: #" + tool.number + " (" + tool.getDescription() + ")");
ensureSpindleOff(true);
moveForToolChange();
writeln("M18 Z ; disable Z stepper");
promptUser("Insert " + tool.getDescription());
autoHome();
}
/**
* Post processor callbacks
*/
function onOpen() {
if (getToolTable().getNumberOfTools() > 1 && properties[PROPERTY.ORIGIN] != ORIGIN.MACHINE) {
error("Machine origin is required when using multiple tools");
}
if (unit != MM) {
error("Only millimeter units are supported");
}
}
function onClose() {
writeHeader("Termination");
ensureSpindleOff(true);
moveForToolChange();
display("Job done");
writeln("M18 ; disable steppers");
}
function onSection() {
if (isFirstSection()) {
writeHeader("Initialization");
ensureSpindleOff(true);
writeln("G90 ; absolute positioning");
writeln("G21 ; millimeter units");
writeln("M18 S0 ; disable stepper timeouts");
if (properties[PROPERTY.ORIGIN] == ORIGIN.START) {
writeln("G92 X0 Y0 Z0 ; set origin to initial position");
}
}
if (isFirstSection()
|| (currentSection.getForceToolChange && currentSection.getForceToolChange())
|| tool.number != getPreviousSection().getTool().number
) {
toolChange(tool)
}
var title = getParameter("operation-comment")
writeHeader("Operation: " + title);
display(title);
var initialPosition = currentSection.getInitialPosition();
onRapid(initialPosition.x, initialPosition.y, initialPosition.z);
ensureSpindleOn(currentSection.getInitialSpindleSpeed());
}
function onSectionEnd() {
xVariable.reset();
yVariable.reset();
zVariable.reset();
fVariable.reset();
}
function onParameter(name, value) {
switch (name) {
case "generated-by":
writeComment("Generated by: " + value);
break;
case "generated-at":
writeComment("Generated at: " + value + " (GMT)");
writeComment("Posts processor: " + FileSystem.getFilename(getConfigurationPath()));
for (var i = 0; i < propertyDefinitions[PROPERTY.TARGET].values.length; i++) {
if (propertyDefinitions[PROPERTY.TARGET].values[i].id !== properties[PROPERTY.TARGET]) continue;
writeComment("Target: " + propertyDefinitions[PROPERTY.TARGET].values[i].title);
}
break;
case "document-path":
writeComment("File: " + value);
break;
case "job-description":
writeComment("Setup: " + value);
break;
case "print":
case "display":
writeHeader("Display on screen");
display(value);
break;
}
}
function onRapid(x, y, z) {
var zGoingDown = z < zVariable.getCurrent();
x = xVariable.format(x);
y = yVariable.format(y);
z = zVariable.format(z);
if (z && !zGoingDown) {
var f = fVariable.format(properties[PROPERTY.RAPID_Z]);
fVariable.reset();
writeln("G1" + z + f);
}
if (x || y) {
var f = fVariable.format(properties[PROPERTY.RAPID_XY]);
fVariable.reset();
writeln("G1" + x + y + f);
}
if (z && zGoingDown) {
var f = fVariable.format(properties[PROPERTY.RAPID_Z]);
fVariable.reset();
writeln("G1" + z + f);
}
}
function onLinear(x, y, z, feedrate) {
x = xVariable.format(x);
y = yVariable.format(y);
z = zVariable.format(z);
if (!x && !y && !z) return;
f = fVariable.format(feedrate); // Make sure this line is below the return
writeln("G1" + x + y + z + f);
}
function onCircular(clockwise, cx, cy, cz, x, y, z, feedrate) {
switch (getCircularPlane()) {
case PLANE_XY:
var command = clockwise ? "G2" : "G3";
var start = getCurrentPosition();
var i = iVariable.format(cx - start.x, 0);
var j = jVariable.format(cy - start.y, 0);
x = xVariable.format(x);
y = yVariable.format(y);
f = fVariable.format(feedrate);
writeln(command + " " + x + y + i + j + f);
break;
default:
linearize(tolerance);
}
}
function onComment(comment) {
writeHeader("Comment");
writeComment(comment);
}
function onDwell(seconds) {
writeHeader("Dwell");
writeln("G4" + sFormat.format(seconds) + " ; wait for " + seconds + " seconds");
}
function onPassThrough(value) {
writeHeader("PassThrough");
writeln(value);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment