From bb48116ac765f67be084018d9d726afd3ee1bbc0 Mon Sep 17 00:00:00 2001 From: flyfisher604 Date: Sun, 7 Mar 2021 13:20:48 -0800 Subject: [PATCH] Merge branch 'v1.beta5' --- DIYCNC_Common.js | 1349 -------------------------------- DIYCNC_Grbl11.cps | 147 ---- DIYCNC_Marlin20.cps | 21 - DIYCNC_RepRapFW.cps | 65 -- MPCNC.cps | 1812 +++++++++++++++++++++++++++++++++++++++++++ README.md | 701 +++-------------- 6 files changed, 1906 insertions(+), 2189 deletions(-) delete mode 100644 DIYCNC_Common.js delete mode 100644 DIYCNC_Grbl11.cps delete mode 100644 DIYCNC_Marlin20.cps delete mode 100644 DIYCNC_RepRapFW.cps create mode 100644 MPCNC.cps diff --git a/DIYCNC_Common.js b/DIYCNC_Common.js deleted file mode 100644 index f083579..0000000 --- a/DIYCNC_Common.js +++ /dev/null @@ -1,1349 +0,0 @@ -/* - -https://github.com/flyfisher604/mpcnc_post_processor - -MPCNC posts processor for milling and laser/plasma cutting. - -*/ - -// Internal properties -certificationLevel = 2; -extension = "gcode"; -setCodePage("ascii"); -capabilities = CAPABILITY_MILLING | CAPABILITY_JET; - -// vendor of MPCNC -vendor = "flyfisher604"; -vendorUrl = "https://github.com/flyfisher604/mpcnc_post_processor"; - - -// user-defined properties -properties = { - jobManualSpindlePowerControl: true, // Spindle motor is controlled by manual switch - - jobUseArcs: true, // Produce G2/G3 for arcs - - jobSetOriginOnStart: true, // Set origin when gcode start (G92) - jobGoOriginOnFinish: true, // Go X0 Y0 Z0 at gcode end - - jobSequenceNumbers: false, // show sequence numbers - jobSequenceNumberStart: 10, // first sequence number - jobSequenceNumberIncrement: 1, // increment for sequence numbers - jobSeparateWordsWithSpace: true, // specifies that the words should be separated with a white space - - jobDuetMillingMode: "M453 P2 I0 R30000 F200", // GCode command to setup Duet3d milling mode - jobDuetLaserMode: "M452 P2 I0 R255 F200", // GCode command to setup Duet3d laser mode - - fr0_TravelSpeedXY: 2500, // High speed for travel movements X & Y (mm/min) - fr1_TravelSpeedZ: 300, // High speed for travel movements Z (mm/min) - frA_ScaleFeedrate: false, // Will feedrated be scaled - frB_MaxCutSpeedXY: 900, // Max speed for cut movements X & Y (mm/min) - frC_MaxCutSpeedZ: 180, // Max speed for cut movements Z (mm/min) - frD_MaxCutSpeedXYZ: 1000, // Max feedrate after scaling - - mapD_RestoreFirstRapids: false, // Map first G01 --> G00 - mapE_RestoreRapids: false, // Map G01 --> G00 for SafeTravelsAboveZ - mapF_SafeZ: 10, // G01 mapped to G00 if Z is >= jobSafeZRapid - mapG_AllowRapidZ: true, // Allow G01 --> G00 for vertical retracts and Z descents above safe - - toolChangeEnabled: true, // Enable tool change code (bultin tool change requires LCD display) - toolChangeX: 0, // X position for builtin tool change - toolChangeY: 0, // Y position for builtin tool change - toolChangeZ: 40, // Z position for builtin tool change - toolChangeZProbe: true, // Z probe after tool change - toolChangeDisableZStepper: false, // disable Z stepper when change a tool - - probeOnStart: true, // Execute probe gcode to align tool - probeThickness: 0.8, // plate thickness - probeUseHomeZ: true, // use G28 or G38 for probing - probeG38Target: -10, // probing up to pos - probeG38Speed: 30, // probing with speed - - 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) - - cutterOnVaporize: 100, // Persent of power to turn on the laser/plasma cutter in vaporize mode - cutterOnThrough: 80, // Persent of power to turn on the laser/plasma cutter in through mode - cutterOnEtch: 40, // Persent of power to turn on the laser/plasma cutter in etch mode - - coolantA_Mode: 0, // Enable issuing g-codes for control Coolant channel A - coolantB_Mode: 0, // Use issuing g-codes for control Coolant channel B - - commentWriteTools: true, - commentActivities: true, - commentSections: true, - commentCommands: true, - commentMovements: true, -}; - -propertyDefinitions = { - /* - jobFirmware: { - title: "Job: Firmware", description: "GCode output mode", group: 1, - type: "integer", default_mm: 0, default_in: 0, - values: [ - { title: "Don's Marlin 2.0/Repetier 1.0.3", id: 0 }, - { title: "GRBL 1.1", id: 1 }, - { title: "RepRap firmware (Duet)", id: 2 }, - ] - }, - */ - jobManualSpindlePowerControl: { - title: "Job: Manual Spindle On/Off", description: "Set Yes when your spindle motor is controlled by manual switch", group: 1, - type: "boolean", default_mm: true, default_in: true - }, - jobUseArcs: { - title: "Job: Use Arcs", description: "Use G2/G3 g-codes fo circular movements", group: 1, - type: "boolean", default_mm: true, default_in: true - }, - - jobMarlinEnforcFeFeedrate: { - title: "Job: Marlin Enforce Feedrate", description: "Add feedrate to each movement g-code", group: 1, - type: "boolean", default_mm: false, default_in: false - }, - - jobSetOriginOnStart: { - title: "Job: Reset on start (G92)", description: "Set origin when gcode start (G92)", group: 1, - type: "boolean", default_mm: true, default_in: true - }, - jobGoOriginOnFinish: { - title: "Job: Goto 0, 0 at end", description: "Go X0 Y0 at gcode end", group: 1, - type: "boolean", default_mm: true, default_in: true - }, - - jobSequenceNumbers: { - title: "Job: Line numbers", description: "Show sequence numbers", group: 1, - type: "boolean", default_mm: false, default_in: false - }, - jobSequenceNumberStart: { - title: "Job: Line # start", description: "First sequence number", group: 1, - type: "integer", default_mm: 10, default_in: 10 - }, - jobSequenceNumberIncrement: { - title: "Job: Line # increment", description: "Increment for sequence numbers", group: 1, - type: "integer", default_mm: 1, default_in: 1 - }, - jobSeparateWordsWithSpace: { - title: "Job: Separate words", description: "Specifies that the words should be separated with a white space", group: 1, - type: "boolean", default_mm: true, default_in: true - }, - jobDuetMillingMode: { - title: "Job: Duet Milling mode", description: "GCode command to setup Duet3d milling mode", group: 1, - type: "string", default_mm: "M453 P2 I0 R30000 F200", default_in: "M453 P2 I0 R30000 F200" - }, - jobDuetLaserMode: { - title: "Job: Duet Laser mode", description: "GCode command to setup Duet3d laser mode", group: 1, - type: "string", default_mm: "M452 P2 I0 R255 F200", default_in: "M452 P2 I0 R255 F200" - }, - - - fr0_TravelSpeedXY: { - title: "Feed: Travel speed X/Y", description: "High speed for Rapid movements X & Y (mm/min; in/min)", group: 2, - type: "spatial", default_mm: 2500, default_in: 100 - }, - fr1_TravelSpeedZ: { - title: "Feed: Travel Speed Z", description: "High speed for Rapid movements z (mm/min; in/min)", group: 2, - type: "spatial", default_mm: 300, default_in: 12 - }, - frA_ScaleFeedrate: { - title: "Feed: Scale feedrate", description: "Scale feedrate based on X, Y, Z axis maximums", group: 2, - type: "boolean", default_mm: false, default_in: false - }, - frB_MaxCutSpeedXY: { - title: "Feed: Max cut speed X or Y", description: "Max X or Y axis cut speed (mm/min; in/min)", group: 2, - type: "spatial", default_mm: 900, default_in: 35.43 - }, - frC_MaxCutSpeedZ: { - title: "Feed: Max cut speed Z", description: "Max Z axis cut speed (mm/min; in/min)", group: 2, - type: "spatial", default_mm: 180, default_in: 7.08 - }, - frD_MaxCutSpeedXYZ: { - title: "Feed: Max toolpath speed", description: "After scaling limit feedrate to this (mm/min; in/min)", group: 2, - type: "spatial", default_mm: 1000, default_in: 39.37 - }, - - - mapD_RestoreFirstRapids: { - title: "Map: First G1 -> G0 Rapids", description: "Convert first G1 of a cut to G0 Rapids", group: 3, - type: "boolean", default_mm: false, default_in: false - }, - mapE_RestoreRapids: { - title: "Map: G1 -> G0 Rapids", description: "When safe, convert G1s to G0 Rapids", group: 3, - type: "boolean", default_mm: false, default_in: false - }, - mapF_SafeZ: { - title: "Map: Safe Z for Rapids", description: "Z must be above or equal to this to map G01 --> G00", group: 3, - type: "integer", default_mm: 10, default_in: 0.590551 - }, - mapG_AllowRapidZ: { - title: "Map: Allow Rapid Z", description: "If G01 to Rapids allowed, then include vertical retracts and safe descents", group: 3, - type: "boolean", default_mm: true, default_in: true - }, - - - toolChangeEnabled: { - title: "Change: Enabled", description: "Enable tool change code (bultin tool change requires LCD display)", group: 4, - type: "boolean", default_mm: true, default_in: true - }, - toolChangeX: { - title: "Change: X", description: "X position for builtin tool change", group: 4, - type: "spatial", default_mm: 0, default_in: 0 - }, - toolChangeY: { - title: "Change: Y", description: "Y position for builtin tool change", group: 4, - type: "spatial", default_mm: 0, default_in: 0 - }, - toolChangeZ: { - title: "Change: Z ", description: "Z position for builtin tool change", group: 4, - type: "spatial", default_mm: 40, default_in: 1.6 - }, - toolChangeZProbe: { - title: "Change: Make Z Probe", description: "Z probe after tool change", group: 4, - type: "boolean", default_mm: true, default_in: true - }, - toolChangeDisableZStepper: { - title: "Change: Disable Z stepper", description: "Disable Z stepper when change a tool", group: 4, - type: "boolean", default_mm: false, default_in: false - }, - - probeOnStart: { - title: "Probe: On job start", description: "Execute probe gcode on job start", group: 5, - type: "boolean", default_mm: true, default_in: true - }, - probeThickness: { - title: "Probe: Plate thickness", description: "Plate thickness", group: 5, - type: "spatial", default_mm: 0.8, default_in: 0.032 - }, - probeUseHomeZ: { - title: "Probe: Use Home Z", description: "Use G28 or G38 for probing", group: 5, - type: "boolean", default_mm: true, default_in: true - }, - probeG38Target: { - title: "Probe: G38 target", description: "Probing up to Z position", group: 5, - type: "spatial", default_mm: -10, default_in: -0.5 - }, - probeG38Speed: { - title: "Probe: G38 speed", description: "Probing with speed (mm/min; in/min)", group: 5, - type: "spatial", default_mm: 30, default_in: 1.2 - }, - - cutterOnVaporize: { - title: "Laser: On - Vaporize", description: "Persent of power to turn on the laser/plasma cutter in vaporize mode", group: 6, - type: "number", default_mm: 100, default_in: 100 - }, - cutterOnThrough: { - title: "Laser: On - Through", description: "Persent of power to turn on the laser/plasma cutter in through mode", group: 6, - type: "number", default_mm: 80, default_in: 80 - }, - cutterOnEtch: { - title: "Laser: On - Etch", description: "Persent of power to on the laser/plasma cutter in etch mode", group: 6, - type: "number", default_mm: 40, default_in: 40 - }, - cutterMarlinMode: { - title: "Laser: Marlin/Reprap mode", description: "Marlin/Reprar mode of the laser/plasma cutter", group: 6, - type: "integer", default_mm: 106, default_in: 106, - values: [ - { title: "M106 S{PWM}/M107", id: 106 }, - { title: "M3 O{PWM}/M5", id: 3 }, - { title: "M42 P{pin} S{PWM}", id: 42 }, - ] - }, - cutterMarlinPin: { - title: "Laser: Marlin M42 pin", description: "Marlin custom pin number for the laser/plasma cutter", group: 6, - type: "integer", default_mm: 4, default_in: 4 - }, - - /* - cutterGrblMode: { - title: "Laser: GRBL mode", description: "GRBL mode of the laser/plasma cutter", group: 5, - type: "integer", default_mm: 4, default_in: 4, - values: [ - { title: "M4 S{PWM}/M5 dynamic power", id: 4 }, - { title: "M3 S{PWM}/M5 static power", id: 3 }, - ] - }, -*/ - - gcodeStartFile: { - title: "Extern: Start File", description: "File with custom Gcode for header/start (in nc folder)", group: 7, - type: "file", default_mm: "", default_in: "" - }, - gcodeStopFile: { - title: "Extern: Stop File", description: "File with custom Gcode for footer/end (in nc folder)", group: 7, - type: "file", default_mm: "", default_in: "" - }, - gcodeToolFile: { - title: "Extern: Tool File", description: "File with custom Gcode for tool change (in nc folder)", group: 7, - type: "file", default_mm: "", default_in: "" - }, - gcodeProbeFile: { - title: "Extern: Probe File", description: "File with custom Gcode for tool probe (in nc folder)", group: 7, - type: "file", default_mm: "", default_in: "" - }, - - coolantA_Mode: { - title: "Coolant: A Mode", description: "Enable issuing g-codes for control Coolant channel A", group: 8, type: "integer", - default_mm: 0, default_in: 0, - values: [ - { title: "off", id: 0 }, - { title: "flood", id: 1 }, - { title: "mist", id: 2 }, - { title: "throughTool", id: 3 }, - { title: "air", id: 4 }, - { title: "airThroughTool", id: 5 }, - { title: "suction", id: 6 }, - { title: "floodMist", id: 7 }, - { title: "floodThroughTool", id: 8 } - ] - }, - coolantAMarlinOn: { title: "Coolant: A On command", description: "GCode command to turn on Coolant channel A", group: 8, type: "string", default_mm: "M42 P11 S255" }, - coolantAMarlinOff: { - title: "Coolant: A Off command", description: "Gcode command to turn off Coolant A", group: 8, type: "string", - default_mm: "M42 P11 S0", default_in: "M42 P11 S0" - }, - - coolantB_Mode: { - title: "Coolant: B Mode", description: "Enable issuing g-codes for control Coolant channel B", group: 8, type: "integer", - default_mm: 0, default_in: 0, - values: [ - { title: "off", id: 0 }, - { title: "flood", id: 1 }, - { title: "mist", id: 2 }, - { title: "throughTool", id: 3 }, - { title: "air", id: 4 }, - { title: "airThroughTool", id: 5 }, - { title: "suction", id: 6 }, - { title: "floodMist", id: 7 }, - { title: "floodThroughTool", id: 8 } - ] - }, - coolantBMarlinOn: { - title: "Coolant: B On command", description: "GCode command to turn on Coolant channel B", group: 8, type: "string", - default_mm: "M42 P6 S255", default_in: "M42 P6 S255" - }, - coolantBMarlinOff: { - title: "Coolant: B Off command", description: "Gcode command to turn off Coolant channel B", group: 8, type: "string", - default_mm: "M42 P6 S0", default_in: "M42 P6 S0" - }, - - commentWriteTools: { - title: "Comment: Write Tools", description: "Write table of used tools in job header", group: 9, - type: "boolean", default_mm: true, default_in: true - }, - commentActivities: { - title: "Comment: Activities", description: "Write comments which somehow helps to understand current piece of g-code", group: 9, - type: "boolean", default_mm: true, default_in: true - }, - commentSections: { - title: "Comment: Sections", description: "Write header of every section", group: 9, - type: "boolean", default_mm: true, default_in: true - }, - commentCommands: { - title: "Comment: Trace Commands", description: "Write stringified commands called by CAM", group: 9, - type: "boolean", default_mm: true, default_in: true - }, - commentMovements: { - title: "Comment: Trace Movements", description: "Write stringified movements called by CAM", group: 9, - type: "boolean", default_mm: true, default_in: true - }, -}; - -/* - -https://github.com/guffy1234/mpcnc_posts_processor - -MPCNC posts processor for milling and laser/plasma cutting. - -*/ - -var sequenceNumber; - -// Formats -var gFormat = createFormat({ prefix: "G", decimals: 1 }); -var mFormat = createFormat({ prefix: "M", decimals: 0 }); - -var xyzFormat = createFormat({ decimals: (unit == MM ? 3 : 4) }); -var xFormat = createFormat({ prefix: "X", decimals: (unit == MM ? 3 : 4) }); -var yFormat = createFormat({ prefix: "Y", decimals: (unit == MM ? 3 : 4) }); -var zFormat = createFormat({ prefix: "Z", decimals: (unit == MM ? 3 : 4) }); -var iFormat = createFormat({ prefix: "I", decimals: (unit == MM ? 3 : 4) }); -var jFormat = createFormat({ prefix: "J", decimals: (unit == MM ? 3 : 4) }); -var kFormat = createFormat({ prefix: "K", decimals: (unit == MM ? 3 : 4) }); - -var speedFormat = createFormat({ decimals: 0 }); -var sFormat = createFormat({ prefix: "S", decimals: 0 }); - -var pFormat = createFormat({ prefix: "P", decimals: 0 }); -var oFormat = createFormat({ prefix: "O", decimals: 0 }); - -var feedFormat = createFormat({ decimals: (unit == MM ? 0 : 2) }); -var fFormat = createFormat({ prefix: "F", decimals: (unit == MM ? 0 : 2) }); - -var toolFormat = createFormat({ decimals: 0 }); -var tFormat = createFormat({ prefix: "T", decimals: 0 }); - -var taperFormat = createFormat({ decimals: 1, scale: DEG }); -var secFormat = createFormat({ decimals: 3, forceDecimal: true }); // seconds - range 0.001-1000 - -// Linear outputs -var xOutput = createVariable({}, xFormat); -var yOutput = createVariable({}, yFormat); -var zOutput = createVariable({}, zFormat); -var fOutput = createVariable({ force: false }, fFormat); -var sOutput = createVariable({ force: true }, sFormat); - -// Circular outputs -var iOutput = createReferenceVariable({}, iFormat); -var jOutput = createReferenceVariable({}, jFormat); -var kOutput = createReferenceVariable({}, kFormat); - -// Modals -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 - -// 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; - -// Writes the specified block. -function writeBlock() { - if (properties.jobSequenceNumbers) { - writeWords2("N" + sequenceNumber, arguments); - sequenceNumber += properties.jobSequenceNumberIncrement; - } else { - writeWords(arguments); - } -} - -function FirmwareBase() { - this.machineMode = undefined; //TYPE_MILLING, TYPE_JET -} - -FirmwareBase.prototype.section = function () { - this.machineMode = currentSection.type; -} - -var currentFirmware; - -// Called in every new gcode file -function onOpen() { - currentFirmware.init(); - - sequenceNumber = properties.jobSequenceNumberStart; - if (!properties.jobSeparateWordsWithSpace) { - setWordSeparator(""); - } -} - -// Called at end of gcode file -function onClose() { - writeActivityComment(" *** STOP begin ***"); - currentFirmware.flushMotions(); - if (properties.gcodeStopFile == "") { - onCommand(COMMAND_COOLANT_OFF); - if (properties.jobGoOriginOnFinish) { - rapidMovementsXY(0, 0); - } - onCommand(COMMAND_STOP_SPINDLE); - currentFirmware.end(); - writeActivityComment(" *** STOP end ***"); - } else { - loadFile(properties.gcodeStopFile); - } - currentFirmware.close(); -} - -var cutterOnCurrentPower; -var forceSectionToStartWithRapid = false; - -function onSection() { - // Every section needs to start with a Rapid to get to the initial location. - // In the hobby version Rapids have been elliminated and the first command is - // a onLinear not a onRapid command. This results in not current position being - // that same as the cut to position which means wecan't determine the direction - // of the move. Without a direction vector we can't scale the feedrate or convert - // onLinear moves back into onRapids. By ensuring the first onLinear is treated as - // a onRapid we have a currentPosition that is correct. - - forceSectionToStartWithRapid = true; - - // Write Start gcode of the documment (after the "onParameters" with the global info) - if (isFirstSection()) { - writeFirstSection(); - } - - writeActivityComment(" *** SECTION begin ***"); - - // Tool change - if (properties.toolChangeEnabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) { - if (properties.gcodeToolFile == "") { - // Builtin tool change gcode - writeActivityComment(" --- CHANGE TOOL begin ---"); - currentFirmware.toolChange(); - writeActivityComment(" --- CHANGE TOOL end ---"); - } else { - // Custom tool change gcode - loadFile(properties.gcodeToolFile); - } - } - - if (properties.commentSections) { - // Machining type - if (currentSection.type == TYPE_MILLING) { - // Specific milling code - writeComment(" " + sectionComment + " - Milling - Tool: " + tool.number + " - " + tool.comment + " " + 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: - cutterOnCurrentPower = properties.cutterOnThrough; - break; - case JET_MODE_ETCHING: - cutterOnCurrentPower = properties.cutterOnEtch; - break; - case JET_MODE_VAPORIZE: - cutterOnCurrentPower = 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())); - } - - currentFirmware.section(); //adjust mode - - onCommand(COMMAND_START_SPINDLE); - onCommand(COMMAND_COOLANT_ON); - - // Display section name in LCD - currentFirmware.display_text(" " + sectionComment); -} - -function resetAll() { - xOutput.reset(); - yOutput.reset(); - zOutput.reset(); - fOutput.reset(); -} - -// Called in every section end -function onSectionEnd() { - resetAll(); - writeActivityComment(" *** SECTION end ***"); - writeln(""); -} - -function onComment(message) { - writeComment(message); -} - -var pendingRadiusCompensation = RADIUS_COMPENSATION_OFF; - -function onRadiusCompensation() { - pendingRadiusCompensation = radiusCompensation; -} - -// Rapid movements -function onRapid(x, y, z) { - forceSectionToStartWithRapid = false; - - rapidMovements(x, y, z); -} - -function safeToRapid(x, y, z) { - if (properties.mapE_RestoreRapids) { - let zSafe = (z >= properties.mapF_SafeZ); - - // Destination z must be in safe zone. - if (zSafe) { - let cur = getCurrentPosition(); - let zConstant = (z == cur.z); - let zUp = (z > cur.z); - let xyConstant = ((x == cur.x) && (y == cur.y)); - let curZSafe = (cur.z >= properties.mapF_SafeZ); - - // Restore Rapids only when the target Z is safe and - // Case 1: Z is not changing, but XY are - // Case 2: Z is increasing, but XY constant - - // Z is not changing and we know we are in the safe zone - if (zConstant) { - return true; - } - - // We include moves of Z up as long as xy are constant - else if (properties.mapG_AllowRapidZ && zUp && xyConstant) { - return true; - } - - // We include moves of Z down as long as xy are constant and z always remains safe - else if (properties.mapG_AllowRapidZ && (!zUp) && xyConstant && curZSafe) { - return true; - } - } - } - - return false; -} - -// Feed movements -function onLinear(x, y, z, feed) { - // If we are allowing Rapids to be recovered from Linear (cut) moves, which is - // only required when F360 Personal edition is used, then if this Linear (cut) - // move is the first operationin a Section (milling operation) then convert it - // to a Rapid. This is OK because Sections normally begin with a Rapid to move - // to the first cutting location but these Rapids were changed to Linears by - // the personal edition. If this Rapid is not recovered and feedrate scaling - // is enabled then the first move to the start of a section will be at the - // slowest cutting feedrate, generally Z's feedrate. - - if (properties.mapD_RestoreFirstRapids && (forceSectionToStartWithRapid == true)) { - writeComment(" First G1 --> G0"); - - forceSectionToStartWithRapid = false; - onRapid(x, y, z); - } - else if (safeToRapid(x, y, z)) { - writeComment(" Safe G1 --> G0"); - - onRapid(x, y, z); - } - else { - linearMovements(x, y, z, feed, true); - } -} - -function onRapid5D(_x, _y, _z, _a, _b, _c) { - forceSectionToStartWithRapid = false; - - error(localize("Multi-axis motion is not supported.")); -} - -function onLinear5D(_x, _y, _z, _a, _b, _c, feed) { - forceSectionToStartWithRapid = false; - - error(localize("Multi-axis motion is not supported.")); -} - -function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { - forceSectionToStartWithRapid = false; - - if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { - error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); - return; - } - currentFirmware.circular(clockwise, cx, cy, cz, x, y, z, feed) -} - -// Called on waterjet/plasma/laser cuts -var powerState = false; - -function onPower(power) { - if (power != powerState) { - if (power) { - writeActivityComment(" >>> LASER Power ON"); - currentFirmware.laserOn(cutterOnCurrentPower); - } else { - writeActivityComment(" >>> LASER Power OFF"); - currentFirmware.laserOff(); - } - powerState = power; - } -} - -// Called on Dwell Manual NC invocation -function onDwell(seconds) { - if (seconds > 99999.999) { - warning(localize("Dwelling time is out of range.")); - } - writeActivityComment(" >>> Dwell"); - currentFirmware.dwell(seconds); -} - -// 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; -} - -function onMovement(movement) { - if (properties.commentMovements) { - var jet = tool.isJetTool && tool.isJetTool(); - var id; - switch (movement) { - case MOVEMENT_RAPID: - id = "MOVEMENT_RAPID"; - break; - case MOVEMENT_LEAD_IN: - id = "MOVEMENT_LEAD_IN"; - break; - case MOVEMENT_CUTTING: - id = "MOVEMENT_CUTTING"; - break; - case MOVEMENT_LEAD_OUT: - id = "MOVEMENT_LEAD_OUT"; - break; - case MOVEMENT_LINK_TRANSITION: - id = jet ? "MOVEMENT_BRIDGING" : "MOVEMENT_LINK_TRANSITION"; - break; - case MOVEMENT_LINK_DIRECT: - id = "MOVEMENT_LINK_DIRECT"; - break; - case MOVEMENT_RAMP_HELIX: - id = jet ? "MOVEMENT_PIERCE_CIRCULAR" : "MOVEMENT_RAMP_HELIX"; - break; - case MOVEMENT_RAMP_PROFILE: - id = jet ? "MOVEMENT_PIERCE_PROFILE" : "MOVEMENT_RAMP_PROFILE"; - break; - case MOVEMENT_RAMP_ZIG_ZAG: - id = jet ? "MOVEMENT_PIERCE_LINEAR" : "MOVEMENT_RAMP_ZIG_ZAG"; - break; - case MOVEMENT_RAMP: - id = "MOVEMENT_RAMP"; - break; - case MOVEMENT_PLUNGE: - id = jet ? "MOVEMENT_PIERCE" : "MOVEMENT_PLUNGE"; - break; - case MOVEMENT_PREDRILL: - id = "MOVEMENT_PREDRILL"; - break; - case MOVEMENT_EXTENDED: - id = "MOVEMENT_EXTENDED"; - break; - case MOVEMENT_REDUCED: - id = "MOVEMENT_REDUCED"; - break; - case MOVEMENT_HIGH_FEED: - id = "MOVEMENT_HIGH_FEED"; - break; - case MOVEMENT_FINISH_CUTTING: - id = "MOVEMENT_FINISH_CUTTING"; - break; - } - if (id == undefined) { - id = String(movement); - } - writeComment(" " + id); - } -} - -var currentSpindleSpeed = 0; - -function setSpindeSpeed(_spindleSpeed, _clockwise) { - if (currentSpindleSpeed != _spindleSpeed) { - if (_spindleSpeed > 0) { - currentFirmware.spindleOn(_spindleSpeed, _clockwise); - } else { - currentFirmware.spindleOff(); - } - currentSpindleSpeed = _spindleSpeed; - } -} - -function onSpindleSpeed(spindleSpeed) { - setSpindeSpeed(spindleSpeed, tool.clockwise); -} - -function onCommand(command) { - if (properties.commentActivities) { - var stringId = getCommandStringId(command); - writeComment(" " + stringId); - } - switch (command) { - case COMMAND_START_SPINDLE: - onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); - return; - case COMMAND_SPINDLE_CLOCKWISE: - if (tool.jetTool) - return; - setSpindeSpeed(spindleSpeed, true); - return; - case COMMAND_SPINDLE_COUNTERCLOCKWISE: - if (tool.jetTool) - return; - setSpindeSpeed(spindleSpeed, false); - return; - case COMMAND_STOP_SPINDLE: - if (tool.jetTool) - return; - setSpindeSpeed(0, true); - return; - case COMMAND_COOLANT_ON: - setCoolant(tool.coolant); - return; - case COMMAND_COOLANT_OFF: - setCoolant(0); //COOLANT_DISABLED - return; - case COMMAND_LOCK_MULTI_AXIS: - return; - case COMMAND_UNLOCK_MULTI_AXIS: - return; - case COMMAND_BREAK_CONTROL: - return; - case COMMAND_TOOL_MEASURE: - if (tool.jetTool) - return; - currentFirmware.probeTool(); - return; - case COMMAND_STOP: - writeBlock(mFormat.format(0)); - return; - } -} - -function writeFirstSection() { - // dump tool information - var toolZRanges = {}; - var vectorX = new Vector(1, 0, 0); - var vectorY = new Vector(0, 1, 0); - var ranges = { - x: { min: undefined, max: undefined }, - y: { min: undefined, max: undefined }, - z: { min: undefined, max: undefined }, - }; - var handleMinMax = function (pair, range) { - var rmin = range.getMinimum(); - var rmax = range.getMaximum(); - if (pair.min == undefined || pair.min > rmin) { - pair.min = rmin; - } - if (pair.max == undefined || pair.max < rmin) { // was pair.min - changed by DG 1/4/2021 - pair.max = rmax; - } - } - - var numberOfSections = getNumberOfSections(); - for (var i = 0; i < numberOfSections; ++i) { - var section = getSection(i); - var tool = section.getTool(); - var zRange = section.getGlobalZRange(); - var xRange = section.getGlobalRange(vectorX); - var yRange = section.getGlobalRange(vectorY); - handleMinMax(ranges.x, xRange); - handleMinMax(ranges.y, yRange); - handleMinMax(ranges.z, zRange); - if (is3D() && properties.commentWriteTools) { - if (toolZRanges[tool.number]) { - toolZRanges[tool.number].expandToRange(zRange); - } else { - toolZRanges[tool.number] = zRange; - } - } - } - - writeComment(" "); - writeComment(" Ranges table:"); - writeComment(" X: Min=" + xyzFormat.format(ranges.x.min) + " Max=" + xyzFormat.format(ranges.x.max) + " Size=" + xyzFormat.format(ranges.x.max - ranges.x.min)); - writeComment(" Y: Min=" + xyzFormat.format(ranges.y.min) + " Max=" + xyzFormat.format(ranges.y.max) + " Size=" + xyzFormat.format(ranges.y.max - ranges.y.min)); - writeComment(" Z: Min=" + xyzFormat.format(ranges.z.min) + " Max=" + xyzFormat.format(ranges.z.max) + " Size=" + xyzFormat.format(ranges.z.max - ranges.z.min)); - - if (properties.commentWriteTools) { - writeComment(" "); - writeComment(" Tools table:"); - 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) + " CR=" + xyzFormat.format(tool.cornerRadius); - if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) { - comment += " TAPER=" + taperFormat.format(tool.taperAngle) + "deg"; - } - if (toolZRanges[tool.number]) { - comment += " - ZMIN=" + xyzFormat.format(toolZRanges[tool.number].getMinimum()); - } - comment += " - " + getToolTypeName(tool.type) + " " + tool.comment; - writeComment(comment); - } - } - } - writeln(""); - - writeActivityComment(" *** START begin ***"); - - if (properties.gcodeStartFile == "") { - currentFirmware.start(); - } else { - loadFile(properties.gcodeStartFile); - } - writeActivityComment(" *** START end ***"); - writeln(""); -} - -// Output a comment -function writeComment(text) { - currentFirmware.comment(text); -} - -// Rapid movements with G1 and differentiated travel speeds for XY -// Changes F360 current XY. -// No longer called for general Rapid only for probing, homing, etc. -function rapidMovementsXY(_x, _y) { - let x = xOutput.format(_x); - let y = yOutput.format(_y); - - if (x || y) { - if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { - error(localize("Radius compensation mode cannot be changed at rapid traversal.")); - } - else { - let f = fOutput.format(propertyMmToUnit(properties.fr0_TravelSpeedXY)); - writeBlock(gMotionModal.format(0), x, y, f); - } - } -} - -// Rapid movements with G1 and differentiated travel speeds for Z -// Changes F360 current Z -// No longer called for general Rapid only for probing, homing, etc. -function rapidMovementsZ(_z) { - let z = zOutput.format(_z); - - if (z) { - if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { - error(localize("Radius compensation mode cannot be changed at rapid traversal.")); - } - else { - let f = fOutput.format(propertyMmToUnit(properties.fr1_TravelSpeedZ)); - writeBlock(gMotionModal.format(0), z, f); - } - } -} - -// Rapid movements with G1 uses the max travel rate (xy or z) and then relies on feedrate scaling -function rapidMovements(_x, _y, _z) { - - rapidMovementsZ(_z); - rapidMovementsXY(_x, _y); -} - -// Calculate the feedX, feedY and feedZ components - -function limitFeedByXYZComponents(curPos, destPos, feed) { - if (!properties.frA_ScaleFeedrate) - return feed; - - var xyz = Vector.diff(destPos, curPos); // Translate the cut so curPos is at 0,0,0 - var dir = xyz.getNormalized(); // Normalize vector to get a direction vector - var xyzFeed = Vector.product(dir.abs, feed); // Determine the effective x,y,z speed on each axis - - // Get the max speed for each axis - let xyLimit = propertyMmToUnit(properties.frB_MaxCutSpeedXY); - let zLimit = propertyMmToUnit(properties.frC_MaxCutSpeedZ); - - // Normally F360 begins a Section (a milling operation) with a Rapid to move to the beginning of the cut. - // Rapids use the defined Travel speed and the Post Processor does not depend on the current location. - // This function must know the current location in order to calculate the actual vector traveled. Without - // the first Rapid the current location is the same as the desination location, which creates a 0 length - // vector. A zero length vector is unusable and so a instead the slowest of the xyLimit or zLimit is used. - // - // Note: if Map: G1 -> Rapid is enabled in the Properties then if the first operation in a Section is a - // cut (which it should always be) then it will be converted to a Rapid. This prevents ever getting a zero - // length vector. - if (xyz.length == 0) { - var lesserFeed = (xyLimit < zLimit) ? xyLimit : zLimit; - - return lesserFeed; - } - - // Force the speed of each axis to be within limits - if (xyzFeed.z > zLimit) { - xyzFeed.multiply(zLimit / xyzFeed.z); - } - - if (xyzFeed.x > xyLimit) { - xyzFeed.multiply(xyLimit / xyzFeed.x); - } - - if (xyzFeed.y > xyLimit) { - xyzFeed.multiply(xyLimit / xyzFeed.y); - } - - // Calculate the new feedrate based on the speed allowed on each axis: feedrate = sqrt(x^2 + y^2 + z^2) - // xyzFeed.length is the same as Math.sqrt((xyzFeed.x * xyzFeed.x) + (xyzFeed.y * xyzFeed.y) + (xyzFeed.z * xyzFeed.z)) - - // Limit the new feedrate by the maximum allowable cut speed - - let xyzLimit = propertyMmToUnit(properties.frD_MaxCutSpeedXYZ); - let newFeed = (xyzFeed.length > xyzLimit) ? xyzLimit : xyzFeed.length; - - if (Math.abs(newFeed - feed) > 0.01) { - return newFeed; - } - else { - return feed; - } -} - -// Linear movements -function linearMovements(_x, _y, _z, _feed) { - if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { - // ensure that we end at desired position when compensation is turned off - xOutput.reset(); - yOutput.reset(); - } - - // Force the feedrate to be scaled (if enabled). The feedrate is projected into the - // x, y, and z axis and each axis is tested to see if it exceeds its defined max. If - // it does then the speed in all 3 axis is scaled proportionately. The resulting feedrate - // is then capped at the maximum defined cutrate. - - let feed = limitFeedByXYZComponents(getCurrentPosition(), new Vector(_x, _y, _z), _feed); - - let x = xOutput.format(_x); - let y = yOutput.format(_y); - let z = zOutput.format(_z); - let f = fOutput.format(feed); - - if (x || y || z) { - if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { - error(localize("Radius compensation mode is not supported.")); - } else { - writeBlock(gMotionModal.format(1), x, y, z, f); - } - } else if (f) { - if (getNextRecord().isMotion()) { // try not to output feed without motion - fOutput.reset(); // force feed on next line - } else { - writeBlock(gMotionModal.format(1), f); - } - } -} - -// 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) { - writeActivityComment(" --- Start custom gcode " + folder + _file); - write(txt); - writeActivityComment(" --- End custom gcode " + folder + _file); - writeln(""); - } - } else { - writeComment(" Can't open file " + folder + _file); - error("Can't open file " + folder + _file); - } -} - -var currentCoolantMode = 0; - -// Manage coolant state -function setCoolant(coolant) { - if (currentCoolantMode == coolant) { - return; - } - if (properties.coolantA_Mode != 0) { - if (currentCoolantMode == properties.coolantA_Mode) { - writeActivityComment(" >>> Coolant A OFF"); - currentFirmware.coolantA(true); - } else if (coolant == properties.coolantA_Mode) { - writeActivityComment(" >>> Coolant A ON"); - currentFirmware.coolantA(false); - } - } - if (properties.coolantB_Mode != 0) { - if (currentCoolantMode == properties.coolantB_Mode) { - writeActivityComment(" >>> Coolant B OFF"); - currentFirmware.coolantB(true); - } else if (coolant == properties.coolantB_Mode) { - writeActivityComment(" >>> Coolant B ON"); - currentFirmware.coolantB(false); - } - } - currentCoolantMode = coolant; -} - -function propertyMmToUnit(_v) { - return (_v / (unit == IN ? 25.4 : 1)); -} - -function writeActivityComment(_comment) { - if (properties.commentActivities) { - writeComment(_comment); - } -} - -function mergeProperties(to, from) { - for (var attrname in from) { - to[attrname] = from[attrname]; - } -} - -function Firmware3dPrinterLike() { - FirmwareBase.apply(this, arguments); - this.spindleEnabled = false; -} - -Firmware3dPrinterLike.prototype = Object.create(FirmwareBase.prototype); -Firmware3dPrinterLike.prototype.constructor = Firmware3dPrinterLike; -Firmware3dPrinterLike.prototype.init = function () { - gMotionModal = createModal({ force: true }, gFormat); // modal group 1 // G0-G3, ... - - if (properties.jobMarlinEnforceFeedrate) { - fOutput = createVariable({ force: true }, fFormat); - } -} - -Firmware3dPrinterLike.prototype.start = function () { - writeComment(" Set Absolute Positioning"); - writeComment(" Units = " + (unit == IN ? "inch" : "mm")); - writeComment(" Disable stepper timeout"); - if (properties.jobSetOriginOnStart) { - writeComment(" Set current position = 0,0,0"); - } - - writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning - writeBlock(gUnitModal.format(unit == IN ? 20 : 21)); // Set the units - writeBlock(mFormat.format(84), sFormat.format(0)); // Disable steppers timeout - - if (properties.jobSetOriginOnStart) { - writeBlock(gFormat.format(92), xFormat.format(0), yFormat.format(0), zFormat.format(0)); // Set origin to initial position - } - - if (properties.probeOnStart && tool.number != 0 && !tool.jetTool) { - onCommand(COMMAND_TOOL_MEASURE); - } -} - -Firmware3dPrinterLike.prototype.end = function () { - this.display_text("Job end"); -} -Firmware3dPrinterLike.prototype.close = function () { -} -Firmware3dPrinterLike.prototype.comment = function (text) { - writeln(";" + String(text).replace(/[\(\)]/g, "")); -} -Firmware3dPrinterLike.prototype.flushMotions = function () { - writeBlock(mFormat.format(400)); -} -Firmware3dPrinterLike.prototype.spindleOn = function (_spindleSpeed, _clockwise) { - if (properties.jobManualSpindlePowerControl) { - // for manual any positive input speed assumed as enabled. so it's just a flag - if (!this.spindleEnabled) { - this.askUser("Turn ON " + speedFormat.format(_spindleSpeed) + "RPM", "Spindle", false); - } - } else { - writeActivityComment(" >>> Spindle Speed " + speedFormat.format(_spindleSpeed)); - writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed)); - } - this.spindleEnabled = true; -} -Firmware3dPrinterLike.prototype.spindleOff = function () { - if (properties.jobManualSpindlePowerControl) { - writeBlock(mFormat.format(300), sFormat.format(300), pFormat.format(3000)); - this.askUser("Turn OFF spindle", "Spindle", false); - } else { - writeBlock(mFormat.format(5)); - } - this.spindleEnabled = false; -} -Firmware3dPrinterLike.prototype.laserOn = function (power) { - var laser_pwm = power / 100 * 255; - switch (properties.cutterMarlinMode) { - case 106: - writeBlock(mFormat.format(106), sFormat.format(laser_pwm)); - break; - case 3: - writeBlock(mFormat.format(3), oFormat.format(laser_pwm)); - break; - case 42: - writeBlock(mFormat.format(42), pFormat.format(properties.cutterMarlinPin), sFormat.format(laser_pwm)); - break; - } -} -Firmware3dPrinterLike.prototype.laserOff = function () { - switch (properties.cutterMarlinMode) { - case 106: - writeBlock(mFormat.format(107)); - break; - case 3: - writeBlock(mFormat.format(5)); - break; - case 42: - writeBlock(mFormat.format(42), pFormat.format(properties.cutterMarlinPin), sFormat.format(0)); - break; - } -} -Firmware3dPrinterLike.prototype.coolantA = function (on) { - writeBlock(on ? properties.coolantAMarlinOn : properties.coolantAMarlinOff); -} -Firmware3dPrinterLike.prototype.coolantB = function (on) { - writeBlock(on ? properties.coolantBMarlinOn : roperties.coolantBMarlinOff); -} -Firmware3dPrinterLike.prototype.dwell = function (seconds) { - writeBlock(gFormat.format(4), "S" + secFormat.format(seconds)); -} -Firmware3dPrinterLike.prototype.display_text = function (txt) { - writeBlock(mFormat.format(117), (properties.jobSeparateWordsWithSpace ? "" : " ") + txt); -} -Firmware3dPrinterLike.prototype.circular = function (clockwise, cx, cy, cz, x, y, z, feed) { - if (!properties.jobUseArcs) { - linearize(tolerance); - return; - } - // Marlin supports arcs only on XY plane - var start = getCurrentPosition(); - - if (isFullCircle()) { - if (isHelical()) { - linearize(tolerance); - return; - } - switch (getCircularPlane()) { - case PLANE_XY: - writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); - break; - default: - linearize(tolerance); - } - } else { - switch (getCircularPlane()) { - case PLANE_XY: - writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); - break; - default: - linearize(tolerance); - } - } -} - -Firmware3dPrinterLike.prototype.askUser = function (text, title, allowJog) { - writeBlock(mFormat.format(0), (properties.jobSeparateWordsWithSpace ? "" : " ") + text); -} - -Firmware3dPrinterLike.prototype.toolChange = function () { - this.flushMotions(); - // Go to tool change position - onRapid(propertyMmToUnit(properties.toolChangeX), propertyMmToUnit(properties.toolChangeY), propertyMmToUnit(properties.toolChangeZ)); - currentFirmware.flushMotions(); - // turn off spindle and coolant - onCommand(COMMAND_COOLANT_OFF); - onCommand(COMMAND_STOP_SPINDLE); - if (!properties.jobManualSpindlePowerControl) { - // Beep - writeBlock(mFormat.format(300), sFormat.format(400), pFormat.format(2000)); - } - - // Disable Z stepper - if (properties.toolChangeDisableZStepper) { - this.askUser("Z Stepper will disabled. Wait for STOP!!", "Tool change", false); - writeBlock(mFormat.format(17), 'Z'); // Disable steppers timeout - } - // Ask tool change and wait user to touch lcd button - this.askUser("Tool " + tool.number + " " + tool.comment, "Tool change", true); - - // Run Z probe gcode - if (properties.toolChangeZProbe && tool.number != 0) { - onCommand(COMMAND_TOOL_MEASURE); - } -} - -Firmware3dPrinterLike.prototype.probeTool = function () { - writeComment(" Ask User to Attach the Z Probe"); - writeComment(" Probe"); - writeComment(" Set Z to probe thickness: " + zFormat.format(propertyMmToUnit(properties.probeThickness))) - if (properties.toolChangeZ != "") { - writeComment(" Retract the tool to " + propertyMmToUnit(properties.toolChangeZ)); - } - writeComment(" Ask User to Remove the Z Probe"); - - this.askUser("Attach ZProbe", "Probe", false); - // refer http://marlinfw.org/docs/gcode/G038.html - if (properties.probeUseHomeZ) { - writeBlock(gFormat.format(28), 'Z'); - } else { - writeBlock(gMotionModal.format(38.3), fFormat.format(propertyMmToUnit(properties.probeG38Speed)), zFormat.format(propertyMmToUnit(properties.probeG38Target))); - } - - let z = zFormat.format(propertyMmToUnit(properties.probeThickness)); - writeBlock(gFormat.format(92), z); // Set origin to initial position - - resetAll(); - if (properties.toolChangeZ != "") { // move up tool to safe height again after probing - rapidMovementsZ(propertyMmToUnit(properties.toolChangeZ), false); - } - this.flushMotions(); - this.askUser("Detach ZProbe", "Probe", false); -} - -properties3dPrinter = { - jobMarlinEnforceFeedrate: false, // Add feedrate to each movement line - - cutterMarlinMode: 106, // Marlin mode laser/plasma cutter - cutterMarlinPin: 4, // Marlin laser/plasma cutter pin for M42 - - coolantAMarlinOn: "M42 P11 S255", // GCode command to turn on Coolant channel A - coolantAMarlinOff: "M42 P11 S0", // Gcode command to turn off Coolant channel A - coolantBMarlinOn: "M42 P6 S255", // GCode command to turn on Coolant channel B - coolantBMarlinOff: "M42 P6 S0", // Gcode command to turn off Coolant channel B -}; - -propertyDefinitions3dPrinter = { - jobMarlinEnforceFeedrate: { - title: "Job: Enforce Feedrate", description: "Add feedrate to each movement g-code", group: 1, - type: "boolean", default_mm: false, default_in: false - }, - cutterMarlinMode: { - title: "Laser: Marlin/Reprap mode", description: "Marlin/Reprar mode of the laser/plasma cutter", group: 5, - type: "integer", default_mm: 106, default_in: 106, - values: [ - { title: "M106 S{PWM}/M107", id: 106 }, - { title: "M3 O{PWM}/M5", id: 3 }, - { title: "M42 P{pin} S{PWM}", id: 42 }, - ] - }, - cutterMarlinPin: { - title: "Laser: Marlin M42 pin", description: "Marlin custom pin number for the laser/plasma cutter", group: 5, - type: "integer", default_mm: 4, default_in: 4 - }, - - coolantAMarlinOn: { title: "Coolant: A On command", description: "GCode command to turn on Coolant channel A", group: 7, type: "string", default_mm: "M42 P11 S255" }, - coolantAMarlinOff: { - title: "Coolant: A Off command", description: "Gcode command to turn off Coolant A", group: 7, type: "string", - default_mm: "M42 P11 S0", default_in: "M42 P11 S0" - }, - - coolantBMarlinOn: { - title: "Coolant: B On command", description: "GCode command to turn on Coolant channel B", group: 7, type: "string", - default_mm: "M42 P6 S255", default_in: "M42 P6 S255" - }, - coolantBMarlinOff: { - title: "Coolant: B Off command", description: "Gcode command to turn off Coolant channel B", group: 7, type: "string", - default_mm: "M42 P6 S0", default_in: "M42 P6 S0" - }, -}; diff --git a/DIYCNC_Grbl11.cps b/DIYCNC_Grbl11.cps deleted file mode 100644 index 4b8fcbc..0000000 --- a/DIYCNC_Grbl11.cps +++ /dev/null @@ -1,147 +0,0 @@ -/* - -https://github.com/guffy1234/mpcnc_posts_processor - -MPCNC posts processor for milling and laser/plasma cutting. - -*/ -include("DIYCNC_Common.js"); - -description = "DIYCNC Milling/Laser - Grbl 1.1"; - -// user-defined properties -mergeProperties(properties, { - cutterGrblMode: 4, // GRBL mode laser/plasma cutter - coolantAGrbl: 7, // GCode command to turn on Coolant channel A - coolantBGrbl: 8, // GCode command to turn on Coolant channel A -}); - -mergeProperties(propertyDefinitions, { - cutterGrblMode: { - title: "Laser: GRBL mode", description: "GRBL mode of the laser/plasma cutter", group: 4, - type: "integer", default_mm: 4, default_in: 4, - values: [ - { title: "M4 S{PWM}/M5 dynamic power", id: 4 }, - { title: "M3 S{PWM}/M5 static power", id: 3 }, - ] - }, - coolantAGrbl: { - title: "Coolant: A code", description: "GRBL g-codes for control Coolant channel A", group: 6, type: "integer", - default_mm: 7, default_in: 7, - values: [ - { title: "M7 flood", id: 7 }, - { title: "M8 mist", id: 8 }, - ] - }, - coolantBGrbl: { - title: "Coolant: B code", description: "GRBL g-codes for control Coolant channel B", group: 6, type: "integer", - default_mm: 8, default_in: 8, - values: [ - { title: "M7 flood", id: 7 }, - { title: "M8 mist", id: 8 }, - ] - }, -}); - -function FirmwareGrbl() { - FirmwareBase.apply(this, arguments); -} - -FirmwareGrbl.prototype = Object.create(FirmwareBase.prototype); -FirmwareGrbl.prototype.constructor = FirmwareGrbl; -FirmwareGrbl.prototype.init = function () { - gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... - writeln("%"); -} -FirmwareGrbl.prototype.start = function () { - writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning - writeBlock(gFeedModeModal.format(94)); - writeBlock(gPlaneModal.format(17)); - writeBlock(gUnitModal.format(unit == IN ? 20 : 21)); -} -FirmwareGrbl.prototype.end = function () { - writeBlock(mFormat.format(30)); -} -FirmwareGrbl.prototype.close = function () { - writeln("%"); -} -FirmwareGrbl.prototype.comment = function (text) { - writeln("(" + String(text).replace(/[\(\)]/g, "") + ")"); -} -FirmwareGrbl.prototype.flushMotions = function () { -}, - FirmwareGrbl.prototype.spindleOn = function (_spindleSpeed, _clockwise) { - writeActivityComment(" >>> Spindle Speed " + speedFormat.format(_spindleSpeed)); - writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed)); - } -FirmwareGrbl.prototype.spindleOff = function () { - writeBlock(mFormat.format(5)); -} -FirmwareGrbl.prototype.laserOn = function (power) { - var laser_pwm = power / 100 * 255; - writeBlock(mFormat.format(properties.cutterGrblMode), sFormat.format(laser_pwm)); -} -FirmwareGrbl.prototype.laserOff = function () { - writeBlock(mFormat.format(5)); -} -FirmwareGrbl.prototype.coolantA = function (on) { - writeBlock(mFormat.format(on ? properties.coolantAGrbl : 9)); -} -FirmwareGrbl.prototype.coolantB = function (on) { - writeBlock(mFormat.format(on ? properties.coolantBGrbl : 9)); -} -FirmwareGrbl.prototype.dwell = function (seconds) { - seconds = clamp(0.001, seconds, 99999.999); - writeBlock(gFormat.format(4), "P" + secFormat.format(seconds)); -} -FirmwareGrbl.prototype.display_text = function (txt) { -} -FirmwareGrbl.prototype.circular = function (clockwise, cx, cy, cz, x, y, z, feed) { - if (!properties.jobUseArcs) { - linearize(tolerance); - return; - } - var start = getCurrentPosition(); - - if (isFullCircle()) { - if (isHelical()) { - linearize(tolerance); - return; - } - switch (getCircularPlane()) { - case PLANE_XY: - writeBlock(gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); - break; - case PLANE_ZX: - writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); - break; - case PLANE_YZ: - writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), yOutput.format(y), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); - break; - default: - linearize(tolerance); - } - } else { - switch (getCircularPlane()) { - case PLANE_XY: - writeBlock(gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); - break; - case PLANE_ZX: - writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); - break; - case PLANE_YZ: - writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); - break; - default: - linearize(tolerance); - } - } -} -FirmwareGrbl.prototype.toolChange = function () { - writeBlock(mFormat.format(6), tFormat.format(tool.number)); - writeBlock(gFormat.format(54)); -} -FirmwareGrbl.prototype.probeTool = function () { -} - -currentFirmware = new FirmwareGrbl(); \ No newline at end of file diff --git a/DIYCNC_Marlin20.cps b/DIYCNC_Marlin20.cps deleted file mode 100644 index d2500d2..0000000 --- a/DIYCNC_Marlin20.cps +++ /dev/null @@ -1,21 +0,0 @@ -/* - -https://github.com/guffy1234/mpcnc_posts_processor - -MPCNC posts processor for milling and laser/plasma cutting. - -*/ -include("DIYCNC_Common.js"); - -description = "DIYCNC Milling/Laser - Marlin 2.0"; - -mergeProperties(properties, properties3dPrinter); -mergeProperties(propertyDefinitions, propertyDefinitions3dPrinter); - -function FirmwareMarlin20() { - Firmware3dPrinterLike.apply(this, arguments); -} -FirmwareMarlin20.prototype = Object.create(Firmware3dPrinterLike.prototype); -FirmwareMarlin20.prototype.constructor = FirmwareMarlin20; - -currentFirmware = new FirmwareMarlin20(); diff --git a/DIYCNC_RepRapFW.cps b/DIYCNC_RepRapFW.cps deleted file mode 100644 index 0bcdcca..0000000 --- a/DIYCNC_RepRapFW.cps +++ /dev/null @@ -1,65 +0,0 @@ -/* - -https://github.com/guffy1234/mpcnc_posts_processor - -MPCNC posts processor for milling and laser/plasma cutting. - -*/ -include("DIYCNC_Common.js"); - -description = "DIYCNC Milling/Laser - RepRapFirmware"; - -// user-defined properties -mergeProperties(properties, properties3dPrinter); -mergeProperties(propertyDefinitions, propertyDefinitions3dPrinter); - -mergeProperties(properties, { - jobDuetMillingMode: "M453 P2 I0 R30000 F200", // GCode command to setup Duet3d milling mode - jobDuetLaserMode: "M452 P2 I0 R255 F200", // GCode command to setup Duet3d laser mode -}); -mergeProperties(propertyDefinitions, { - jobDuetMillingMode: { - title: "Job: Duet Milling mode", description: "GCode command to setup Duet3d milling mode", group: 1, type: "string", - default_mm: "M453 P2 I0 R30000 F200", default_in: "M453 P2 I0 R30000 F200" - }, - jobDuetLaserMode: { - title: "Job: Duet Laser mode", description: "GCode command to setup Duet3d laser mode", group: 1, type: "string", - default_mm: "M452 P2 I0 R255 F200", default_in: "M452 P2 I0 R255 F200" - }, -}); - - -function FirmwareRepRap() { - Firmware3dPrinterLike.apply(this, arguments); -} -FirmwareRepRap.prototype = Object.create(Firmware3dPrinterLike.prototype); -FirmwareRepRap.prototype.constructor = FirmwareRepRap; -FirmwareRepRap.prototype.askUser = function (text, title, allowJog) { - var v1 = " P\"" + text + "\" R\"" + title + "\" S3"; - var v2 = allowJog ? " X1 Y1 Z1" : ""; - writeBlock(mFormat.format(291), (properties.jobSeparateWordsWithSpace ? "" : " ") + v1 + v2); -} -FirmwareRepRap.prototype.section = function () { - if (this.machineMode != currentSection.type) { - switch (currentSection.type) { - case TYPE_MILLING: - writeBlock(properties.jobDuetMillingMode); - break; - case TYPE_JET: - writeBlock(properties.jobDuetLaserMode); - break; - } - } - this.machineMode = currentSection.type; -} -FirmwareRepRap.prototype.laserOn = function (power) { - switch (properties.cutterMarlinMode) { - case 3: - var laser_pwm = power / 100 * 255; - writeBlock(mFormat.format(3), sFormat.format(laser_pwm)); - return; - } - Firmware3dPrinterLike.prototype.laserOn.apply(this, arguments); -} - -currentFirmware = new FirmwareRepRap(); \ No newline at end of file diff --git a/MPCNC.cps b/MPCNC.cps new file mode 100644 index 0000000..2c898fb --- /dev/null +++ b/MPCNC.cps @@ -0,0 +1,1812 @@ +/* + +https://github.com/guffy1234/mpcnc_posts_processor + +MPCNC posts processor for milling and laser/plasma cutting. + +*/ + +description = "MPCNC Milling/Laser - Marlin 2.0, Grbl 1.1, RepRap"; +vendor = "flyfisher604"; +vendorUrl = "https://github.com/flyfisher604/mpcnc_post_processor"; + +// Internal properties +certificationLevel = 2; +extension = "gcode"; +setCodePage("ascii"); +capabilities = CAPABILITY_MILLING | CAPABILITY_JET; + +machineMode = undefined; //TYPE_MILLING, TYPE_JET + +var eFirmware = { + MARLIN: 0, + GRBL: 1, + REPRAP: 2, + prop: { + 0: {name: "Marlin 2.x", value: 0}, + 1: {name: "Grbl 1.1", value: 1}, + 2: {name: "RepRap", value: 2} + } + }; + +var fw = eFirmware.MARLIN; + +var eComment = { + Off: 0, + Important: 1, + Info: 2, + Debug: 3, + prop: { + 0: {name: "Off", value: 0}, + 1: {name: "Important", value: 1}, + 2: {name: "Info", value: 2}, + 3: {name: "Debug", value: 3} + } +}; + +var eCoolant = { + Off: 0, + Flood: 1, + Mist: 2, + ThroughTool: 3, + Air: 4, + AirThroughTool: 5, + Suction: 6, + FloodMist: 7, + FloodThroughTool: 8, + prop: { + 0: {name: "Off", value: 0}, + 1: {name: "Flood", value: 1}, + 2: {name: "Mist", value: 2}, + 3: {name: "ThroughTool", value: 3}, + 4: {name: "Air", value: 4}, + 5: {name: "AirThroughTool", value: 5}, + 6: {name: "Suction", value: 6}, + 7: {name: "Flood and Mist", value: 7}, + 8: {name: "Flood and ThroughTool", value: 8}, + } +}; + + +// user-defined properties +properties = { + job0_SelectedFirmware : fw, // Firmware to use in special cases + job1_SetOriginOnStart: true, // Set current position as 0,0,0 on start (G92) + job2_ManualSpindlePowerControl: true, // Spindle motor is controlled by manual switch + job3_CommentLevel: eComment.Info, // The level of comments included + job4_UseArcs: true, // Produce G2/G3 for arcs + job5_SequenceNumbers: false, // show sequence numbers + job6_SequenceNumberStart: 10, // first sequence number + job7_SequenceNumberIncrement: 1, // increment for sequence numbers + job8_SeparateWordsWithSpace: true, // specifies that the words should be separated with a white space + job9_GoOriginOnFinish: true, // Go X0 Y0 current Z at end + + fr0_TravelSpeedXY: 2500, // High speed for travel movements X & Y (mm/min) + fr1_TravelSpeedZ: 300, // High speed for travel movements Z (mm/min) + fr2_EnforceFeedrate: true, // Add feedrate to each movement line + frA_ScaleFeedrate: false, // Will feedrated be scaled + frB_MaxCutSpeedXY: 900, // Max speed for cut movements X & Y (mm/min) + frC_MaxCutSpeedZ: 180, // Max speed for cut movements Z (mm/min) + frD_MaxCutSpeedXYZ: 1000, // Max feedrate after scaling + + mapD_RestoreFirstRapids: false, // Map first G01 --> G00 + mapE_RestoreRapids: false, // Map G01 --> G00 for SafeTravelsAboveZ + mapF_SafeZ: "Retract:15", // G01 mapped to G00 if Z is >= jobSafeZRapid + mapG_AllowRapidZ: false, // Allow G01 --> G00 for vertical retracts and Z descents above safe + + toolChange0_Enabled: false, // Enable tool change code (bultin tool change requires LCD display) + toolChange1_X: 0, // X position for builtin tool change + toolChange2_Y: 0, // Y position for builtin tool change + toolChange3_Z: 40, // Z position for builtin tool change + toolChange4_DisableZStepper: false, // disable Z stepper when change a tool + + probe1_OnStart: false, // Execute probe gcode to align tool + probe2_OnToolChange: false, // Z probe after tool change + probe3_Thickness: 0.8, // plate thickness + probe4_UseHomeZ: true, // use G28 or G38 for probing + probe5_G38Target: -10, // probing up to pos + probe6_G38Speed: 30, // probing with speed + + 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) + + cutter1_OnVaporize: 100, // Percentage of power to turn on the laser/plasma cutter in vaporize mode + cutter2_OnThrough: 80, // Percentage of power to turn on the laser/plasma cutter in through mode + cutter3_OnEtch: 40, // Percentage of power to turn on the laser/plasma cutter in etch mode + cutter4_MarlinMode: 106, // Marlin mode laser/plasma cutter + cutter5_MarlinPin: 4, // Marlin laser/plasma cutter pin for M42 + cutter6_GrblMode: 4, // GRBL mode laser/plasma cutter + cutter7_Coolant: eCoolant.Off, // Use this coolant. F360 doesn't define a coolant for cutters + + cl0_coolantA_Mode: eCoolant.Off, // Enable issuing g-codes for control Coolant channel A + cl1_coolantB_Mode: eCoolant.Off, // Use issuing g-codes for control Coolant channel B + cl2_coolantAOn: "M42 P6 S255", // GCode command to turn on Coolant channel A + cl3_coolantAOff: "M42 P6 S0", // Gcode command to turn off Coolant channel A + cl4_coolantBOn: "M42 P11 S255", // GCode command to turn on Coolant channel B + cl5_coolantBOff: "M42 P11 S0", // Gcode command to turn off Coolant channel B + + DuetMillingMode: "M453 P2 I0 R30000 F200", // GCode command to setup Duet3d milling mode + DuetLaserMode: "M452 P2 I0 R255 F200", // GCode command to setup Duet3d laser mode + +}; + +propertyDefinitions = { + + job0_SelectedFirmware: { + title: "Job: CNC Firmware", description: "Dialect of GCode to create", group: 1, + type: "integer", default_mm: eFirmware.MARLIN, default_in: eFirmware.MARLIN, + values: [ + { title: eFirmware.prop[eFirmware.MARLIN].name, id: eFirmware.MARLIN }, + { title: eFirmware.prop[eFirmware.GRBL].name, id: eFirmware.GRBL }, + { title: eFirmware.prop[eFirmware.REPRAP].name, id: eFirmware.REPRAP }, + ] + }, + + job1_SetOriginOnStart: { + title: "Job: Zero Starting Location (G92)", description: "On start set the current location as 0,0,0 (G92)", group: 1, + type: "boolean", default_mm: true, default_in: true + }, + job2_ManualSpindlePowerControl: { + title: "Job: Manual Spindle On/Off", description: "Enable to manually turn spindle motor on/off", group: 1, + type: "boolean", default_mm: true, default_in: true + }, + job3_CommentLevel: { + title: "Job: Comment Level", description: "Controls the comments include", group: 1, + type: "integer", default_mm: eComment.Info, default_in: eComment.Info, + values: [ + { title: eComment.prop[eComment.Off].name, id: eComment.Off }, + { title: eComment.prop[eComment.Important].name, id: eComment.Important }, + { title: eComment.prop[eComment.Info].name, id: eComment.Info }, + { title: eComment.prop[eComment.Debug].name, id: eComment.Debug }, + ] + }, + job4_UseArcs: { + title: "Job: Use Arcs", description: "Use G2/G3 g-codes fo circular movements", group: 1, + type: "boolean", default_mm: true, default_in: true + }, + job5_SequenceNumbers: { + title: "Job: Enable Line #s", description: "Show sequence numbers", group: 1, + type: "boolean", default_mm: false, default_in: false + }, + job6_SequenceNumberStart: { + title: "Job: First Line #", description: "First sequence number", group: 1, + type: "integer", default_mm: 10, default_in: 10 + }, + job7_SequenceNumberIncrement: { + title: "Job: Line # Increment", description: "Sequence number increment", group: 1, + type: "integer", default_mm: 1, default_in: 1 + }, + job8_SeparateWordsWithSpace: { + title: "Job: Include Whitespace", description: "Includes whitespace seperation between text", group: 1, + type: "boolean", default_mm: true, default_in: true + }, + job9_GoOriginOnFinish: { + title: "Job: At end go to 0,0", description: "Go to X0 Y0 at gcode end, Z remains unchanged", group: 1, + type: "boolean", default_mm: true, default_in: true + }, + + fr0_TravelSpeedXY: { + title: "Feed: Travel speed X/Y", description: "High speed for Rapid movements X & Y (mm/min; in/min)", group: 2, + type: "spatial", default_mm: 2500, default_in: 100 + }, + fr1_TravelSpeedZ: { + title: "Feed: Travel Speed Z", description: "High speed for Rapid movements z (mm/min; in/min)", group: 2, + type: "spatial", default_mm: 300, default_in: 12 + }, + fr2_EnforceFeedrate: { + title: "Feed: Enforce Feedrate", description: "Add feedrate to each movement g-code", group: 2, + type: "boolean", default_mm: true, default_in: true + }, + frA_ScaleFeedrate: { + title: "Feed: Scale Feedrate", description: "Scale feedrate based on X, Y, Z axis maximums", group: 2, + type: "boolean", default_mm: false, default_in: false + }, + frB_MaxCutSpeedXY: { + title: "Feed: Max XY Cut Speed", description: "Maximum X or Y axis cut speed (mm/min; in/min)", group: 2, + type: "spatial", default_mm: 900, default_in: 35.43 + }, + frC_MaxCutSpeedZ: { + title: "Feed: Max Z Cut Speed", description: "Maximum Z axis cut speed (mm/min; in/min)", group: 2, + type: "spatial", default_mm: 180, default_in: 7.08 + }, + frD_MaxCutSpeedXYZ: { + title: "Feed: Max Toolpath Speed", description: "Maximum scaled feedrate for toolpath (mm/min; in/min)", group: 2, + type: "spatial", default_mm: 1000, default_in: 39.37 + }, + + mapD_RestoreFirstRapids: { + title: "Map: First G1 -> G0 Rapid", description: "Ensure move to start of a cut is with a G0 Rapid", group: 3, + type: "boolean", default_mm: false, default_in: false + }, + mapE_RestoreRapids: { + title: "Map: G1s -> G0 Rapids", description: "Enable to convert G1s to G0 Rapids when safe", group: 3, + type: "boolean", default_mm: false, default_in: false + }, + mapF_SafeZ: { + title: "Map: Safe Z to Rapid", description: "Must be above or equal to this value to map G1s --> G0s; constant or keyword (see docs)", group: 3, + type: "string", default_mm: "Retract:15", default_in: "Retract:15" + }, + mapG_AllowRapidZ: { + title: "Map: Allow Rapid Z", description: "Enable to include vertical retracts and safe descents", group: 3, + type: "boolean", default_mm: false, default_in: false + }, + + toolChange0_Enabled: { + title: "Tool Change: Enable", description: "Include tool change code when tool changes (bultin tool change requires LCD display)", group: 4, + type: "boolean", default_mm: false, default_in: false + }, + toolChange1_X: { + title: "Tool Change: X", description: "X location for tool change", group: 4, + type: "spatial", default_mm: 0, default_in: 0 + }, + toolChange2_Y: { + title: "Tool Change: Y", description: "Y location for tool change", group: 4, + type: "spatial", default_mm: 0, default_in: 0 + }, + toolChange3_Z: { + title: "Tool Change: Z ", description: "Z location for tool change", group: 4, + type: "spatial", default_mm: 40, default_in: 1.6 + }, + toolChange4_DisableZStepper: { + title: "Tool Change: Disable Z stepper", description: "Disable Z stepper after reaching tool change location", group: 4, + type: "boolean", default_mm: false, default_in: false + }, + + probe1_OnStart: { + title: "Probe: On job start", description: "Execute probe gcode on job start", group: 5, + type: "boolean", default_mm: false, default_in: false + }, + probe2_OnToolChange: { + title: "Probe: After Tool Change", description: "After tool change, probe Z at the current location", group: 5, + type: "boolean", default_mm: false, default_in: false + }, + probe3_Thickness: { + title: "Probe: Plate thickness", description: "Plate thickness", group: 5, + type: "spatial", default_mm: 0.8, default_in: 0.032 + }, + probe4_UseHomeZ: { + title: "Probe: Use Home Z (G28)", description: "Probe with G28 (Yes) or G38 (No)", group: 5, + type: "boolean", default_mm: true, default_in: true + }, + probe5_G38Target: { + title: "Probe: G38 target", description: "G38 Probing's furthest Z position", group: 5, + type: "spatial", default_mm: -10, default_in: -0.5 + }, + probe6_G38Speed: { + title: "Probe: G38 speed", description: "G38 Probing's speed (mm/min; in/min)", group: 5, + type: "spatial", default_mm: 30, default_in: 1.2 + }, + + cutter1_OnVaporize: { + title: "Laser: On - Vaporize", description: "Persent of power to turn on the laser/plasma cutter in vaporize mode", group: 6, + type: "number", default_mm: 100, default_in: 100 + }, + cutter2_OnThrough: { + title: "Laser: On - Through", description: "Persent of power to turn on the laser/plasma cutter in through mode", group: 6, + type: "number", default_mm: 80, default_in: 80 + }, + cutter3_OnEtch: { + title: "Laser: On - Etch", description: "Persent of power to on the laser/plasma cutter in etch mode", group: 6, + type: "number", default_mm: 40, default_in: 40 + }, + cutter4_MarlinMode: { + title: "Laser: Marlin/Reprap Mode", description: "Marlin/Reprap mode of the laser/plasma cutter", group: 6, + type: "integer", default_mm: 106, default_in: 106, + values: [ + { title: "Fan - M106 S{PWM}/M107", id: 106 }, + { title: "Spindle - M3 O{PWM}/M5", id: 3 }, + { title: "Pin - M42 P{pin} S{PWM}", id: 42 }, + ] + }, + cutter5_MarlinPin: { + title: "Laser: Marlin M42 Pin", description: "Marlin custom pin number for the laser/plasma cutter", group: 6, + type: "integer", default_mm: 4, default_in: 4 + }, + cutter6_GrblMode: { + title: "Laser: GRBL Mode", description: "GRBL mode of the laser/plasma cutter", group: 6, + type: "integer", default_mm: 4, default_in: 4, + values: [ + { title: "M4 S{PWM}/M5 dynamic power", id: 4 }, + { title: "M3 S{PWM}/M5 static power", id: 3 }, + ] + }, + cutter7_Coolant: { + title: "Laser: Coolant", description: "Force a coolant to be used", group: 6, + type: "integer", default_mm: eCoolant.Off, default_in: eCoolant.Off, + values: [ + { title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off }, + { title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood }, + { title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist }, + { title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool }, + { title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air }, + { title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool }, + { title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction }, + { title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist }, + { title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool } + ] + }, + + gcodeStartFile: { + title: "Extern: Start File", description: "File with custom Gcode for header/start (in nc folder)", group: 7, + type: "file", default_mm: "", default_in: "" + }, + gcodeStopFile: { + title: "Extern: Stop File", description: "File with custom Gcode for footer/end (in nc folder)", group: 7, + type: "file", default_mm: "", default_in: "" + }, + gcodeToolFile: { + title: "Extern: Tool File", description: "File with custom Gcode for tool change (in nc folder)", group: 7, + type: "file", default_mm: "", default_in: "" + }, + gcodeProbeFile: { + title: "Extern: Probe File", description: "File with custom Gcode for tool probe (in nc folder)", group: 7, + type: "file", default_mm: "", default_in: "" + }, + + // Coolant + cl0_coolantA_Mode: { + title: "Coolant: A Mode", description: "Enable channel A when tool is set this coolant", group: 8, + type: "integer", default_mm: 0, default_in: 0, + values: [ + { title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off }, + { title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood }, + { title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist }, + { title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool }, + { title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air }, + { title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool }, + { title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction }, + { title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist }, + { title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool } + ] + }, + cl1_coolantB_Mode: { + title: "Coolant: B Mode", description: "Enable channel B when tool is set this coolant", group: 8, + type: "integer", default_mm: 0, default_in: 0, + values: [ + { title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off }, + { title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood }, + { title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist }, + { title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool }, + { title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air }, + { title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool }, + { title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction }, + { title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist }, + { title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool } + ] + }, + cl2_coolantAOn: { + title: "Coolant: A Enable", description: "GCode to turn On coolant channel A", group: 8, + type: "enum", default_mm: "M42 P6 S255", default_in: "M42 P6 S255", + values: [ + { title: "Mrln: M42 P6 S255", id: "M42 P6 S255" }, + { title: "Mrln: M42 P11 S255", id: "M42 P11 S255" }, + { title: "Grbl: M7 (mist)", id: "M7" }, + { title: "Grbl: M8 (flood)", id: "M8" } + ] + }, + cl3_coolantAOff: { + title: "Coolant: A Disable", description: "Gcode to turn Off coolant A", group: 8, + type: "enum", default_mm: "M42 P6 S0", default_in: "M42 P6 S0", + values: [ + { title: "Mrln: M42 P6 S0", id: "M42 P6 S0" }, + { title: "Mrln: M42 P11 S0", id: "M42 P11 S0" }, + { title: "Grbl: M9 (off)", id: "M9" } + ] + }, + cl4_coolantBOn: { + title: "Coolant: B Enable", description: "GCode to turn On coolant channel B", group: 8, + type: "enum", default_mm: "M42 P11 S255", default_in: "M42 P11 S255", + values: [ + { title: "Mrln: M42 P11 S255", id: "M42 P11 S255" }, + { title: "Mrln: M42 P6 S255", id: "M42 P6 S255" }, + { title: "Grbl: M7 (mist)", id: "M7" }, + { title: "Grbl: M8 (flood)", id: "M8" } + ] + }, + cl5_coolantBOff: { + title: "Coolant: B Disable", description: "Gcode to turn Off coolant B", group: 8, + type: "enum", default_mm: "M42 P11 S0", default_in: "M42 P11 S0", + values: [ + { title: "Mrln: M42 P11 S0", id: "M42 P11 S0" }, + { title: "Mrln: M42 P6 S0", id: "M42 P6 S0" }, + { title: "Grbl: M9 (off)", id: "M9" } + ] + }, + + DuetMillingMode: { + title: "Duet: Milling mode", description: "GCode command to setup Duet3d milling mode", group: 9, + type: "string", default_mm: "M453 P2 I0 R30000 F200", default_in: "M453 P2 I0 R30000 F200" + }, + DuetLaserMode: { + title: "Duet: Laser mode", description: "GCode command to setup Duet3d laser mode", group: 9, + type: "string", default_mm: "M452 P2 I0 R255 F200", default_in: "M452 P2 I0 R255 F200" + }, +}; + +var sequenceNumber; + +// Formats +var gFormat = createFormat({ prefix: "G", decimals: 1 }); +var mFormat = createFormat({ prefix: "M", decimals: 0 }); + +var xyzFormat = createFormat({ decimals: (unit == MM ? 3 : 4) }); +var xFormat = createFormat({ prefix: "X", decimals: (unit == MM ? 3 : 4) }); +var yFormat = createFormat({ prefix: "Y", decimals: (unit == MM ? 3 : 4) }); +var zFormat = createFormat({ prefix: "Z", decimals: (unit == MM ? 3 : 4) }); +var iFormat = createFormat({ prefix: "I", decimals: (unit == MM ? 3 : 4) }); +var jFormat = createFormat({ prefix: "J", decimals: (unit == MM ? 3 : 4) }); +var kFormat = createFormat({ prefix: "K", decimals: (unit == MM ? 3 : 4) }); + +var speedFormat = createFormat({ decimals: 0 }); +var sFormat = createFormat({ prefix: "S", decimals: 0 }); + +var pFormat = createFormat({ prefix: "P", decimals: 0 }); +var oFormat = createFormat({ prefix: "O", decimals: 0 }); + +var feedFormat = createFormat({ decimals: (unit == MM ? 0 : 2) }); +var fFormat = createFormat({ prefix: "F", decimals: (unit == MM ? 0 : 2) }); + +var toolFormat = createFormat({ decimals: 0 }); +var tFormat = createFormat({ prefix: "T", decimals: 0 }); + +var taperFormat = createFormat({ decimals: 1, scale: DEG }); +var secFormat = createFormat({ decimals: 3, forceDecimal: true }); // seconds - range 0.001-1000 + +// Linear outputs +var xOutput = createVariable({}, xFormat); +var yOutput = createVariable({}, yFormat); +var zOutput = createVariable({}, zFormat); +var fOutput = createVariable({ force: false }, fFormat); +var sOutput = createVariable({ force: true }, sFormat); + +// Circular outputs +var iOutput = createReferenceVariable({}, iFormat); +var jOutput = createReferenceVariable({}, jFormat); +var kOutput = createReferenceVariable({}, kFormat); + +// Modals +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 + +// 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; + +// Writes the specified block. +function writeBlock() { + if (properties.job5_SequenceNumbers) { + writeWords2("N" + sequenceNumber, arguments); + sequenceNumber += properties.job7_SequenceNumberIncrement; + } else { + writeWords(arguments); + } +} + +function flushMotions() { + if (fw == eFirmware.GRBL) { + } + + // Default + else { + writeBlock(mFormat.format(400)); + } +} + +//---------------- Safe Rapids ---------------- + +var eSafeZ = { + CONST: 0, + FEED: 1, + RETRACT: 2, + CLEARANCE: 3, + ERROR: 4, + prop: { + 0: {name: "Const", regex: /^\d+\.?\d*$/, numRegEx: /^(\d+\.?\d*)$/, value: 0}, + 1: {name: "Feed", regex: /^Feed:/i, numRegEx: /:(\d+\.?\d*)$/, value: 1}, + 2: {name: "Retract", regex: /^Retract:/i, numRegEx: /:(\d+\.?\d*)$/, alue: 2}, + 3: {name: "Clearance", regex: /^Clearance:/i, numRegEx: /:(\d+\.?\d*)$/, value: 3}, + 4: {name: "Error", regex: /^$/, numRegEx: /^$/, value: 4} + } +}; + +var safeZMode = eSafeZ.CONST; +var safeZHeightDefault = 15; +var safeZHeight; + +function parseSafeZProperty() { + var str = properties.mapF_SafeZ; + + // Look for either a number by itself or 'Feed:', 'Retract:' or 'Clearance:' + for (safeZMode = eSafeZ.CONST; safeZMode < eSafeZ.ERROR; safeZMode++) { + if (str.search(eSafeZ.prop[safeZMode].regex) == 0) { + break; + } + } + + // If it was not an error then get the number + if (safeZMode != eSafeZ.ERROR) { + safeZHeightDefault = str.match(eSafeZ.prop[safeZMode].numRegEx); + + if ((safeZHeightDefault == null) || (safeZHeightDefault.length !=2)) { + writeComment(eComment.Debug, " parseSafeZProperty: " + safeZHeightDefault); + writeComment(eComment.Debug, " parseSafeZProperty.length: " + (safeZHeightDefault != null? safeZHeightDefault.length : "na")); + writeComment(eComment.Debug, " parseSafeZProperty: Couldn't find number"); + safeZMode = eSafeZ.ERROR; + safeZHeightDefault = 15; + } + else { + safeZHeightDefault = safeZHeightDefault[1]; + } + } + + writeComment(eComment.Debug, " parseSafeZProperty: safeZMode = '" + eSafeZ.prop[safeZMode].name + "'"); + writeComment(eComment.Debug, " parseSafeZProperty: safeZHeightDefault = " + safeZHeightDefault); +} + +function safeZforSection(_section) +{ + if (properties.mapE_RestoreRapids) { + switch (safeZMode) { + case eSafeZ.CONST: + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ using const: " + safeZHeight); + break; + + case eSafeZ.FEED: + if (hasParameter("operation:feedHeight_value") && hasParameter("operation:feedHeight_absolute")) { + let feed = _section.getParameter("operation:feedHeight_value"); + let abs = _section.getParameter("operation:feedHeight_absolute"); + + if (abs == 1) { + safeZHeight = feed; + writeComment(eComment.Info, " SafeZ feed level: " + safeZHeight); + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ feed level not abs: " + safeZHeight); + } + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ feed level not defined: " + safeZHeight); + } + break; + + case eSafeZ.RETRACT: + if (hasParameter("operation:retractHeight_value") && hasParameter("operation:retractHeight_absolute")) { + let retract = _section.getParameter("operation:retractHeight_value"); + let abs = _section.getParameter("operation:retractHeight_absolute"); + + if (abs == 1) { + safeZHeight = retract; + writeComment(eComment.Info, " SafeZ retract level: " + safeZHeight); + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ retract level not abs: " + safeZHeight); + } + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ: retract level not defined: " + safeZHeight); + } + break; + + case eSafeZ.CLEARANCE: + if (hasParameter("operation:clearanceHeight_value") && hasParameter("operation:clearanceHeight_absolute")) { + var clearance = _section.getParameter("operation:clearanceHeight_value"); + let abs = _section.getParameter("operation:clearanceHeight_absolute"); + + if (abs == 1) { + safeZHeight = clearance; + writeComment(eComment.Info, " SafeZ clearance level: " + safeZHeight); + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ clearance level not abs: " + safeZHeight); + } + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ clearance level not defined: " + safeZHeight); + } + break; + + case eSafeZ.ERROR: + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " >>> WARNING: " + propertyDefinitions.mapF_SafeZ.title + "format error: " + safeZHeight); + break; + } + } +} + + +Number.prototype.round = function(places) { + return +(Math.round(this + "e+" + places) + "e-" + places); +} + +// Returns true if the rules to convert G1s to G0s are satisfied +function isSafeToRapid(x, y, z) { + if (properties.mapE_RestoreRapids) { + + // Calculat a z to 3 decimal places for zSafe comparison, every where else use z to avoid mixing rounded with unrounded + var z_round = z.round(3); + writeComment(eComment.Debug, "isSafeToRapid z: " + z + " z_round: " + z_round); + + let zSafe = (z_round >= safeZHeight); + + writeComment(eComment.Debug, "isSafeToRapid zSafe: " + zSafe + " z_round: " + z_round + " safeZHeight: " + safeZHeight); + + // Destination z must be in safe zone. + if (zSafe) { + let cur = getCurrentPosition(); + let zConstant = (z == cur.z); + let zUp = (z > cur.z); + let xyConstant = ((x == cur.x) && (y == cur.y)); + let curZSafe = (cur.z >= safeZHeight); + writeComment(eComment.Debug, "isSafeToRapid curZSafe: " + curZSafe + " cur.z: " + cur.z); + + // Restore Rapids only when the target Z is safe and + // Case 1: Z is not changing, but XY are + // Case 2: Z is increasing, but XY constant + + // Z is not changing and we know we are in the safe zone + if (zConstant) { + return true; + } + + // We include moves of Z up as long as xy are constant + else if (properties.mapG_AllowRapidZ && zUp && xyConstant) { + return true; + } + + // We include moves of Z down as long as xy are constant and z always remains safe + else if (properties.mapG_AllowRapidZ && (!zUp) && xyConstant && curZSafe) { + return true; + } + } + } + + return false; +} + +//---------------- Coolant ---------------- + +function CoolantA(on) { + writeBlock(on ? properties.cl2_coolantAOn : properties.cl3_coolantAOff); +} + +function CoolantB(on) { + writeBlock(on ? properties.cl4_coolantBOn : properties.cl5_coolantBOff); +} + +// Manage two channels of coolant by tracking which coolant is being using for +// a channel (0 = disabled). SetCoolant called with desired coolant to use or 0 to disable + +var curCoolant = eCoolant.Off; // The coolant requested by the tool +var coolantChannelA = eCoolant.Off; // The coolant running in ChannelA +var coolantChannelB = eCoolant.Off; // The coolant running in ChannelB + +function setCoolant(coolant) { + writeComment(eComment.Debug, " ---- Coolant: " + coolant + " cur: " + curCoolant + " A: " + coolantChannelA + " B: " + coolantChannelB); + + // If the coolant for this tool is the same as the current coolant then there is nothing to do + if (curCoolant == coolant) { + return; + } + + // We are changing coolant, so disable any active coolant channels + // before we switch to the other coolant + if (coolantChannelA != eCoolant.Off) { + writeComment((coolant == eCoolant.Off) ? eComment.Important: eComment.Info, " >>> Coolant Channel A: " + eCoolant.prop[eCoolant.Off].name); + coolantChannelA = eCoolant.Off; + CoolantA(false); + } + + if (coolantChannelB != eCoolant.Off) { + writeComment((coolant == eCoolant.Off) ? eComment.Important: eComment.Info, " >>> Coolant Channel B: " + eCoolant.prop[eCoolant.Off].name); + coolantChannelB = eCoolant.Off; + CoolantB(false); + } + + // At this point we know that all coolant is off so make that the current coolant + curCoolant = eCoolant.Off; + + // As long as we are not disabling coolant (coolant = 0), then check if either coolant channel + // matches the coolant requested. If neither do then issue an warning + + var warn = true; + + if (coolant != eCoolant.Off) { + if (properties.cl0_coolantA_Mode == coolant) { + writeComment(eComment.Important, " >>> Coolant Channel A: " + eCoolant.prop[coolant].name); + coolantChannelA = coolant; + curCoolant = coolant; + warn = false; + CoolantA(true); + } + + if (properties.cl1_coolantB_Mode == coolant) { + writeComment(eComment.Important, " >>> Coolant Channel B: " + eCoolant.prop[coolant].name); + coolantChannelB = coolant; + curCoolant = coolant; + warn = false; + CoolantB(true); + } + + if (warn) { + writeComment(eComment.Important, " >>> WARNING: No matching Coolant channel : " + ((coolant <= eCoolant.FloodThroughTool) ? eCoolant.prop[coolant].name : "unknown") + " requested"); + } + } +} + +//---------------- Cutters - Waterjet/Laser/Plasma ---------------- + +var cutterOnCurrentPower; + +function laserOn(power) { + // Firmware is Grbl + if (fw == eFirmware.GRBL) { + var laser_pwm = power * 10; + + writeBlock(mFormat.format(properties.cutter6_GrblMode), sFormat.format(laser_pwm)); + } + + // Default firmware + else { + var laser_pwm = power / 100 * 255; + + switch (properties.cutter4_MarlinMode) { + case 106: + writeBlock(mFormat.format(106), sFormat.format(laser_pwm)); + break; + case 3: + if (fw == eFirmware.REPRAP) { + writeBlock(mFormat.format(3), sFormat.format(laser_pwm)); + } else { + writeBlock(mFormat.format(3), oFormat.format(laser_pwm)); + } + break; + case 42: + writeBlock(mFormat.format(42), pFormat.format(properties.cutter5_MarlinPin), sFormat.format(laser_pwm)); + break; + } + } +} + +function laserOff() { + // Firmware is Grbl + if (fw == eFirmware.GRBL) { + writeBlock(mFormat.format(5)); + } + + // Default + else { + switch (properties.cutter4_MarlinMode) { + case 106: + writeBlock(mFormat.format(107)); + break; + case 3: + writeBlock(mFormat.format(5)); + break; + case 42: + writeBlock(mFormat.format(42), pFormat.format(properties.cutter5_MarlinPin), sFormat.format(0)); + break; + } + } +} + +//---------------- on Entry Points ---------------- + +// Called in every new gcode file +function onOpen() { + fw = properties.job0_SelectedFirmware; + + // Output anything special to start the GCode + if (fw == eFirmware.GRBL) { + writeln("%"); + } + + // Configure the GCode G commands + if (fw == eFirmware.GRBL) { + gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... + } + else { + gMotionModal = createModal({ force: true }, gFormat); // modal group 1 // G0-G3, ... + } + + // Configure how the feedrate is formatted + if (properties.fr2_EnforceFeedrate) { + fOutput = createVariable({ force: true }, fFormat); + } + + // Set the starting sequence number for line numbering + sequenceNumber = properties.job6_SequenceNumberStart; + + // Set the seperator used between text + if (!properties.job8_SeparateWordsWithSpace) { + setWordSeparator(""); + } + + // Determine the safeZHeight to do rapids + parseSafeZProperty(); +} + +// Called at end of gcode file +function onClose() { + writeComment(eComment.Important, " *** STOP begin ***"); + + flushMotions(); + + if (properties.gcodeStopFile == "") { + onCommand(COMMAND_COOLANT_OFF); + if (properties.job9_GoOriginOnFinish) { + rapidMovementsXY(0, 0); + } + onCommand(COMMAND_STOP_SPINDLE); + + end(true); + + writeComment(eComment.Important, " *** STOP end ***"); + } else { + loadFile(properties.gcodeStopFile); + } + + if (fw == eFirmware.GRBL) { + writeln("%"); + } +} + +var forceSectionToStartWithRapid = false; + +function onSection() { + // Every section needs to start with a Rapid to get to the initial location. + // In the hobby version Rapids have been elliminated and the first command is + // a onLinear not a onRapid command. This results in not current position being + // that same as the cut to position which means wecan't determine the direction + // of the move. Without a direction vector we can't scale the feedrate or convert + // onLinear moves back into onRapids. By ensuring the first onLinear is treated as + // a onRapid we have a currentPosition that is correct. + + forceSectionToStartWithRapid = true; + + // Write Start gcode of the documment (after the "onParameters" with the global info) + if (isFirstSection()) { + writeFirstSection(); + } + + writeComment(eComment.Important, " *** SECTION begin ***"); + + // Print min/max boundaries for each section + vectorX = new Vector(1, 0, 0); + vectorY = new Vector(0, 1, 0); + writeComment(eComment.Info, " X Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMinimum()) + " - X Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMaximum())); + writeComment(eComment.Info, " Y Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMinimum()) + " - Y Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMaximum())); + writeComment(eComment.Info, " Z Min: " + xyzFormat.format(currentSection.getGlobalZRange().getMinimum()) + " - Z Max: " + xyzFormat.format(currentSection.getGlobalZRange().getMaximum())); + + // Determine the Safe Z Height to map G1s to G0s + safeZforSection(currentSection); + + // Do a tool change if tool changes are enabled and its not the first section and this section uses + // a different tool then the previous section + if (properties.toolChange0_Enabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) { + if (properties.gcodeToolFile == "") { + // Post Processor does the tool change + + writeComment(eComment.Important, " --- Tool Change Start") + toolChange(); + writeComment(eComment.Important, " --- Tool Change End") + } else { + // Users custom tool change gcode is used + loadFile(properties.gcodeToolFile); + } + } + + // Machining type + if (currentSection.type == TYPE_MILLING) { + // Specific milling code + writeComment(eComment.Info, " " + sectionComment + " - Milling - Tool: " + tool.number + " - " + tool.comment + " " + getToolTypeName(tool.type)); + } + + else if (currentSection.type == TYPE_JET) { + var jetModeStr; + var warn = false; + + // Cutter mode used for different cutting power in PWM laser + switch (currentSection.jetMode) { + case JET_MODE_THROUGH: + cutterOnCurrentPower = properties.cutter2_OnThrough; + jetModeStr = "Through" + break; + case JET_MODE_ETCHING: + cutterOnCurrentPower = properties.cutter3_OnEtch; + jetModeStr = "Etching" + break; + case JET_MODE_VAPORIZE: + jetModeStr = "Vaporize" + cutterOnCurrentPower = properties.cutter1_OnVaporize; + break; + default: + jetModeStr = "*** Unknown ***" + warn = true; + } + + if (warn) { + writeComment(eComment.Info, " " + sectionComment + ", Laser/Plasma Cutting mode: " + getParameter("operation:cuttingMode") + ", jetMode: " + jetModeStr); + writeComment(eComment.Important, "Selected cutting mode " + currentSection.jetMode + " not mapped to power level"); + } + else { + writeComment(eComment.Info, " " + sectionComment + ", Laser/Plasma Cutting mode: " + getParameter("operation:cuttingMode") + ", jetMode: " + jetModeStr + ", power: " + cutterOnCurrentPower); + } + } + + // Adjust the mode + if (fw == eFirmware.REPRAP) { + if (machineMode != currentSection.type) { + switch (currentSection.type) { + case TYPE_MILLING: + writeBlock(properties.DuetMillingMode); + break; + case TYPE_JET: + writeBlock(properties.DuetLaserMode); + break; + } + } + } + + machineMode = currentSection.type; + + onCommand(COMMAND_START_SPINDLE); + onCommand(COMMAND_COOLANT_ON); + + // Display section name in LCD + display_text(" " + sectionComment); +} + +// Called in every section end +function onSectionEnd() { + resetAll(); + writeComment(eComment.Important, " *** SECTION end ***"); + writeComment(eComment.Important, ""); +} + +function onComment(message) { + writeComment(eComment.Important, message); +} + +var pendingRadiusCompensation = RADIUS_COMPENSATION_OFF; + +function onRadiusCompensation() { + pendingRadiusCompensation = radiusCompensation; +} + +// Rapid movements +function onRapid(x, y, z) { + forceSectionToStartWithRapid = false; + + rapidMovements(x, y, z); +} + +// Feed movements +function onLinear(x, y, z, feed) { + // If we are allowing Rapids to be recovered from Linear (cut) moves, which is + // only required when F360 Personal edition is used, then if this Linear (cut) + // move is the first operationin a Section (milling operation) then convert it + // to a Rapid. This is OK because Sections normally begin with a Rapid to move + // to the first cutting location but these Rapids were changed to Linears by + // the personal edition. If this Rapid is not recovered and feedrate scaling + // is enabled then the first move to the start of a section will be at the + // slowest cutting feedrate, generally Z's feedrate. + + if (properties.mapD_RestoreFirstRapids && (forceSectionToStartWithRapid == true)) { + writeComment(eComment.Important, " First G1 --> G0"); + + forceSectionToStartWithRapid = false; + onRapid(x, y, z); + } + else if (isSafeToRapid(x, y, z)) { + writeComment(eComment.Important, " Safe G1 --> G0"); + + onRapid(x, y, z); + } + else { + linearMovements(x, y, z, feed, true); + } +} + +function onRapid5D(_x, _y, _z, _a, _b, _c) { + forceSectionToStartWithRapid = false; + + error(localize("Multi-axis motion is not supported.")); +} + +function onLinear5D(_x, _y, _z, _a, _b, _c, feed) { + forceSectionToStartWithRapid = false; + + error(localize("Multi-axis motion is not supported.")); +} + +function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { + forceSectionToStartWithRapid = false; + + if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { + error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); + return; + } + circular(clockwise, cx, cy, cz, x, y, z, feed) +} + +// Called on waterjet/plasma/laser cuts +var powerState = false; + +function onPower(power) { + if (power != powerState) { + if (power) { + writeComment(eComment.Important, " >>> LASER Power ON"); + + laserOn(cutterOnCurrentPower); + } else { + writeComment(eComment.Important, " >>> LASER Power OFF"); + + laserOff(); + } + powerState = power; + } +} + +// Called on Dwell Manual NC invocation +function onDwell(seconds) { + writeComment(eComment.Important, " >>> Dwell"); + if (seconds > 99999.999) { + warning(localize("Dwelling time is out of range.")); + } + + seconds = clamp(0.001, seconds, 99999.999); + + // Firmware is Grbl + if (fw == eFirmware.GRBL) { + writeBlock(gFormat.format(4), "P" + secFormat.format(seconds)); + } + + // Default + else { + writeBlock(gFormat.format(4), "S" + secFormat.format(seconds)); + } +} + +// Called with every parameter in the documment/section +function onParameter(name, value) { + + // Write gcode initial info + // Product version + if (name == "generated-by") { + writeComment(eComment.Important, value); + writeComment(eComment.Important, " Posts processor: " + FileSystem.getFilename(getConfigurationPath())); + } + + // Date + else if (name == "generated-at") { + writeComment(eComment.Important, " Gcode generated: " + value + " GMT"); + } + + // Document + else if (name == "document-path") { + writeComment(eComment.Important, " Document: " + value); + } + + // Setup + else if (name == "job-description") { + writeComment(eComment.Important, " Setup: " + value); + } + + // Get section comment + else if (name == "operation-comment") { + sectionComment = value; + } + + else { + writeComment(eComment.Debug, " param: " + name + " = " + value); + } +} + +function onMovement(movement) { + var jet = tool.isJetTool && tool.isJetTool(); + var id; + + switch (movement) { + case MOVEMENT_RAPID: + id = "MOVEMENT_RAPID"; + break; + case MOVEMENT_LEAD_IN: + id = "MOVEMENT_LEAD_IN"; + break; + case MOVEMENT_CUTTING: + id = "MOVEMENT_CUTTING"; + break; + case MOVEMENT_LEAD_OUT: + id = "MOVEMENT_LEAD_OUT"; + break; + case MOVEMENT_LINK_TRANSITION: + id = jet ? "MOVEMENT_BRIDGING" : "MOVEMENT_LINK_TRANSITION"; + break; + case MOVEMENT_LINK_DIRECT: + id = "MOVEMENT_LINK_DIRECT"; + break; + case MOVEMENT_RAMP_HELIX: + id = jet ? "MOVEMENT_PIERCE_CIRCULAR" : "MOVEMENT_RAMP_HELIX"; + break; + case MOVEMENT_RAMP_PROFILE: + id = jet ? "MOVEMENT_PIERCE_PROFILE" : "MOVEMENT_RAMP_PROFILE"; + break; + case MOVEMENT_RAMP_ZIG_ZAG: + id = jet ? "MOVEMENT_PIERCE_LINEAR" : "MOVEMENT_RAMP_ZIG_ZAG"; + break; + case MOVEMENT_RAMP: + id = "MOVEMENT_RAMP"; + break; + case MOVEMENT_PLUNGE: + id = jet ? "MOVEMENT_PIERCE" : "MOVEMENT_PLUNGE"; + break; + case MOVEMENT_PREDRILL: + id = "MOVEMENT_PREDRILL"; + break; + case MOVEMENT_EXTENDED: + id = "MOVEMENT_EXTENDED"; + break; + case MOVEMENT_REDUCED: + id = "MOVEMENT_REDUCED"; + break; + case MOVEMENT_HIGH_FEED: + id = "MOVEMENT_HIGH_FEED"; + break; + case MOVEMENT_FINISH_CUTTING: + id = "MOVEMENT_FINISH_CUTTING"; + break; + } + + if (id == undefined) { + id = String(movement); + } + + writeComment(eComment.Info, " " + id); +} + +var currentSpindleSpeed = 0; + +function setSpindeSpeed(_spindleSpeed, _clockwise) { + if (currentSpindleSpeed != _spindleSpeed) { + if (_spindleSpeed > 0) { + spindleOn(_spindleSpeed, _clockwise); + } else { + spindleOff(); + } + currentSpindleSpeed = _spindleSpeed; + } +} + +function onSpindleSpeed(spindleSpeed) { + setSpindeSpeed(spindleSpeed, tool.clockwise); +} + +function onCommand(command) { + writeComment(eComment.Info, " " + getCommandStringId(command)); + + switch (command) { + case COMMAND_START_SPINDLE: + onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); + return; + case COMMAND_SPINDLE_CLOCKWISE: + if (!tool.isJetTool()) { + setSpindeSpeed(spindleSpeed, true); + } + return; + case COMMAND_SPINDLE_COUNTERCLOCKWISE: + if (!tool.isJetTool()) { + setSpindeSpeed(spindleSpeed, false); + } + return; + case COMMAND_STOP_SPINDLE: + if (!tool.isJetTool()) { + setSpindeSpeed(0, true); + } + return; + case COMMAND_COOLANT_ON: + if (tool.isJetTool()) { + // F360 doesn't support coolant with jet tools (water jet/laser/plasma) but we've + // added a parameter to force a coolant to be selected for jet tool operations. Note: tool.coolant + // is not used as F360 doesn't define it. + + if (properties.cutter7_Coolant != eCoolant.Off) { + setCoolant(properties.cutter7_Coolant); + } + } + else { + setCoolant(tool.coolant); + } + return; + case COMMAND_COOLANT_OFF: + setCoolant(eCoolant.Off); //COOLANT_DISABLED + return; + case COMMAND_LOCK_MULTI_AXIS: + return; + case COMMAND_UNLOCK_MULTI_AXIS: + return; + case COMMAND_BREAK_CONTROL: + return; + case COMMAND_TOOL_MEASURE: + if (!tool.isJetTool()) { + probeTool(); + } + return; + case COMMAND_STOP: + writeBlock(mFormat.format(0)); + return; + } +} + +function resetAll() { + xOutput.reset(); + yOutput.reset(); + zOutput.reset(); + fOutput.reset(); +} + +function writeInformation() { + // Calcualte the min/max ranges across all sections + var toolZRanges = {}; + var vectorX = new Vector(1, 0, 0); + var vectorY = new Vector(0, 1, 0); + var ranges = { + x: { min: undefined, max: undefined }, + y: { min: undefined, max: undefined }, + z: { min: undefined, max: undefined }, + }; + var handleMinMax = function (pair, range) { + var rmin = range.getMinimum(); + var rmax = range.getMaximum(); + if (pair.min == undefined || pair.min > rmin) { + pair.min = rmin; + } + if (pair.max == undefined || pair.max < rmin) { // was pair.min - changed by DG 1/4/2021 + pair.max = rmax; + } + } + + var numberOfSections = getNumberOfSections(); + for (var i = 0; i < numberOfSections; ++i) { + var section = getSection(i); + var tool = section.getTool(); + var zRange = section.getGlobalZRange(); + var xRange = section.getGlobalRange(vectorX); + var yRange = section.getGlobalRange(vectorY); + handleMinMax(ranges.x, xRange); + handleMinMax(ranges.y, yRange); + handleMinMax(ranges.z, zRange); + if (is3D()) { + if (toolZRanges[tool.number]) { + toolZRanges[tool.number].expandToRange(zRange); + } else { + toolZRanges[tool.number] = zRange; + } + } + } + + // Display the Range Table + writeComment(eComment.Info, " "); + writeComment(eComment.Info, " Ranges Table:"); + writeComment(eComment.Info, " X: Min=" + xyzFormat.format(ranges.x.min) + " Max=" + xyzFormat.format(ranges.x.max) + " Size=" + xyzFormat.format(ranges.x.max - ranges.x.min)); + writeComment(eComment.Info, " Y: Min=" + xyzFormat.format(ranges.y.min) + " Max=" + xyzFormat.format(ranges.y.max) + " Size=" + xyzFormat.format(ranges.y.max - ranges.y.min)); + writeComment(eComment.Info, " Z: Min=" + xyzFormat.format(ranges.z.min) + " Max=" + xyzFormat.format(ranges.z.max) + " Size=" + xyzFormat.format(ranges.z.max - ranges.z.min)); + + // Display the Tools Table + writeComment(eComment.Info, " "); + writeComment(eComment.Info, " Tools Table:"); + 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) + " CR=" + xyzFormat.format(tool.cornerRadius); + if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) { + comment += " TAPER=" + taperFormat.format(tool.taperAngle) + "deg"; + } + if (toolZRanges[tool.number]) { + comment += " - ZMIN=" + xyzFormat.format(toolZRanges[tool.number].getMinimum()); + } + comment += " - " + getToolTypeName(tool.type) + " " + tool.comment; + writeComment(eComment.Info, comment); + } + } + + // Display the Feedrate and Scaling Properties + writeComment(eComment.Info, " "); + writeComment(eComment.Info, " Feedrate and Scaling Properties:"); + writeComment(eComment.Info, " Feed: Travel speed X/Y = " + properties.fr0_TravelSpeedXY); + writeComment(eComment.Info, " Feed: Travel Speed Z = " + properties.fr1_TravelSpeedZ); + writeComment(eComment.Info, " Feed: Enforce Feedrate = " + properties.fr2_EnforceFeedrate); + writeComment(eComment.Info, " Feed: Scale Feedrate = " + properties.frA_ScaleFeedrate); + writeComment(eComment.Info, " Feed: Max XY Cut Speed = " + properties.frB_MaxCutSpeedXY); + writeComment(eComment.Info, " Feed: Max Z Cut Speed = " + properties.frC_MaxCutSpeedZ); + writeComment(eComment.Info, " Feed: Max Toolpath Speed = " + properties.frD_MaxCutSpeedXYZ); + + // Display the G1->G0 Mapping Properties + writeComment(eComment.Info, " "); + writeComment(eComment.Info, " G1->G0 Mapping Properties:"); + writeComment(eComment.Info, " Map: First G1 -> G0 Rapid = " + properties.mapD_RestoreFirstRapids); + writeComment(eComment.Info, " Map: G1s -> G0 Rapids = " + properties.mapE_RestoreRapids); + writeComment(eComment.Info, " Map: SafeZ Mode = " + eSafeZ.prop[safeZMode].name + " : default = " + safeZHeightDefault); + writeComment(eComment.Info, " Map: Allow Rapid Z = " + properties.mapG_AllowRapidZ); + + writeComment(eComment.Info, " "); +} + +function writeFirstSection() { + // Write out the information block at the beginning of the file + writeInformation(); + + writeComment(eComment.Important, " *** START begin ***"); + + if (properties.gcodeStartFile == "") { + Start(); + } else { + loadFile(properties.gcodeStartFile); + } + + writeComment(eComment.Important, " *** START end ***"); + writeComment(eComment.Important, " "); +} + +// Output a comment +function writeComment(level, text) { + if (level <= properties.job3_CommentLevel) { + if (fw == eFirmware.GRBL) { + writeln("(" + String(text).replace(/[\(\)]/g, "") + ")"); + } + else { + writeln(";" + String(text).replace(/[\(\)]/g, "")); + } + } +} + +// Rapid movements with G1 and differentiated travel speeds for XY +// Changes F360 current XY. +// No longer called for general Rapid only for probing, homing, etc. +function rapidMovementsXY(_x, _y) { + let x = xOutput.format(_x); + let y = yOutput.format(_y); + + if (x || y) { + if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { + error(localize("Radius compensation mode cannot be changed at rapid traversal.")); + } + else { + let f = fOutput.format(propertyMmToUnit(properties.fr0_TravelSpeedXY)); + writeBlock(gMotionModal.format(0), x, y, f); + } + } +} + +// Rapid movements with G1 and differentiated travel speeds for Z +// Changes F360 current Z +// No longer called for general Rapid only for probing, homing, etc. +function rapidMovementsZ(_z) { + let z = zOutput.format(_z); + + if (z) { + if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { + error(localize("Radius compensation mode cannot be changed at rapid traversal.")); + } + else { + let f = fOutput.format(propertyMmToUnit(properties.fr1_TravelSpeedZ)); + writeBlock(gMotionModal.format(0), z, f); + } + } +} + +// Rapid movements with G1 uses the max travel rate (xy or z) and then relies on feedrate scaling +function rapidMovements(_x, _y, _z) { + + rapidMovementsZ(_z); + rapidMovementsXY(_x, _y); +} + +// Calculate the feedX, feedY and feedZ components + +function limitFeedByXYZComponents(curPos, destPos, feed) { + if (!properties.frA_ScaleFeedrate) + return feed; + + var xyz = Vector.diff(destPos, curPos); // Translate the cut so curPos is at 0,0,0 + var dir = xyz.getNormalized(); // Normalize vector to get a direction vector + var xyzFeed = Vector.product(dir.abs, feed); // Determine the effective x,y,z speed on each axis + + // Get the max speed for each axis + let xyLimit = propertyMmToUnit(properties.frB_MaxCutSpeedXY); + let zLimit = propertyMmToUnit(properties.frC_MaxCutSpeedZ); + + // Normally F360 begins a Section (a milling operation) with a Rapid to move to the beginning of the cut. + // Rapids use the defined Travel speed and the Post Processor does not depend on the current location. + // This function must know the current location in order to calculate the actual vector traveled. Without + // the first Rapid the current location is the same as the desination location, which creates a 0 length + // vector. A zero length vector is unusable and so a instead the slowest of the xyLimit or zLimit is used. + // + // Note: if Map: G1 -> Rapid is enabled in the Properties then if the first operation in a Section is a + // cut (which it should always be) then it will be converted to a Rapid. This prevents ever getting a zero + // length vector. + if (xyz.length == 0) { + var lesserFeed = (xyLimit < zLimit) ? xyLimit : zLimit; + + return lesserFeed; + } + + // Force the speed of each axis to be within limits + if (xyzFeed.z > zLimit) { + xyzFeed.multiply(zLimit / xyzFeed.z); + } + + if (xyzFeed.x > xyLimit) { + xyzFeed.multiply(xyLimit / xyzFeed.x); + } + + if (xyzFeed.y > xyLimit) { + xyzFeed.multiply(xyLimit / xyzFeed.y); + } + + // Calculate the new feedrate based on the speed allowed on each axis: feedrate = sqrt(x^2 + y^2 + z^2) + // xyzFeed.length is the same as Math.sqrt((xyzFeed.x * xyzFeed.x) + (xyzFeed.y * xyzFeed.y) + (xyzFeed.z * xyzFeed.z)) + + // Limit the new feedrate by the maximum allowable cut speed + + let xyzLimit = propertyMmToUnit(properties.frD_MaxCutSpeedXYZ); + let newFeed = (xyzFeed.length > xyzLimit) ? xyzLimit : xyzFeed.length; + + if (Math.abs(newFeed - feed) > 0.01) { + return newFeed; + } + else { + return feed; + } +} + +// Linear movements +function linearMovements(_x, _y, _z, _feed) { + if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { + // ensure that we end at desired position when compensation is turned off + xOutput.reset(); + yOutput.reset(); + } + + // Force the feedrate to be scaled (if enabled). The feedrate is projected into the + // x, y, and z axis and each axis is tested to see if it exceeds its defined max. If + // it does then the speed in all 3 axis is scaled proportionately. The resulting feedrate + // is then capped at the maximum defined cutrate. + + let feed = limitFeedByXYZComponents(getCurrentPosition(), new Vector(_x, _y, _z), _feed); + + let x = xOutput.format(_x); + let y = yOutput.format(_y); + let z = zOutput.format(_z); + let f = fOutput.format(feed); + + if (x || y || z) { + if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { + error(localize("Radius compensation mode is not supported.")); + } else { + writeBlock(gMotionModal.format(1), x, y, z, f); + } + } else if (f) { + if (getNextRecord().isMotion()) { // try not to output feed without motion + fOutput.reset(); // force feed on next line + } else { + writeBlock(gMotionModal.format(1), f); + } + } +} + +// 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(eComment.Info, " --- Start custom gcode " + folder + _file); + write(txt); + writeComment("eComment.Info, --- End custom gcode " + folder + _file); + } + } else { + writeComment(eComment.Important, " Can't open file " + folder + _file); + error("Can't open file " + folder + _file); + } +} + +function propertyMmToUnit(_v) { + return (_v / (unit == IN ? 25.4 : 1)); +} + +/* +function mergeProperties(to, from) { + for (var attrname in from) { + to[attrname] = from[attrname]; + } +} + +function Firmware3dPrinterLike() { + FirmwareBase.apply(this, arguments); + this.spindleEnabled = false; +} + +Firmware3dPrinterLike.prototype = Object.create(FirmwareBase.prototype); +Firmware3dPrinterLike.prototype.constructor = Firmware3dPrinterLike; +*/ + +function Start() { + // Is Grbl? + if (fw == eFirmware.GRBL) { + writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning + writeBlock(gFeedModeModal.format(94)); + writeBlock(gPlaneModal.format(17)); + writeBlock(gUnitModal.format(unit == IN ? 20 : 21)); + } + + // Default + else { + writeComment(eComment.Info, " Set Absolute Positioning"); + writeComment(eComment.Info, " Units = " + (unit == IN ? "inch" : "mm")); + writeComment(eComment.Info, " Disable stepper timeout"); + if (properties.job1_SetOriginOnStart) { + writeComment(eComment.Info, " Set current position to 0,0,0"); + } + + writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning + writeBlock(gUnitModal.format(unit == IN ? 20 : 21)); // Set the units + writeBlock(mFormat.format(84), sFormat.format(0)); // Disable steppers timeout + + if (properties.job1_SetOriginOnStart) { + writeBlock(gFormat.format(92), xFormat.format(0), yFormat.format(0), zFormat.format(0)); // Set origin to initial position + } + + if (properties.probe1_OnStart && tool.number != 0 && !tool.isJetTool()) { + onCommand(COMMAND_TOOL_MEASURE); + } + } +} + +function end() { + // Is Grbl? + if (fw == eFirmware.GRBL) { + writeBlock(mFormat.format(30)); + } + + // Default + else { + display_text("Job end"); + } +} + +function spindleOn(_spindleSpeed, _clockwise) { + // Is Grbl? + if (fw == eFirmware.GRBL) { + writeComment(eComment.Important, " >>> Spindle Speed " + speedFormat.format(_spindleSpeed)); + writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed)); + } + + // Default + else { + if (properties.job2_ManualSpindlePowerControl) { + // For manual any positive input speed assumed as enabled. so it's just a flag + if (!this.spindleEnabled) { + writeComment(eComment.Important, " >>> Spindle Speed: Manual"); + askUser("Turn ON " + speedFormat.format(_spindleSpeed) + "RPM", "Spindle", false); + } + } else { + writeComment(eComment.Important, " >>> Spindle Speed " + speedFormat.format(_spindleSpeed)); + writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed)); + } + this.spindleEnabled = true; + } +} + +function spindleOff() { + // Is Grbl? + if (fw == eFirmware.GRBL) { + writeBlock(mFormat.format(5)); + } + + //Default + else { + if (properties.job2_ManualSpindlePowerControl) { + writeBlock(mFormat.format(300), sFormat.format(300), pFormat.format(3000)); + askUser("Turn OFF spindle", "Spindle", false); + } else { + writeBlock(mFormat.format(5)); + } + this.spindleEnabled = false; + } +} + +function display_text(txt) { + // Firmware is Grbl + if (fw == eFirmware.GRBL) { + // Don't display text + } + + // Default + else { + writeBlock(mFormat.format(117), (properties.job8_SeparateWordsWithSpace ? "" : " ") + txt); + } +} + +function circular(clockwise, cx, cy, cz, x, y, z, feed) { + if (!properties.job4_UseArcs) { + linearize(tolerance); + return; + } + + var start = getCurrentPosition(); + + // Firmware is Grbl + if (fw == eFirmware.GRBL) { + if (isFullCircle()) { + if (isHelical()) { + linearize(tolerance); + return; + } + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); + break; + case PLANE_ZX: + writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); + break; + case PLANE_YZ: + writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), yOutput.format(y), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); + break; + default: + linearize(tolerance); + } + } else { + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); + break; + case PLANE_ZX: + writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); + break; + case PLANE_YZ: + writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed)); + break; + default: + linearize(tolerance); + } + } + } + + // Default + else { + // Marlin supports arcs only on XY plane + if (isFullCircle()) { + if (isHelical()) { + linearize(tolerance); + return; + } + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); + break; + default: + linearize(tolerance); + } + } else { + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); + break; + default: + linearize(tolerance); + } + } + } +} + +function askUser(text, title, allowJog) { + // Firmware is RepRap? + if (fw == eFirmware.REPRAP) { + var v1 = " P\"" + text + "\" R\"" + title + "\" S3"; + var v2 = allowJog ? " X1 Y1 Z1" : ""; + writeBlock(mFormat.format(291), (properties.job8_SeparateWordsWithSpace ? "" : " ") + v1 + v2); + } + + // Default + else { + writeBlock(mFormat.format(0), (properties.job8_SeparateWordsWithSpace ? "" : " ") + text); + } +} + +function toolChange() { + // Grbl tool change? + if (fw == eFirmware.GRBL) { + + writeBlock(mFormat.format(6), tFormat.format(tool.number)); + writeBlock(gFormat.format(54)); + } + + // Default tool change + else + { + flushMotions(); + + // Go to tool change position + onRapid(propertyMmToUnit(properties.toolChange1_X), propertyMmToUnit(properties.toolChange2_Y), propertyMmToUnit(properties.toolChangeZ)); + + flushMotions(); + + // turn off spindle and coolant + onCommand(COMMAND_COOLANT_OFF); + onCommand(COMMAND_STOP_SPINDLE); + if (!properties.job2_ManualSpindlePowerControl) { + // Beep + writeBlock(mFormat.format(300), sFormat.format(400), pFormat.format(2000)); + } + + // Disable Z stepper + if (properties.toolChange4_DisableZStepper) { + askUser("Z Stepper will disabled. Wait for STOP!!", "Tool change", false); + writeBlock(mFormat.format(17), 'Z'); // Disable steppers timeout + } + // Ask tool change and wait user to touch lcd button + askUser("Tool " + tool.number + " " + tool.comment, "Tool change", true); + + // Run Z probe gcode + if (properties.probe2_OnToolChange && tool.number != 0) { + onCommand(COMMAND_TOOL_MEASURE); + } + } +} + +function probeTool() { + // Is Grbl? + if (fw == eFirmware.GRBL) { + writeComment(eComment.Important, " >>> WARNING: No probing implemented for GRBL"); + } + + // Default + else { + writeComment(eComment.Important, " Probe to Zero Z"); + writeComment(eComment.Info, " Ask User to Attach the Z Probe"); + writeComment(eComment.Info, " Do Probing"); + writeComment(eComment.Info, " Set Z to probe thickness: " + zFormat.format(propertyMmToUnit(properties.probe3_Thickness))) + if (properties.toolChange3_Z != "") { + writeComment(eComment.Info, " Retract the tool to " + propertyMmToUnit(properties.toolChange3_Z)); + } + writeComment(eComment.Info, " Ask User to Remove the Z Probe"); + + askUser("Attach ZProbe", "Probe", false); + // refer http://marlinfw.org/docs/gcode/G038.html + if (properties.probe4_UseHomeZ) { + writeBlock(gFormat.format(28), 'Z'); + } else { + writeBlock(gMotionModal.format(38.3), fFormat.format(propertyMmToUnit(properties.probe6_G38Speed)), zFormat.format(propertyMmToUnit(properties.probe5_G38Target))); + } + + let z = zFormat.format(propertyMmToUnit(properties.probe3_Thickness)); + writeBlock(gFormat.format(92), z); // Set origin to initial position + + resetAll(); + if (properties.toolChange3_Z != "") { // move up tool to safe height again after probing + rapidMovementsZ(propertyMmToUnit(properties.toolChange3_Z), false); + } + + flushMotions(); + + askUser("Detach ZProbe", "Probe", false); + } +} \ No newline at end of file diff --git a/README.md b/README.md index 764028e..a9d35dc 100644 --- a/README.md +++ b/README.md @@ -6,56 +6,55 @@ This is modified fork of https://github.com/guffy1234/mpcnc_posts_processor that CAM posts processor for use with Fusion 360 and [MPCNC](https://www.v1engineering.com). -Supported firmwares: +Supported firmware: - Marlin 2.0 - Repetier firmware 1.0.3 (not tested. gcode is same as for Marlin) - GRBL 1.1 - RepRap firmware (Duet3d) Installation: -- The post processor has three .cps files. -- Each .csp file is considered by F360 to be a seperate Post Processor. -- Currently each .cps file depends on DIYCNC_common.js. -- Can not use F360 Manage->Post Library menu to install the post processor -as the DIYCNC_common.js will not be installed. -- Instead copy the .cps file and DIYCNC_common.js -to the F360 install director or use the ... to select to the post processor's directory when -running the post processor. +- The post processor consists of a single file, mpcnc.cps. +- It can be simply installed by selecting Manage->Post Library from the Fusion 360 menubar; alternatively the mpcnc.cps can be copied into a directory and selecting each time prior to a post operation. If there is an existing mpcnc.cps installed select it prior to installing and use the trash can icon to delete it +- The desired post processor can be selected during a post using the Setup button and selecting Use Personal Post Library +- Use the Job: CNC Firmware property to select between Marlin 2.x, Grbl 1.1 and RepRap firmware Some design points: - Setup operation types: Milling, Water/Laser/Plasma - Support mm and Inches units (**but all properties MUST be set in MM**) -- Rapids movements use seperate G0 moves, first to move in Z and then to move in XY. Moves use independent travel speeds for XY and Z. +- Rapids movements use two G0 moves. The first moves Z and the second moves XY. Moves are seperate to allow retraction from the work surface prior to horizontal travel. Moves use independent travel speeds for Z and XY. - Arcs support on XY plane (Marlin/Repetier/RepRap) or all panes (Grbl) - 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) - Support 2 coolant channels. You may attach relays to control external devices - as example air jet valve. - Customizable level of verbosity of comments - Support line numbers -- Support GRBL laser mode (**be noted that you probably to have enabled laser mode [$32=1](https://github.com/gnea/grbl/wiki/Grbl-v1.1-Laser-Mode)**) +- Support GRBL laser mode (**note: you probably have to enabled laser mode [$32=1](https://github.com/gnea/grbl/wiki/Grbl-v1.1-Laser-Mode)**) ![screenshot](/screenshot.jpg "screenshot") # Properties +> WARNING: If you are using the Fusion 360 for Personal Use license, formally know as the Fusion 360 Hobbyist license, please respect the [limitations of that license](https://knowledge.autodesk.com/support/fusion-360/learn-explore/caas/sfdcarticles/sfdcarticles/Fusion-360-Free-License-Changes.html). To remain compliant with that license set your [Feed: Travel Speed X/Y] and [Feed: Travel Speed Z] no faster then your machine's maximum cut feedrate (see Group 2 Properties). +> +>Fusion 360 for Personal Use restricts all moves not to exceed the maximum cut speed. This has been implemented not by reducing the speed of G0s but by changing all G0 (moves) to G1 (cut) commands. The side effect of this was to unintentionally introduce situations where tool dragging and/or work piece collisions occur, general at the start of jobs or after tool changes. +> +>You can choose to resolve these issues by enabling the selective mapping of G1s->G0s (see Group 3 Properties). Theses issues are resolved as the post processor implements G0 moves by doing first a move in Z and then a move in X,Y while a G1 cuts travel in X,Y,Z at the same time. + ## Group 1: Job Properties Use these properties to control overall aspects of the job. |Title|Description|Default| |---|---|---| -Job: Duet: Milling Mode|GCode command to setup Duet3d milling mode.|**"M453 P2 I0 R30000 F200"**| -Job: Duet: Laser Mode|GCode command to setup Duet3d laser mode.|**"M452 P2 I0 R255 F200"**| -Job: Goto 0 at end|Go X0 Y0 at gcode end. Useful to find if your machine loss steeps or have any other mechanic issue (like loose pulleys). Also useful for repetitive jobs. Only apply if not using gcodeStopFile.|**true**| -Job: Marlin: Manual Spindle On/Off|Set it to true when the motor of your spindle is controlled by manual switch. So the preprocessor will issue additional pauses for TURN ON/TURN OFF the motor.|**true**| -Job: Marlin: Enforce feedrate|Add feedrate to each movement g-code.|**false**| -Job: Separate words|Specifies that the words should be separated with a white space.|**true**| -Job: Line increment|Increment for sequence numbers.|**1**| -Job: Line start|First sequence number.|**10**| -Job: Line numbers|Show sequence numbers.|**false**| -Job: Reset on start (G92)|Set origin when gcode start (G92 X0 Y0 Z0). Only apply if not using -GcodeStartFile.|**true**| +Job: CNC Firmware|Dialect of GCode to create|**Marlin 2.x**| +Job: Job: Zero Starting Location (G92)|On start set the current location as 0,0,0 (G92).|**true**| +Job: Manual Spindle On/Off|Enable to manually turn spindle motor on/off. Post processor will issue additional pauses for TURN ON/TURN OFF the motor.|**true**| +Job: Comment Level|Controls a increasing level of comments to be included: Off, Important, Info, Debug|**Info**| Job: Use Arcs|Use G2/G3 g-codes for circular movements.|**true**| -Job: Firmware|Target firmware (marlin 2.0 or Repetir 1.0.3 / GRBL 1.1) / RepRap Firmware.|**Marlin**| +Job: Enable Line #s|Show sequence numbers.|**false**| +Job: First Line #|First sequence number.|**1**| +Job: Line # Increment|Sequence number increment.|**10**| +Job: Include Whitespace|Includes whitespace seperation between text.|**true**| +Job: At end go to 0,0|Go to X0 Y0 at gcode end, Z remains unchanged.|**true**| ## Group 2: Travel Speed and Feedrate Scaling Properties Use these properties to set the speed used for G0 Rapids and to scale the feedrate used @@ -63,103 +62,99 @@ for G1 cuts. [Feed: Travel Speed X/Y] and [Feed: Travel Speed Z] are always used for G0 Rapids. -Scaling of the G1 cut feedrates will only occur if [Feed:Scaled feedrate] is true. +Scaling of the G1 cut feedrates will only occur if [Feed:Scaled Feedrate] is true. Scaling ensures that no G1 cut exceeds the speed capablities of the X, Y, or Z axes. The cut's toolpath feedrate is projected onto the X, Y and Z axes. In turn each axis is tested to see if its cut speed is within the limits of that axis. If not, then all axes feedrates are scaled proportionatly to bring it within limits. This is repeated for all axes. The three axis feedrates are then merged to create a new toolpath feedrate which is then limited to ensure it -doesn't exceed [Feed: Max toolpath speed]. +doesn't exceed [Feed: Max Toolpath Speed]. Note: Because scaling considered 3 dimensional movement a resulting toolpath's feedrate may be greater then one or all of the X, Y or Z limits. For example, a small movement in Z compared to a much larger movement in XY may result in a feedrate that appears to exceed the capability of -Z but in reality since Z is move a much smaller distance for the same time period its actual feedrate is within the established limits. +Z but in reality since Z is moving a much smaller distance for the same time period its actual +feedrate is within the established limits. |Title|Description|Default| |---|---|---| Feed: Travel Speed X/Y|High speed for travel movements X & Y (mm/min).|**2500 mm/min**| Feed: Travel Speed Z|High speed for travel movements Z (mm/min).|**300 mm/min**| -Feed: Scaled feedrate|Scale cut feedrates to respect XY and Z max cut speeds.|**false**| -Feed: Max cut speed X or Y|Maximum cut speed along the X or Y axes (mm/min).|**900 mm/min**| -Feed: Max cut speed Z|Maximum cut speed along the Z axis (mm/min).|**180 mm/min**| -Feed: Max toolpath speed|Maximum cut speed along the toolpath (mm/min).|**1000 mm/min**| - - - +Feed: Enforce Feedrate|Forces the Fxxx to be include even if hasn't changed, useful for Marlin.|**true**| +Feed: Scaled Feedrate|Scale feedrate based on X, Y, Z axis maximums.|**false**| +Feed: Max Cut Speed X or Y|Maximum X or Y axis cut speed (mm/min).|**900 mm/min**| +Feed: Max Cut Speed Z|Maximum Z axis cut speed (mm/min).|**180 mm/min**| +Feed: Max Toolpath Speed|Maximum scaled feedrate for toolpath (mm/min).|**1000 mm/min**| ## Group 3: Map G1->G0 Properties -Performs three actions by allowing G1 cuts to be mapped to G0 Rapid movements. +Allows G1 cuts to be converted to G0 Rapid movements in specific cases: -1. If [Map: First G1 -> G0 Rapids] is true the post processor resolves the loss of the +If [Map: First G1 -> G0 Rapid] is true the post processor resolves the lost initial positioning movement at the beginning of a cut toolpath. This problem is often identified in forums as the tool being initially dragged across the work surface. -2. If [Map: G1 -> G0] is true then allows G1 XY cut movements (i.e. no change in Z) that occur -at a height greater or equal to [Map: Safe Z for Rapids] to be converted to G0 Rapids. -Note: this assumes that the top of material is 0 in F360 and that any Z above -[Map: Safe Z for Rapids] is a movement in the air. If top of material is not 0, then adjust -[Map: Safe Z for Rapids] appropriately. +If [Map: G1s -> G0s] is true then allows G1 XY cut movements (i.e. no change in Z) that occur +at a height greater or equal to [Map: Safe Z to Rapid] to be converted to G0 Rapids. +Note: this assumes that any Z above [Map: Safe Z to Rapid] is a movement in the air and clear of +obstacles. Can be defined as a number or one of F360's planes (Feed, Retract or Clearance). -3. If [Map: Allow Rapid Z] is true then includes G1 Z cut movements that either move straight up -and end above [Map: Safe Z for Rapids], or straight down with the start and end positions both -above [Map: Safe Z for Rapids]. Only occurs if [Map: G1 -> G0] is also true. +Map: Safe Z for Rapids may be defined as: +* As a constant numeric value - safe Z will then always be this value for all sections, or +* As a reference to a F360 Height - safe Z will then follow the Height defined within the operation's Height tab. Allowable Heights are: Feed, Retract, or Clearance. The Height must be followed by a ":" and then a numeric value. The value will be used if Height is not defined for a section. -|Title|Description|Default| -|---|---|---| -Map: First G1 -> G0 Rapids|Convert first G1 of a cut to G0 Rapid|**false**| -Map: G1 -> G0|Allow G1 cuts to be converted to Rapid G0 moves when safe and appropriate.|**false**| -Map: Safe Z for Rapids|A G1 cut's Z must be >= to this to be mapped to a Rapid G0.|**10**| -Map: Allow Rapid Z|Include vertical cut if they are safe.|**true**| +If [Map: Allow Rapid Z] is true then G1 Z cut movements that either move straight up +and end above [Map: Safe Z to Rapid], or straight down with the start and end positions both +above [Map: Safe Z to Rapid] are included. Only occurs if [Map: G1s -> G0s] is also true. + +|Title|Description|Default|Format| +|---|---|---|---| +Map: First G1 -> G0 Rapid|Converts the first G1 of a cut to G0 Rapid|**false**| | +Map: G1s -> G0s|Allow G1 cuts to be converted to Rapid G0 moves when safe and appropriate.|**false**| | +Map: Safe Z for Rapids|A G1 cut's Z must be >= to this to be mapped to a Rapid G0. Can be two formats (1) a number which will be used for all sections, or (2) a reference to F360's Height followed by a default if Height is not available.|**Retract:15** (use the Retract height and if not available 15)| \ or \:\; e.g. 10 or Retract:7 or Feed:5| +Map: Allow Rapid Z|Include the mapping of vertical cuts if they are safe.|**false**| ## Group 4: Tool change Properties |Title|Description|Default| |---|---|---| -Change: Disable Z stepper|Disable Z stepper when change a tool|**false**| -Change: Enabled|Enable tool change code (bultin tool change requires LCD display)|**true**| -Change: X|X position for builtin tool change|**0**| -Change: Y|Y position for builtin tool change|**0**| -Change: Z|Z position for builtin tool change|**40**| -Change: Make Z Probe|Z probe after tool change|**true**| +Tool Change: Enable|Include tool change code when tool changes (bultin tool change requires LCD display|**false**| +Tool Change: X|X position for built-in tool change|**0**| +Tool Change: Y|Y position for built-in tool change|**0**| +Tool Change: Z|Z position for built-in tool change|**40**| +Tool Change: Disable Z stepper|Disable Z stepper after reaching tool change location|**false**| ## Group 5: Z Probe Properties |Title|Description|Default| |---|---|---| -Probe: On job start|Execute probe gcode on job start|**true**| +Probe: On job start|Execute probe gcode on job start|**false**| +Probe: After Tool Change|Z probe after tool change|**false**| Probe: Plate thickness|Plate thickness|**0.8**| -Probe: Use Home Z|Use G28 or G38 for probing|**true**| -Probe: G38 target|Probing up to Z position|**-10**| -Probe: G38 speed|Probing with speed|**30**| +Probe: Use Home Z (G28)|Probe with G28 (Yes) or G38 (No)|**true**| +Probe: G38 target|G38 Probing's furthest Z position|**-10**| +Probe: G38 speed|G38 Probing's speed|**30**| ## Group 6: Laser/Plasma Properties +Fusion 360 defines four levels of Through cut, currently these all map to power level "On - Through". + +The firmware selected in the parameter [Job: CNC Firmware] determines if the Grbl or Marlin/Reprap laser parameters are used. + +Fusion 360 does not use a coolant when using its jet tools (waterjet/laser/plasma). When using a laser it may be desirable to use air or some other device you have connected to the coolant channels. The [Laser: Coolant] can be used to force a coolant to be used for the laser operations (see coolant parameter on details for configuring the coolant channels). + |Title|Description|Default|Values| |---|---|---|---| Laser: On - Vaporize|Persent of power to turn on the laser/plasma cutter in vaporize mode|**100**|| Laser: On - Through|Persent of power to turn on the laser/plasma cutter in through mode|**80**|| Laser: On - Etch|Persent of power to turn on the laser/plasma cutter in etch mode|**40**|| -Laser: Marlin mode|Marlin mode of the laser/plasma cutter ()|**M106**|M106 S{PWM}/M107 = 0; M3 O{PWM}/M5 = 1; M42 P{pin} S{PWM} = 2;| -Laser: Marlin pin|Marlin custom pin number for the laser/plasma cutter|**4**|| -Laser: GRBL mode|GRBL mode of the laser/plasma cutter|**M4**|M4 S{PWM}/M5 dynamic power = 4; M3 S{PWM}/M5 static power = 3;| - -## Group 7: Coolant Control Pin Properties - -|Title|Description|Default|Values| -|---|---|---|---| -Coolant: A Mode|Enable issuing g-codes for control Coolant channel A|**0**|off=0; flood=1; mist=2; throughTool=3; air=4; airThroughTool=5; suction=6; floodMist=7; floodThroughTool=8| -Coolant: A Marlin On command|GCode command to turn on Coolant channel A|**M42 P11 S255**|| -Coolant: A Marlin Off command|Gcode command to turn off Coolant A|**M42 P11 S0**|| -Coolant: A GRBL|GRBL g-codes for control Coolant channel A|**M7**|M7 flood = 7; M8 mist = 8| -Coolant: B Mode|Enable issuing g-codes for control Coolant channel B|**0**|off=0; flood=1; mist=2; throughTool=3; air=4; airThroughTool=5; suction=6; floodMist=7; floodThroughTool=8| -Coolant: B Marlin On command|GCode command to turn on Coolant channel B|**M42 P6 S255**|| -Coolant: B Marlin Off command|Gcode command to turn off Coolant channel B|**M42 P6 S0**|| -Coolant: B GRBL|GRBL g-codes for control Coolant channel B|**M8**|M7 flood = 7; M8 mist = 8| +Laser: Marlin/Reprap Mode|Marlin/Reprap mode of the laser/plasma cutter|**Fan - M106 S{PWM}/M107**|"Fan - M106 S{PWM}/M107", "Spindle - M3 O{PWM}/M5", "Pin - M42 P{pin} S{PWM}"| +Laser: Marlin M42 Pin|Marlin custom pin number for the laser/plasma cutter|**4**|| +Laser: GRBL Mode|GRBL mode of the laser/plasma cutter|**M4 S{PWM}/M5 dynamic power**|"M4 S{PWM}/M5 dynamic power", "M3 S{PWM}/M5 static power"| +Laser: Coolant|Force a coolant to be used|**Off**|off, flood, mist, throughTool, air, airThroughTool, suction, floodMist, floodThroughTool| -## Group 8: Override Behaviour by External File Properties +## Group 7: Override Behaviour by External File Properties |Title|Description|Default| |---|---|---| @@ -168,550 +163,42 @@ Extern: Stop File|File with custom Gcode for footer/end (in nc folder)|| Extern: Tool File|File with custom Gcode for tool change (in nc folder)|| Extern: Probe File|File with custom Gcode for tool probe (in nc folder)|| -## Group 9: Write Comments Properties - -|Title|Description|Default| -|---|---|---| -Comment: Write Tools|Write table of used tools in job header|true| -Comment: Sections|Write header of every section|true| -Comment: Activities|Write comments which somehow helps to understand current piece of g-code|true| -Comment: Trace Commands|Write stringified commands called by CAM|true| -Comment: Trace Movements|Write stringified movements called by CAM|true| +## Group 7: Coolant Control Pin Properties +Coolant has two channels, A and B. Each channel can be configured to be off or set to 1 of the 8 coolant modes that Fusion 360 allows on operation. If a tool's collant requirements match a channel's setting then that channel is enabled. A warning is generated if a tool askes for coolant and there is not a channel that matches. -# Sample of issued code blocks +If a channel matches the coolant requested the Channel becomes enabled. When a channel is enabled the post processor will include the text associated with the corresponding property [Coolant \ Enable]. Note, Marlin and Grbl values are included as options, you must select based on your actual configuration. The firmware selected in property [Job: CNC Firmware] will not override your selection. -## Gcode of milling with manually control spindel +If a channel needs to be Disabled because it no longer matchs the coolant requested then the channel is physically disabled by the post processor by including the text associated with the corresponding property [Coolant \ Disable]. Note, Marlin and Grbl values are included as options, you must select based on your actual configuration. The firmware selected in the propery [Job: CNC Firmware] will not override your selection. -```G-code -;Fusion 360 CAM 2.0.4860 -; Posts processor: MPCNC_Mill_Laser.cps -; Gcode generated: Sunday, December 2, 2018 1:57:21 PM GMT -; Document: cam_testpp v5 -; Setup: Setup1 -; -; Ranges table: -; X: Min=2.588 Max=36 Size=33.412 -; Y: Min=2.588 Max=36 Size=33.412 -; Z: Min=-1 Max=15 Size=16 -; -; Tools table: -; T1 D=3.175 CR=0 - ZMIN=-1 - flat end mill -; T2 D=1.5 CR=0 - ZMIN=-1 - flat end mill - -; *** START begin *** -G90 -G21 -M84 S0 -G92 X0 Y0 Z0 -; COMMAND_TOOL_MEASURE -; --- PROBE TOOL begin --- -M0 Attach ZProbe -G28 Z -G92 Z0.8 -G0 Z50 F300 -M0 Detach ZProbe -; --- PROBE TOOL end --- -; *** START end *** - -; *** SECTION begin *** -;2D Contour1 - Milling - Tool: 1 - 1/8inch flat end mill -; X Min: 2.588 - X Max: 49.412 -; Y Min: 2.588 - Y Max: 49.412 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -M0 Turn ON spindle -; COMMAND_COOLANT_ON -M117 2D Contour1 -G0 Z15 -G0 X49.412 Y26 F2500 -G0 Z5 F300 -; MOVEMENT_PLUNGE -G1 Z1 F100 -G1 Z-1 -; 14 -G1 Y49.412 F300 -G1 X2.588 -G1 Y2.588 -G1 X49.412 -G1 Y26 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** SECTION begin *** -;2D Contour2 - Milling - Tool: 1 - 1/8inch flat end mill -; X Min: 9.587 - X Max: 42.412 -; Y Min: 9.587 - Y Max: 42.412 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -; COMMAND_COOLANT_ON -M117 2D Contour2 -G0 Z15 F300 -G0 X42.412 Y26 F2500 -G0 Z5 F300 -; MOVEMENT_PLUNGE -G1 Z1 F100 -G1 Z-1 -; 14 -G1 Y42.412 F300 -G1 X9.587 -G1 Y9.587 -G1 X42.412 -G1 Y26 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** SECTION begin *** -; --- CHANGE TOOL begin --- -; COMMAND_COOLANT_OFF -M400 -M300 S400 P2000 -G0 Z50 F300 -G0 X0 Y0 F2500 -; COMMAND_STOP_SPINDLE -M0 Turn OFF spindle -M0 Put tool 2 - 1.5mm -; COMMAND_TOOL_MEASURE -; --- PROBE TOOL begin --- -M0 Attach ZProbe -G28 Z -G92 Z0.8 -G0 Z50 F300 -M0 Detach ZProbe -; --- PROBE TOOL end --- -; --- CHANGE TOOL end --- -;Trace1 - Milling - Tool: 2 - 1.5mm flat end mill -; X Min: 16 - X Max: 36 -; Y Min: 16 - Y Max: 36 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -M0 Turn ON spindle -; COMMAND_COOLANT_ON -M117 Trace1 -G0 Z15 -G0 X36 Y36 F2500 -G0 Z4 F300 -; MOVEMENT_LEAD_IN -G1 Z-1 F300 -; MOVEMENT_CUTTING -G1 Y16 -G1 X16 -G1 Y36 -G1 X36 -; MOVEMENT_LEAD_OUT -G1 Z4 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** STOP begin *** -M400 -; COMMAND_COOLANT_OFF -; COMMAND_STOP_SPINDLE -M0 Turn OFF spindle -G0 X0 Y0 F2500 -M117 Job end -; *** STOP end *** -``` +For coolant requests, like "Flood and Mist" or "Flood and Through Tool" you may want to enable one or +two channels dependent on if your hardware uses one connections to enable both or a seperate connection for each. Two channels may be enabled by placing the same coolant code in both. For example, setting both channels to "Flood and Mist" will result in enabling both channel A and channel B when the tool requests "Flood and Mist". Correspondingly channels A's enable value will be output (to enable flooding) and channel B's enable value will be output (to enable Mist). -## Gcode of milling with spindel controlled by M3/M4/M5 +|Title|Description|Default|Values| +|---|---|---|---| +Coolant: A Mode|Enable channel A when tool is set this coolant|**off**|off, flood, mist, throughTool, air, airThroughTool, suction, floodMist, floodThroughTool| +Coolant: B Mode|Enable channel B when tool is set this coolant|**off**|off, flood, mist, throughTool, air, airThroughTool, suction, floodMist, floodThroughTool| +Coolant: A Enable|GCode to turn On coolant channel A|**Mrln: M42 P6 S255**|"Mrln: M42 P6 S255", , Mrln: M42 P11 S255", "Grbl: M7 (mist)", "Grbl: M8 (flood)"| +Coolant: A Disable Off command|GCode to turn Off coolant channel A|**Mrln: M42 P6 S0**|"Mrln: M42 P6 S0", "Mrln: M42 P11 S0", "Grbl: M9 (off)"| +Coolant: B Enable|GCode to turn On coolant channel B|**Mrln: M42 P11 S255**|"Mrln: M42 P11 S255", "Mrln: M42 P6 S255", "Grbl: M7 (mist)", "Grbl: M8 (flood)"| +Coolant: B Disable Off command|GCode to turn Off coolant channel B|**Mrln: M42 P11 S0**|"Mrln: M42 P11 S0", "Mrln: M42 P6 S0", "Grbl: M9 (off)"| -```G-code -;Fusion 360 CAM 2.0.4860 -; Posts processor: MPCNC_Mill_Laser.cps -; Gcode generated: Sunday, December 2, 2018 1:56:26 PM GMT -; Document: cam_testpp v5 -; Setup: Setup1 -; -; Ranges table: -; X: Min=2.588 Max=36 Size=33.412 -; Y: Min=2.588 Max=36 Size=33.412 -; Z: Min=-1 Max=15 Size=16 -; -; Tools table: -; T1 D=3.175 CR=0 - ZMIN=-1 - flat end mill -; T2 D=1.5 CR=0 - ZMIN=-1 - flat end mill - -; *** START begin *** -G90 -G21 -M84 S0 -G92 X0 Y0 Z0 -; COMMAND_TOOL_MEASURE -; --- PROBE TOOL begin --- -M0 Attach ZProbe -G28 Z -G92 Z0.8 -G0 Z50 F300 -M0 Detach ZProbe -; --- PROBE TOOL end --- -; *** START end *** - -; *** SECTION begin *** -;2D Contour1 - Milling - Tool: 1 - 1/8inch flat end mill -; X Min: 2.588 - X Max: 49.412 -; Y Min: 2.588 - Y Max: 49.412 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -; >>> Spindle Speed 21000 -M3 S21000 -; COMMAND_COOLANT_ON -M117 2D Contour1 -G0 Z15 -G0 X49.412 Y26 F2500 -G0 Z5 F300 -; MOVEMENT_PLUNGE -G1 Z1 F100 -G1 Z-1 -; 14 -G1 Y49.412 F300 -G1 X2.588 -G1 Y2.588 -G1 X49.412 -G1 Y26 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** SECTION begin *** -;2D Contour2 - Milling - Tool: 1 - 1/8inch flat end mill -; X Min: 9.587 - X Max: 42.412 -; Y Min: 9.587 - Y Max: 42.412 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -; >>> Spindle Speed 20000 -M3 S20000 -; COMMAND_COOLANT_ON -M117 2D Contour2 -G0 Z15 F300 -G0 X42.412 Y26 F2500 -G0 Z5 F300 -; MOVEMENT_PLUNGE -G1 Z1 F100 -G1 Z-1 -; 14 -G1 Y42.412 F300 -G1 X9.587 -G1 Y9.587 -G1 X42.412 -G1 Y26 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** SECTION begin *** -; --- CHANGE TOOL begin --- -; COMMAND_COOLANT_OFF -M400 -M300 S400 P2000 -G0 Z50 F300 -G0 X0 Y0 F2500 -; COMMAND_STOP_SPINDLE -M5 -M0 Put tool 2 - 1.5mm -; COMMAND_TOOL_MEASURE -; --- PROBE TOOL begin --- -M0 Attach ZProbe -G28 Z -G92 Z0.8 -G0 Z50 F300 -M0 Detach ZProbe -; --- PROBE TOOL end --- -; --- CHANGE TOOL end --- -;Trace1 - Milling - Tool: 2 - 1.5mm flat end mill -; X Min: 16 - X Max: 36 -; Y Min: 16 - Y Max: 36 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -; >>> Spindle Speed 21000 -M3 S21000 -; COMMAND_COOLANT_ON -M117 Trace1 -G0 Z15 -G0 X36 Y36 F2500 -G0 Z4 F300 -; MOVEMENT_LEAD_IN -G1 Z-1 F300 -; MOVEMENT_CUTTING -G1 Y16 -G1 X16 -G1 Y36 -G1 X36 -; MOVEMENT_LEAD_OUT -G1 Z4 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** STOP begin *** -M400 -; COMMAND_COOLANT_OFF -; COMMAND_STOP_SPINDLE -M5 -G0 X0 Y0 F2500 -M117 Job end -; *** STOP end *** -``` +## Group 9: Duet Properties -## Gcode of milling with spindel controlled by M3/M4/M5 with using Coolants (both A and B channels) +|Title|Description|Default| +|---|---|---| +Duet: Milling mode|GCode command to setup Duet3d milling mode|**M453 P2 I0 R30000 F200**| +Duet: Laser mode|GCode command to setup Duet3d laser mode|**M452 P2 I0 R255 F200**| -```G-code -;Fusion 360 CAM 2.0.4860 -; Posts processor: MPCNC_Mill_Laser.cps -; Gcode generated: Sunday, December 2, 2018 2:06:54 PM GMT -; Document: cam_testpp v5 -; Setup: Setup1 -; -; Ranges table: -; X: Min=2.588 Max=36 Size=33.412 -; Y: Min=2.588 Max=36 Size=33.412 -; Z: Min=-1 Max=15 Size=16 -; -; Tools table: -; T1 D=3.175 CR=0 - ZMIN=-1 - flat end mill -; T2 D=1.5 CR=0 - ZMIN=-1 - flat end mill - -; *** START begin *** -G90 -G21 -M84 S0 -G92 X0 Y0 Z0 -; COMMAND_TOOL_MEASURE -; --- PROBE TOOL begin --- -M0 Attach ZProbe -G28 Z -G92 Z0.8 -G0 Z50 F300 -M0 Detach ZProbe -; --- PROBE TOOL end --- -; *** START end *** - -; *** SECTION begin *** -;2D Contour1 - Milling - Tool: 1 - 1/8inch flat end mill -; X Min: 2.588 - X Max: 49.412 -; Y Min: 2.588 - Y Max: 49.412 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -; >>> Spindle Speed 21000 -M3 S21000 -; COMMAND_COOLANT_ON -; >>> Coolant A ON -M42 P11 S255 -M117 2D Contour1 -G0 Z15 -G0 X49.412 Y26 F2500 -G0 Z5 F300 -; MOVEMENT_PLUNGE -G1 Z1 F100 -G1 Z-1 -; 14 -G1 Y49.412 F300 -G1 X2.588 -G1 Y2.588 -G1 X49.412 -G1 Y26 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** SECTION begin *** -;2D Contour2 - Milling - Tool: 1 - 1/8inch flat end mill -; X Min: 9.587 - X Max: 42.412 -; Y Min: 9.587 - Y Max: 42.412 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -; >>> Spindle Speed 20000 -M3 S20000 -; COMMAND_COOLANT_ON -; >>> Coolant A OFF -M42 P11 S0 -; >>> Coolant B ON -M42 P6 S255 -M117 2D Contour2 -G0 Z15 F300 -G0 X42.412 Y26 F2500 -G0 Z5 F300 -; MOVEMENT_PLUNGE -G1 Z1 F100 -G1 Z-1 -; 14 -G1 Y42.412 F300 -G1 X9.587 -G1 Y9.587 -G1 X42.412 -G1 Y26 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** SECTION begin *** -; --- CHANGE TOOL begin --- -M400 -G0 Z50 F300 -G0 X0 Y0 F2500 -; COMMAND_COOLANT_OFF -; >>> Coolant B OFF -M42 P6 S0 -; COMMAND_STOP_SPINDLE -M5 -M300 S400 P2000 -M0 Put tool 2 - 1.5mm -; COMMAND_TOOL_MEASURE -; --- PROBE TOOL begin --- -M0 Attach ZProbe -G28 Z -G92 Z0.8 -G0 Z50 F300 -M0 Detach ZProbe -; --- PROBE TOOL end --- -; --- CHANGE TOOL end --- -;Trace1 - Milling - Tool: 2 - 1.5mm flat end mill -; X Min: 16 - X Max: 36 -; Y Min: 16 - Y Max: 36 -; Z Min: -1 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_CLOCKWISE -; >>> Spindle Speed 21000 -M3 S21000 -; COMMAND_COOLANT_ON -M117 Trace1 -G0 Z15 -G0 X36 Y36 F2500 -G0 Z4 F300 -; MOVEMENT_LEAD_IN -G1 Z-1 F300 -; MOVEMENT_CUTTING -G1 Y16 -G1 X16 -G1 Y36 -G1 X36 -; MOVEMENT_LEAD_OUT -G1 Z4 -; MOVEMENT_RAPID -G0 Z15 -; *** SECTION end *** - -; *** STOP begin *** -M400 -; COMMAND_COOLANT_OFF -; COMMAND_STOP_SPINDLE -M5 -G0 X0 Y0 F2500 -M117 Job end -; *** STOP end *** -``` +# Sample of issued code blocks -## Gcode of laser cutting +## Gcode of milling with manually control spindle ```G-code -;Fusion 360 CAM 2.0.4860 -; Posts processor: MPCNC_Mill_Laser.cps -; Gcode generated: Sunday, December 2, 2018 2:07:32 PM GMT -; Document: cam_testpp v5 -; Setup: Setup2 -; -; Ranges table: -; X: Min=-25 Max=25 Size=50 -; Y: Min=-25.5 Max=25 Size=50.5 -; Z: Min=0 Max=15 Size=15 -; -; Tools table: -; T1 D=0 CR=0 - ZMIN=0 - laser cutter - -; *** START begin *** -G90 -G21 -M84 S0 -G92 X0 Y0 Z0 -; COMMAND_TOOL_MEASURE -; *** START end *** - -; *** SECTION begin *** -;2D Profile1 - Laser/Plasma - Cutting mode: auto -; X Min: -25 - X Max: 25 -; Y Min: -25.5 - Y Max: 25 -; Z Min: 0 - Z Max: 15 -M400 -; COMMAND_START_SPINDLE -; COMMAND_SPINDLE_COUNTERCLOCKWISE -; COMMAND_COOLANT_ON -M117 2D Profile1 -G0 Z15 F300 -G0 X-9.94 Y-10.5 F2500 -G0 Z0 F300 -; >>> LASER Power ON -M106 S200 -; COMMAND_POWER_ON -; MOVEMENT_LEAD_IN -G1 Y-10 F1000 -G1 X-9.95 -; MOVEMENT_CUTTING -G1 X-10 -G1 Y10 -G1 X10 -G1 Y-10 -G1 X-9.95 -; MOVEMENT_LEAD_OUT -G1 X-9.96 -G1 Y-10.5 -; >>> LASER Power OFF -M107 -; COMMAND_POWER_OFF -; MOVEMENT_RAPID -G0 Z5 F300 -G0 X-9.99 Y-25.5 F2500 -G0 Z0 F300 -; >>> LASER Power ON -M106 S200 -; COMMAND_POWER_ON -; MOVEMENT_LEAD_IN -G1 Y-25 F1000 -G1 X-10 -; MOVEMENT_CUTTING -G1 X-25 -G1 Y25 -G1 X25 -G1 Y-25 -G1 X-10 -; MOVEMENT_LEAD_OUT -G1 X-10.01 -G1 Y-25.5 -; >>> LASER Power OFF -M107 -; COMMAND_POWER_OFF -; MOVEMENT_RAPID -G0 Z15 F300 -; *** SECTION end *** - -; *** STOP begin *** -M400 -; COMMAND_COOLANT_OFF -; COMMAND_STOP_SPINDLE -G0 X0 Y0 F2500 -M117 Job end -; *** STOP end *** +To be updated ``` -# Resorces +# Resources [Marlin G-codes](http://marlinfw.org/meta/gcode/)