Last active
February 22, 2020 16:36
-
-
Save robbertkl/f1b37189fd6cd6096c71bd638108cf40 to your computer and use it in GitHub Desktop.
MPCNC milling post processor for Autodesk Fusion 360
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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