├── LICENSE ├── MPCNC.cps ├── README.md ├── Test ├── 1001_laser.gcode ├── 1001_m3m5.gcode ├── 1001_m3m5_coolant.gcode ├── 1001_manual.gcode └── cam_testpp.f3d └── screenshot.jpg /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 martindb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MPCNC.cps: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | https://github.com/guffy1234/mpcnc_posts_processor 4 | 5 | MPCNC posts processor for milling and laser/plasma cutting. 6 | 7 | */ 8 | 9 | description = "MPCNC Milling/Laser - Marlin 2.0, Grbl 1.1, RepRap"; 10 | vendor = "flyfisher604"; 11 | vendorUrl = "https://github.com/flyfisher604/mpcnc_post_processor"; 12 | 13 | // Internal properties 14 | certificationLevel = 2; 15 | extension = "gcode"; 16 | setCodePage("ascii"); 17 | capabilities = CAPABILITY_MILLING | CAPABILITY_JET; 18 | 19 | machineMode = undefined; //TYPE_MILLING, TYPE_JET 20 | 21 | var eFirmware = { 22 | MARLIN: 0, 23 | GRBL: 1, 24 | REPRAP: 2, 25 | prop: { 26 | 0: {name: "Marlin 2.x", value: 0}, 27 | 1: {name: "Grbl 1.1", value: 1}, 28 | 2: {name: "RepRap", value: 2} 29 | } 30 | }; 31 | 32 | var fw = eFirmware.MARLIN; 33 | 34 | var eComment = { 35 | Off: 0, 36 | Important: 1, 37 | Info: 2, 38 | Debug: 3, 39 | prop: { 40 | 0: {name: "Off", value: 0}, 41 | 1: {name: "Important", value: 1}, 42 | 2: {name: "Info", value: 2}, 43 | 3: {name: "Debug", value: 3} 44 | } 45 | }; 46 | 47 | var eCoolant = { 48 | Off: 0, 49 | Flood: 1, 50 | Mist: 2, 51 | ThroughTool: 3, 52 | Air: 4, 53 | AirThroughTool: 5, 54 | Suction: 6, 55 | FloodMist: 7, 56 | FloodThroughTool: 8, 57 | prop: { 58 | 0: {name: "Off", value: 0}, 59 | 1: {name: "Flood", value: 1}, 60 | 2: {name: "Mist", value: 2}, 61 | 3: {name: "ThroughTool", value: 3}, 62 | 4: {name: "Air", value: 4}, 63 | 5: {name: "AirThroughTool", value: 5}, 64 | 6: {name: "Suction", value: 6}, 65 | 7: {name: "Flood and Mist", value: 7}, 66 | 8: {name: "Flood and ThroughTool", value: 8}, 67 | } 68 | }; 69 | 70 | 71 | // user-defined properties 72 | properties = { 73 | job0_SelectedFirmware : fw, // Firmware to use in special cases 74 | job1_SetOriginOnStart: true, // Set current position as 0,0,0 on start (G92) 75 | job2_ManualSpindlePowerControl: true, // Spindle motor is controlled by manual switch 76 | job3_CommentLevel: eComment.Info, // The level of comments included 77 | job4_UseArcs: true, // Produce G2/G3 for arcs 78 | job5_SequenceNumbers: false, // show sequence numbers 79 | job6_SequenceNumberStart: 10, // first sequence number 80 | job7_SequenceNumberIncrement: 1, // increment for sequence numbers 81 | job8_SeparateWordsWithSpace: true, // specifies that the words should be separated with a white space 82 | job9_GoOriginOnFinish: true, // Go X0 Y0 current Z at end 83 | 84 | fr0_TravelSpeedXY: 2500, // High speed for travel movements X & Y (mm/min) 85 | fr1_TravelSpeedZ: 300, // High speed for travel movements Z (mm/min) 86 | fr2_EnforceFeedrate: true, // Add feedrate to each movement line 87 | frA_ScaleFeedrate: false, // Will feedrated be scaled 88 | frB_MaxCutSpeedXY: 900, // Max speed for cut movements X & Y (mm/min) 89 | frC_MaxCutSpeedZ: 180, // Max speed for cut movements Z (mm/min) 90 | frD_MaxCutSpeedXYZ: 1000, // Max feedrate after scaling 91 | 92 | mapD_RestoreFirstRapids: false, // Map first G01 --> G00 93 | mapE_RestoreRapids: false, // Map G01 --> G00 for SafeTravelsAboveZ 94 | mapF_SafeZ: "Retract:15", // G01 mapped to G00 if Z is >= jobSafeZRapid 95 | mapG_AllowRapidZ: false, // Allow G01 --> G00 for vertical retracts and Z descents above safe 96 | 97 | toolChange0_Enabled: false, // Enable tool change code (bultin tool change requires LCD display) 98 | toolChange1_X: 0, // X position for builtin tool change 99 | toolChange2_Y: 0, // Y position for builtin tool change 100 | toolChange3_Z: 40, // Z position for builtin tool change 101 | toolChange4_DisableZStepper: false, // disable Z stepper when change a tool 102 | 103 | probe1_OnStart: false, // Execute probe gcode to align tool 104 | probe2_OnToolChange: false, // Z probe after tool change 105 | probe3_Thickness: 0.8, // plate thickness 106 | probe4_UseHomeZ: true, // use G28 or G38 for probing 107 | probe5_G38Target: -10, // probing up to pos 108 | probe6_G38Speed: 30, // probing with speed 109 | 110 | gcodeStartFile: "", // File with custom Gcode for header/start (in nc folder) 111 | gcodeStopFile: "", // File with custom Gcode for footer/end (in nc folder) 112 | gcodeToolFile: "", // File with custom Gcode for tool change (in nc folder) 113 | gcodeProbeFile: "", // File with custom Gcode for tool probe (in nc folder) 114 | 115 | cutter1_OnVaporize: 100, // Percentage of power to turn on the laser/plasma cutter in vaporize mode 116 | cutter2_OnThrough: 80, // Percentage of power to turn on the laser/plasma cutter in through mode 117 | cutter3_OnEtch: 40, // Percentage of power to turn on the laser/plasma cutter in etch mode 118 | cutter4_MarlinMode: 106, // Marlin mode laser/plasma cutter 119 | cutter5_MarlinPin: 4, // Marlin laser/plasma cutter pin for M42 120 | cutter6_GrblMode: 4, // GRBL mode laser/plasma cutter 121 | cutter7_Coolant: eCoolant.Off, // Use this coolant. F360 doesn't define a coolant for cutters 122 | 123 | cl0_coolantA_Mode: eCoolant.Off, // Enable issuing g-codes for control Coolant channel A 124 | cl1_coolantB_Mode: eCoolant.Off, // Use issuing g-codes for control Coolant channel B 125 | cl2_coolantAOn: "M42 P6 S255", // GCode command to turn on Coolant channel A 126 | cl3_coolantAOff: "M42 P6 S0", // Gcode command to turn off Coolant channel A 127 | cl4_coolantBOn: "M42 P11 S255", // GCode command to turn on Coolant channel B 128 | cl5_coolantBOff: "M42 P11 S0", // Gcode command to turn off Coolant channel B 129 | cl6_cust_coolantAOn: "", // Custom GCode command to turn on Coolant channel A 130 | cl7_cust_coolantAOff: "", // Custom Gcode command to turn off Coolant channel A 131 | cl8_cust_coolantBOn: "", // Custom GCode command to turn on Coolant channel B 132 | cl9_cust_coolantBOff: "", // Custom Gcode command to turn off Coolant channel B 133 | 134 | DuetMillingMode: "M453 P2 I0 R30000 F200", // GCode command to setup Duet3d milling mode 135 | DuetLaserMode: "M452 P2 I0 R255 F200", // GCode command to setup Duet3d laser mode 136 | 137 | }; 138 | 139 | propertyDefinitions = { 140 | 141 | job0_SelectedFirmware: { 142 | title: "Job: CNC Firmware", description: "Dialect of GCode to create", group: 1, 143 | type: "integer", default_mm: eFirmware.MARLIN, default_in: eFirmware.MARLIN, 144 | values: [ 145 | { title: eFirmware.prop[eFirmware.MARLIN].name, id: eFirmware.MARLIN }, 146 | { title: eFirmware.prop[eFirmware.GRBL].name, id: eFirmware.GRBL }, 147 | { title: eFirmware.prop[eFirmware.REPRAP].name, id: eFirmware.REPRAP }, 148 | ] 149 | }, 150 | 151 | job1_SetOriginOnStart: { 152 | title: "Job: Zero Starting Location (G92)", description: "On start set the current location as 0,0,0 (G92)", group: 1, 153 | type: "boolean", default_mm: true, default_in: true 154 | }, 155 | job2_ManualSpindlePowerControl: { 156 | title: "Job: Manual Spindle On/Off", description: "Enable to manually turn spindle motor on/off", group: 1, 157 | type: "boolean", default_mm: true, default_in: true 158 | }, 159 | job3_CommentLevel: { 160 | title: "Job: Comment Level", description: "Controls the comments include", group: 1, 161 | type: "integer", default_mm: eComment.Info, default_in: eComment.Info, 162 | values: [ 163 | { title: eComment.prop[eComment.Off].name, id: eComment.Off }, 164 | { title: eComment.prop[eComment.Important].name, id: eComment.Important }, 165 | { title: eComment.prop[eComment.Info].name, id: eComment.Info }, 166 | { title: eComment.prop[eComment.Debug].name, id: eComment.Debug }, 167 | ] 168 | }, 169 | job4_UseArcs: { 170 | title: "Job: Use Arcs", description: "Use G2/G3 g-codes fo circular movements", group: 1, 171 | type: "boolean", default_mm: true, default_in: true 172 | }, 173 | job5_SequenceNumbers: { 174 | title: "Job: Enable Line #s", description: "Show sequence numbers", group: 1, 175 | type: "boolean", default_mm: false, default_in: false 176 | }, 177 | job6_SequenceNumberStart: { 178 | title: "Job: First Line #", description: "First sequence number", group: 1, 179 | type: "integer", default_mm: 10, default_in: 10 180 | }, 181 | job7_SequenceNumberIncrement: { 182 | title: "Job: Line # Increment", description: "Sequence number increment", group: 1, 183 | type: "integer", default_mm: 1, default_in: 1 184 | }, 185 | job8_SeparateWordsWithSpace: { 186 | title: "Job: Include Whitespace", description: "Includes whitespace seperation between text", group: 1, 187 | type: "boolean", default_mm: true, default_in: true 188 | }, 189 | job9_GoOriginOnFinish: { 190 | title: "Job: At end go to 0,0", description: "Go to X0 Y0 at gcode end, Z remains unchanged", group: 1, 191 | type: "boolean", default_mm: true, default_in: true 192 | }, 193 | 194 | fr0_TravelSpeedXY: { 195 | title: "Feed: Travel speed X/Y", description: "High speed for Rapid movements X & Y (mm/min; in/min)", group: 2, 196 | type: "spatial", default_mm: 2500, default_in: 100 197 | }, 198 | fr1_TravelSpeedZ: { 199 | title: "Feed: Travel Speed Z", description: "High speed for Rapid movements z (mm/min; in/min)", group: 2, 200 | type: "spatial", default_mm: 300, default_in: 12 201 | }, 202 | fr2_EnforceFeedrate: { 203 | title: "Feed: Enforce Feedrate", description: "Add feedrate to each movement g-code", group: 2, 204 | type: "boolean", default_mm: true, default_in: true 205 | }, 206 | frA_ScaleFeedrate: { 207 | title: "Feed: Scale Feedrate", description: "Scale feedrate based on X, Y, Z axis maximums", group: 2, 208 | type: "boolean", default_mm: false, default_in: false 209 | }, 210 | frB_MaxCutSpeedXY: { 211 | title: "Feed: Max XY Cut Speed", description: "Maximum X or Y axis cut speed (mm/min; in/min)", group: 2, 212 | type: "spatial", default_mm: 900, default_in: 35.43 213 | }, 214 | frC_MaxCutSpeedZ: { 215 | title: "Feed: Max Z Cut Speed", description: "Maximum Z axis cut speed (mm/min; in/min)", group: 2, 216 | type: "spatial", default_mm: 180, default_in: 7.08 217 | }, 218 | frD_MaxCutSpeedXYZ: { 219 | title: "Feed: Max Toolpath Speed", description: "Maximum scaled feedrate for toolpath (mm/min; in/min)", group: 2, 220 | type: "spatial", default_mm: 1000, default_in: 39.37 221 | }, 222 | 223 | mapD_RestoreFirstRapids: { 224 | title: "Map: First G1 -> G0 Rapid", description: "Ensure move to start of a cut is with a G0 Rapid", group: 3, 225 | type: "boolean", default_mm: false, default_in: false 226 | }, 227 | mapE_RestoreRapids: { 228 | title: "Map: G1s -> G0 Rapids", description: "Enable to convert G1s to G0 Rapids when safe", group: 3, 229 | type: "boolean", default_mm: false, default_in: false 230 | }, 231 | mapF_SafeZ: { 232 | 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, 233 | type: "string", default_mm: "Retract:15", default_in: "Retract:15" 234 | }, 235 | mapG_AllowRapidZ: { 236 | title: "Map: Allow Rapid Z", description: "Enable to include vertical retracts and safe descents", group: 3, 237 | type: "boolean", default_mm: false, default_in: false 238 | }, 239 | 240 | toolChange0_Enabled: { 241 | title: "Tool Change: Enable", description: "Include tool change code when tool changes (bultin tool change requires LCD display)", group: 4, 242 | type: "boolean", default_mm: false, default_in: false 243 | }, 244 | toolChange1_X: { 245 | title: "Tool Change: X", description: "X location for tool change", group: 4, 246 | type: "spatial", default_mm: 0, default_in: 0 247 | }, 248 | toolChange2_Y: { 249 | title: "Tool Change: Y", description: "Y location for tool change", group: 4, 250 | type: "spatial", default_mm: 0, default_in: 0 251 | }, 252 | toolChange3_Z: { 253 | title: "Tool Change: Z ", description: "Z location for tool change", group: 4, 254 | type: "spatial", default_mm: 40, default_in: 1.6 255 | }, 256 | toolChange4_DisableZStepper: { 257 | title: "Tool Change: Disable Z stepper", description: "Disable Z stepper after reaching tool change location", group: 4, 258 | type: "boolean", default_mm: false, default_in: false 259 | }, 260 | 261 | probe1_OnStart: { 262 | title: "Probe: On job start", description: "Execute probe gcode on job start", group: 5, 263 | type: "boolean", default_mm: false, default_in: false 264 | }, 265 | probe2_OnToolChange: { 266 | title: "Probe: After Tool Change", description: "After tool change, probe Z at the current location", group: 5, 267 | type: "boolean", default_mm: false, default_in: false 268 | }, 269 | probe3_Thickness: { 270 | title: "Probe: Plate thickness", description: "Plate thickness", group: 5, 271 | type: "spatial", default_mm: 0.8, default_in: 0.032 272 | }, 273 | probe4_UseHomeZ: { 274 | title: "Probe: Use Home Z (G28)", description: "Probe with G28 (Yes) or G38 (No)", group: 5, 275 | type: "boolean", default_mm: true, default_in: true 276 | }, 277 | probe5_G38Target: { 278 | title: "Probe: G38 target", description: "G38 Probing's furthest Z position", group: 5, 279 | type: "spatial", default_mm: -10, default_in: -0.5 280 | }, 281 | probe6_G38Speed: { 282 | title: "Probe: G38 speed", description: "G38 Probing's speed (mm/min; in/min)", group: 5, 283 | type: "spatial", default_mm: 30, default_in: 1.2 284 | }, 285 | 286 | cutter1_OnVaporize: { 287 | title: "Laser: On - Vaporize", description: "Persent of power to turn on the laser/plasma cutter in vaporize mode", group: 6, 288 | type: "number", default_mm: 100, default_in: 100 289 | }, 290 | cutter2_OnThrough: { 291 | title: "Laser: On - Through", description: "Persent of power to turn on the laser/plasma cutter in through mode", group: 6, 292 | type: "number", default_mm: 80, default_in: 80 293 | }, 294 | cutter3_OnEtch: { 295 | title: "Laser: On - Etch", description: "Persent of power to on the laser/plasma cutter in etch mode", group: 6, 296 | type: "number", default_mm: 40, default_in: 40 297 | }, 298 | cutter4_MarlinMode: { 299 | title: "Laser: Marlin/Reprap Mode", description: "Marlin/Reprap mode of the laser/plasma cutter", group: 6, 300 | type: "integer", default_mm: 106, default_in: 106, 301 | values: [ 302 | { title: "Fan - M106 S{PWM}/M107", id: 106 }, 303 | { title: "Spindle - M3 O{PWM}/M5", id: 3 }, 304 | { title: "Pin - M42 P{pin} S{PWM}", id: 42 }, 305 | ] 306 | }, 307 | cutter5_MarlinPin: { 308 | title: "Laser: Marlin M42 Pin", description: "Marlin custom pin number for the laser/plasma cutter", group: 6, 309 | type: "integer", default_mm: 4, default_in: 4 310 | }, 311 | cutter6_GrblMode: { 312 | title: "Laser: GRBL Mode", description: "GRBL mode of the laser/plasma cutter", group: 6, 313 | type: "integer", default_mm: 4, default_in: 4, 314 | values: [ 315 | { title: "M4 S{PWM}/M5 dynamic power", id: 4 }, 316 | { title: "M3 S{PWM}/M5 static power", id: 3 }, 317 | ] 318 | }, 319 | cutter7_Coolant: { 320 | title: "Laser: Coolant", description: "Force a coolant to be used", group: 6, 321 | type: "integer", default_mm: eCoolant.Off, default_in: eCoolant.Off, 322 | values: [ 323 | { title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off }, 324 | { title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood }, 325 | { title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist }, 326 | { title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool }, 327 | { title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air }, 328 | { title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool }, 329 | { title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction }, 330 | { title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist }, 331 | { title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool } 332 | ] 333 | }, 334 | 335 | gcodeStartFile: { 336 | title: "Extern: Start File", description: "File with custom Gcode for header/start (in nc folder)", group: 7, 337 | type: "file", default_mm: "", default_in: "" 338 | }, 339 | gcodeStopFile: { 340 | title: "Extern: Stop File", description: "File with custom Gcode for footer/end (in nc folder)", group: 7, 341 | type: "file", default_mm: "", default_in: "" 342 | }, 343 | gcodeToolFile: { 344 | title: "Extern: Tool File", description: "File with custom Gcode for tool change (in nc folder)", group: 7, 345 | type: "file", default_mm: "", default_in: "" 346 | }, 347 | gcodeProbeFile: { 348 | title: "Extern: Probe File", description: "File with custom Gcode for tool probe (in nc folder)", group: 7, 349 | type: "file", default_mm: "", default_in: "" 350 | }, 351 | 352 | // Coolant 353 | cl0_coolantA_Mode: { 354 | title: "Coolant: A Mode", description: "Enable channel A when tool is set this coolant", group: 8, 355 | type: "integer", default_mm: 0, default_in: 0, 356 | values: [ 357 | { title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off }, 358 | { title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood }, 359 | { title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist }, 360 | { title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool }, 361 | { title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air }, 362 | { title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool }, 363 | { title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction }, 364 | { title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist }, 365 | { title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool } 366 | ] 367 | }, 368 | cl1_coolantB_Mode: { 369 | title: "Coolant: B Mode", description: "Enable channel B when tool is set this coolant", group: 8, 370 | type: "integer", default_mm: 0, default_in: 0, 371 | values: [ 372 | { title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off }, 373 | { title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood }, 374 | { title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist }, 375 | { title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool }, 376 | { title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air }, 377 | { title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool }, 378 | { title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction }, 379 | { title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist }, 380 | { title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool } 381 | ] 382 | }, 383 | cl2_coolantAOn: { 384 | title: "Coolant: A Enable", description: "GCode to turn On coolant channel A", group: 8, 385 | type: "enum", default_mm: "M42 P6 S255", default_in: "M42 P6 S255", 386 | values: [ 387 | { title: "Mrln: M42 P6 S255", id: "M42 P6 S255" }, 388 | { title: "Mrln: M42 P11 S255", id: "M42 P11 S255" }, 389 | { title: "Grbl: M7 (mist)", id: "M7" }, 390 | { title: "Grbl: M8 (flood)", id: "M8" }, 391 | { title: "Use custom", id: "Use custom" } 392 | ] 393 | }, 394 | cl3_coolantAOff: { 395 | title: "Coolant: A Disable", description: "Gcode to turn Off coolant A", group: 8, 396 | type: "enum", default_mm: "M42 P6 S0", default_in: "M42 P6 S0", 397 | values: [ 398 | { title: "Mrln: M42 P6 S0", id: "M42 P6 S0" }, 399 | { title: "Mrln: M42 P11 S0", id: "M42 P11 S0" }, 400 | { title: "Grbl: M9 (off)", id: "M9" }, 401 | { title: "Use custom", id: "Use custom" } 402 | ] 403 | }, 404 | cl4_coolantBOn: { 405 | title: "Coolant: B Enable", description: "GCode to turn On coolant channel B", group: 8, 406 | type: "enum", default_mm: "M42 P11 S255", default_in: "M42 P11 S255", 407 | values: [ 408 | { title: "Mrln: M42 P11 S255", id: "M42 P11 S255" }, 409 | { title: "Mrln: M42 P6 S255", id: "M42 P6 S255" }, 410 | { title: "Grbl: M7 (mist)", id: "M7" }, 411 | { title: "Grbl: M8 (flood)", id: "M8" }, 412 | { title: "Use custom", id: "Use custom" } 413 | ] 414 | }, 415 | cl5_coolantBOff: { 416 | title: "Coolant: B Disable", description: "Gcode to turn Off coolant B", group: 8, 417 | type: "enum", default_mm: "M42 P11 S0", default_in: "M42 P11 S0", 418 | values: [ 419 | { title: "Mrln: M42 P11 S0", id: "M42 P11 S0" }, 420 | { title: "Mrln: M42 P6 S0", id: "M42 P6 S0" }, 421 | { title: "Grbl: M9 (off)", id: "M9" }, 422 | { title: "Use custom", id: "Use custom" } 423 | ] 424 | }, 425 | cl6_cust_coolantAOn: { 426 | title: "Coolant: Custom A Enable", description: "Custom GCode to turn On coolant channel A", group: 8, 427 | type: "string", default_mm: "", default_in: "", 428 | }, 429 | cl7_cust_coolantAOff: { 430 | title: "Coolant: Custom A Disable", description: "Custom Gcode to turn Off coolant A", group: 8, 431 | type: "string", default_mm: "", default_in: "", 432 | }, 433 | cl8_cust_coolantBOn: { 434 | title: "Coolant: Custom B Enable", description: "Custom GCode to turn On coolant channel B", group: 8, 435 | type: "string", default_mm: "", default_in: "", 436 | }, 437 | cl9_cust_coolantBOff: { 438 | title: "Coolant: Custom B Disable", description: "Custom Gcode to turn Off coolant B", group: 8, 439 | type: "string", default_mm: "", default_in: "", 440 | }, 441 | 442 | DuetMillingMode: { 443 | title: "Duet: Milling mode", description: "GCode command to setup Duet3d milling mode", group: 9, 444 | type: "string", default_mm: "M453 P2 I0 R30000 F200", default_in: "M453 P2 I0 R30000 F200" 445 | }, 446 | DuetLaserMode: { 447 | title: "Duet: Laser mode", description: "GCode command to setup Duet3d laser mode", group: 9, 448 | type: "string", default_mm: "M452 P2 I0 R255 F200", default_in: "M452 P2 I0 R255 F200" 449 | }, 450 | }; 451 | 452 | var sequenceNumber; 453 | 454 | // Formats 455 | var gFormat = createFormat({ prefix: "G", decimals: 1 }); 456 | var mFormat = createFormat({ prefix: "M", decimals: 0 }); 457 | 458 | var xyzFormat = createFormat({ decimals: (unit == MM ? 3 : 4) }); 459 | var xFormat = createFormat({ prefix: "X", decimals: (unit == MM ? 3 : 4) }); 460 | var yFormat = createFormat({ prefix: "Y", decimals: (unit == MM ? 3 : 4) }); 461 | var zFormat = createFormat({ prefix: "Z", decimals: (unit == MM ? 3 : 4) }); 462 | var iFormat = createFormat({ prefix: "I", decimals: (unit == MM ? 3 : 4) }); 463 | var jFormat = createFormat({ prefix: "J", decimals: (unit == MM ? 3 : 4) }); 464 | var kFormat = createFormat({ prefix: "K", decimals: (unit == MM ? 3 : 4) }); 465 | 466 | var speedFormat = createFormat({ decimals: 0 }); 467 | var sFormat = createFormat({ prefix: "S", decimals: 0 }); 468 | 469 | var pFormat = createFormat({ prefix: "P", decimals: 0 }); 470 | var oFormat = createFormat({ prefix: "O", decimals: 0 }); 471 | 472 | var feedFormat = createFormat({ decimals: (unit == MM ? 0 : 2) }); 473 | var fFormat = createFormat({ prefix: "F", decimals: (unit == MM ? 0 : 2) }); 474 | 475 | var toolFormat = createFormat({ decimals: 0 }); 476 | var tFormat = createFormat({ prefix: "T", decimals: 0 }); 477 | 478 | var taperFormat = createFormat({ decimals: 1, scale: DEG }); 479 | var secFormat = createFormat({ decimals: 3, forceDecimal: true }); // seconds - range 0.001-1000 480 | 481 | // Linear outputs 482 | var xOutput = createVariable({}, xFormat); 483 | var yOutput = createVariable({}, yFormat); 484 | var zOutput = createVariable({}, zFormat); 485 | var fOutput = createVariable({ force: false }, fFormat); 486 | var sOutput = createVariable({ force: true }, sFormat); 487 | 488 | // Circular outputs 489 | var iOutput = createReferenceVariable({}, iFormat); 490 | var jOutput = createReferenceVariable({}, jFormat); 491 | var kOutput = createReferenceVariable({}, kFormat); 492 | 493 | // Modals 494 | var gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... 495 | var gPlaneModal = createModal({ onchange: function () { gMotionModal.reset(); } }, gFormat); // modal group 2 // G17-19 496 | var gAbsIncModal = createModal({}, gFormat); // modal group 3 // G90-91 497 | var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G93-94 498 | var gUnitModal = createModal({}, gFormat); // modal group 6 // G20-21 499 | 500 | // Arc support variables 501 | minimumChordLength = spatial(0.01, MM); 502 | minimumCircularRadius = spatial(0.01, MM); 503 | maximumCircularRadius = spatial(1000, MM); 504 | minimumCircularSweep = toRad(0.01); 505 | maximumCircularSweep = toRad(180); 506 | allowHelicalMoves = false; 507 | allowedCircularPlanes = undefined; 508 | 509 | // Writes the specified block. 510 | function writeBlock() { 511 | if (properties.job5_SequenceNumbers) { 512 | writeWords2("N" + sequenceNumber, arguments); 513 | sequenceNumber += properties.job7_SequenceNumberIncrement; 514 | } else { 515 | writeWords(arguments); 516 | } 517 | } 518 | 519 | function flushMotions() { 520 | if (fw == eFirmware.GRBL) { 521 | } 522 | 523 | // Default 524 | else { 525 | writeBlock(mFormat.format(400)); 526 | } 527 | } 528 | 529 | //---------------- Safe Rapids ---------------- 530 | 531 | var eSafeZ = { 532 | CONST: 0, 533 | FEED: 1, 534 | RETRACT: 2, 535 | CLEARANCE: 3, 536 | ERROR: 4, 537 | prop: { 538 | 0: {name: "Const", regex: /^\d+\.?\d*$/, numRegEx: /^(\d+\.?\d*)$/, value: 0}, 539 | 1: {name: "Feed", regex: /^Feed:/i, numRegEx: /:(\d+\.?\d*)$/, value: 1}, 540 | 2: {name: "Retract", regex: /^Retract:/i, numRegEx: /:(\d+\.?\d*)$/, alue: 2}, 541 | 3: {name: "Clearance", regex: /^Clearance:/i, numRegEx: /:(\d+\.?\d*)$/, value: 3}, 542 | 4: {name: "Error", regex: /^$/, numRegEx: /^$/, value: 4} 543 | } 544 | }; 545 | 546 | var safeZMode = eSafeZ.CONST; 547 | var safeZHeightDefault = 15; 548 | var safeZHeight; 549 | 550 | function parseSafeZProperty() { 551 | var str = properties.mapF_SafeZ; 552 | 553 | // Look for either a number by itself or 'Feed:', 'Retract:' or 'Clearance:' 554 | for (safeZMode = eSafeZ.CONST; safeZMode < eSafeZ.ERROR; safeZMode++) { 555 | if (str.search(eSafeZ.prop[safeZMode].regex) == 0) { 556 | break; 557 | } 558 | } 559 | 560 | // If it was not an error then get the number 561 | if (safeZMode != eSafeZ.ERROR) { 562 | safeZHeightDefault = str.match(eSafeZ.prop[safeZMode].numRegEx); 563 | 564 | if ((safeZHeightDefault == null) || (safeZHeightDefault.length !=2)) { 565 | writeComment(eComment.Debug, " parseSafeZProperty: " + safeZHeightDefault); 566 | writeComment(eComment.Debug, " parseSafeZProperty.length: " + (safeZHeightDefault != null? safeZHeightDefault.length : "na")); 567 | writeComment(eComment.Debug, " parseSafeZProperty: Couldn't find number"); 568 | safeZMode = eSafeZ.ERROR; 569 | safeZHeightDefault = 15; 570 | } 571 | else { 572 | safeZHeightDefault = safeZHeightDefault[1]; 573 | } 574 | } 575 | 576 | writeComment(eComment.Debug, " parseSafeZProperty: safeZMode = '" + eSafeZ.prop[safeZMode].name + "'"); 577 | writeComment(eComment.Debug, " parseSafeZProperty: safeZHeightDefault = " + safeZHeightDefault); 578 | } 579 | 580 | function safeZforSection(_section) 581 | { 582 | if (properties.mapE_RestoreRapids) { 583 | switch (safeZMode) { 584 | case eSafeZ.CONST: 585 | safeZHeight = safeZHeightDefault; 586 | writeComment(eComment.Important, " SafeZ using const: " + safeZHeight); 587 | break; 588 | 589 | case eSafeZ.FEED: 590 | if (hasParameter("operation:feedHeight_value") && hasParameter("operation:feedHeight_absolute")) { 591 | let feed = _section.getParameter("operation:feedHeight_value"); 592 | let abs = _section.getParameter("operation:feedHeight_absolute"); 593 | 594 | if (abs == 1) { 595 | safeZHeight = feed; 596 | writeComment(eComment.Info, " SafeZ feed level: " + safeZHeight); 597 | } 598 | else { 599 | safeZHeight = safeZHeightDefault; 600 | writeComment(eComment.Important, " SafeZ feed level not abs: " + safeZHeight); 601 | } 602 | } 603 | else { 604 | safeZHeight = safeZHeightDefault; 605 | writeComment(eComment.Important, " SafeZ feed level not defined: " + safeZHeight); 606 | } 607 | break; 608 | 609 | case eSafeZ.RETRACT: 610 | if (hasParameter("operation:retractHeight_value") && hasParameter("operation:retractHeight_absolute")) { 611 | let retract = _section.getParameter("operation:retractHeight_value"); 612 | let abs = _section.getParameter("operation:retractHeight_absolute"); 613 | 614 | if (abs == 1) { 615 | safeZHeight = retract; 616 | writeComment(eComment.Info, " SafeZ retract level: " + safeZHeight); 617 | } 618 | else { 619 | safeZHeight = safeZHeightDefault; 620 | writeComment(eComment.Important, " SafeZ retract level not abs: " + safeZHeight); 621 | } 622 | } 623 | else { 624 | safeZHeight = safeZHeightDefault; 625 | writeComment(eComment.Important, " SafeZ: retract level not defined: " + safeZHeight); 626 | } 627 | break; 628 | 629 | case eSafeZ.CLEARANCE: 630 | if (hasParameter("operation:clearanceHeight_value") && hasParameter("operation:clearanceHeight_absolute")) { 631 | var clearance = _section.getParameter("operation:clearanceHeight_value"); 632 | let abs = _section.getParameter("operation:clearanceHeight_absolute"); 633 | 634 | if (abs == 1) { 635 | safeZHeight = clearance; 636 | writeComment(eComment.Info, " SafeZ clearance level: " + safeZHeight); 637 | } 638 | else { 639 | safeZHeight = safeZHeightDefault; 640 | writeComment(eComment.Important, " SafeZ clearance level not abs: " + safeZHeight); 641 | } 642 | } 643 | else { 644 | safeZHeight = safeZHeightDefault; 645 | writeComment(eComment.Important, " SafeZ clearance level not defined: " + safeZHeight); 646 | } 647 | break; 648 | 649 | case eSafeZ.ERROR: 650 | safeZHeight = safeZHeightDefault; 651 | writeComment(eComment.Important, " >>> WARNING: " + propertyDefinitions.mapF_SafeZ.title + "format error: " + safeZHeight); 652 | break; 653 | } 654 | } 655 | } 656 | 657 | 658 | Number.prototype.round = function(places) { 659 | return +(Math.round(this + "e+" + places) + "e-" + places); 660 | } 661 | 662 | // Returns true if the rules to convert G1s to G0s are satisfied 663 | function isSafeToRapid(x, y, z) { 664 | if (properties.mapE_RestoreRapids) { 665 | 666 | // Calculat a z to 3 decimal places for zSafe comparison, every where else use z to avoid mixing rounded with unrounded 667 | var z_round = z.round(3); 668 | writeComment(eComment.Debug, "isSafeToRapid z: " + z + " z_round: " + z_round); 669 | 670 | let zSafe = (z_round >= safeZHeight); 671 | 672 | writeComment(eComment.Debug, "isSafeToRapid zSafe: " + zSafe + " z_round: " + z_round + " safeZHeight: " + safeZHeight); 673 | 674 | // Destination z must be in safe zone. 675 | if (zSafe) { 676 | let cur = getCurrentPosition(); 677 | let zConstant = (z == cur.z); 678 | let zUp = (z > cur.z); 679 | let xyConstant = ((x == cur.x) && (y == cur.y)); 680 | let curZSafe = (cur.z >= safeZHeight); 681 | writeComment(eComment.Debug, "isSafeToRapid curZSafe: " + curZSafe + " cur.z: " + cur.z); 682 | 683 | // Restore Rapids only when the target Z is safe and 684 | // Case 1: Z is not changing, but XY are 685 | // Case 2: Z is increasing, but XY constant 686 | 687 | // Z is not changing and we know we are in the safe zone 688 | if (zConstant) { 689 | return true; 690 | } 691 | 692 | // We include moves of Z up as long as xy are constant 693 | else if (properties.mapG_AllowRapidZ && zUp && xyConstant) { 694 | return true; 695 | } 696 | 697 | // We include moves of Z down as long as xy are constant and z always remains safe 698 | else if (properties.mapG_AllowRapidZ && (!zUp) && xyConstant && curZSafe) { 699 | return true; 700 | } 701 | } 702 | } 703 | 704 | return false; 705 | } 706 | 707 | //---------------- Coolant ---------------- 708 | 709 | function CoolantA(on) { 710 | var coolantText = on ? properties.cl2_coolantAOn : properties.cl3_coolantAOff; 711 | 712 | if (coolantText == "Use custom") { 713 | coolantText = on ? properties.cl6_cust_coolantAOn : properties.cl7_cust_coolantAOff; 714 | } 715 | 716 | writeBlock(coolantText); 717 | } 718 | 719 | function CoolantB(on) { 720 | var coolantText = on ? properties.cl4_coolantBOn : properties.cl5_coolantBOff; 721 | 722 | if (coolantText == "Use custom") { 723 | coolantText = on ? properties.cl8_cust_coolantBOn : properties.cl9_cust_coolantBOff; 724 | } 725 | 726 | writeBlock(coolantText); 727 | } 728 | 729 | // Manage two channels of coolant by tracking which coolant is being using for 730 | // a channel (0 = disabled). SetCoolant called with desired coolant to use or 0 to disable 731 | 732 | var curCoolant = eCoolant.Off; // The coolant requested by the tool 733 | var coolantChannelA = eCoolant.Off; // The coolant running in ChannelA 734 | var coolantChannelB = eCoolant.Off; // The coolant running in ChannelB 735 | 736 | function setCoolant(coolant) { 737 | writeComment(eComment.Debug, " ---- Coolant: " + coolant + " cur: " + curCoolant + " A: " + coolantChannelA + " B: " + coolantChannelB); 738 | 739 | // If the coolant for this tool is the same as the current coolant then there is nothing to do 740 | if (curCoolant == coolant) { 741 | return; 742 | } 743 | 744 | // We are changing coolant, so disable any active coolant channels 745 | // before we switch to the other coolant 746 | if (coolantChannelA != eCoolant.Off) { 747 | writeComment((coolant == eCoolant.Off) ? eComment.Important: eComment.Info, " >>> Coolant Channel A: " + eCoolant.prop[eCoolant.Off].name); 748 | coolantChannelA = eCoolant.Off; 749 | CoolantA(false); 750 | } 751 | 752 | if (coolantChannelB != eCoolant.Off) { 753 | writeComment((coolant == eCoolant.Off) ? eComment.Important: eComment.Info, " >>> Coolant Channel B: " + eCoolant.prop[eCoolant.Off].name); 754 | coolantChannelB = eCoolant.Off; 755 | CoolantB(false); 756 | } 757 | 758 | // At this point we know that all coolant is off so make that the current coolant 759 | curCoolant = eCoolant.Off; 760 | 761 | // As long as we are not disabling coolant (coolant = 0), then check if either coolant channel 762 | // matches the coolant requested. If neither do then issue an warning 763 | 764 | var warn = true; 765 | 766 | if (coolant != eCoolant.Off) { 767 | if (properties.cl0_coolantA_Mode == coolant) { 768 | writeComment(eComment.Important, " >>> Coolant Channel A: " + eCoolant.prop[coolant].name); 769 | coolantChannelA = coolant; 770 | curCoolant = coolant; 771 | warn = false; 772 | CoolantA(true); 773 | } 774 | 775 | if (properties.cl1_coolantB_Mode == coolant) { 776 | writeComment(eComment.Important, " >>> Coolant Channel B: " + eCoolant.prop[coolant].name); 777 | coolantChannelB = coolant; 778 | curCoolant = coolant; 779 | warn = false; 780 | CoolantB(true); 781 | } 782 | 783 | if (warn) { 784 | writeComment(eComment.Important, " >>> WARNING: No matching Coolant channel : " + ((coolant <= eCoolant.FloodThroughTool) ? eCoolant.prop[coolant].name : "unknown") + " requested"); 785 | } 786 | } 787 | } 788 | 789 | //---------------- Cutters - Waterjet/Laser/Plasma ---------------- 790 | 791 | var cutterOnCurrentPower; 792 | 793 | function laserOn(power) { 794 | // Firmware is Grbl 795 | if (fw == eFirmware.GRBL) { 796 | var laser_pwm = power * 10; 797 | 798 | writeBlock(mFormat.format(properties.cutter6_GrblMode), sFormat.format(laser_pwm)); 799 | } 800 | 801 | // Default firmware 802 | else { 803 | var laser_pwm = power / 100 * 255; 804 | 805 | switch (properties.cutter4_MarlinMode) { 806 | case 106: 807 | writeBlock(mFormat.format(106), sFormat.format(laser_pwm)); 808 | break; 809 | case 3: 810 | if (fw == eFirmware.REPRAP) { 811 | writeBlock(mFormat.format(3), sFormat.format(laser_pwm)); 812 | } else { 813 | writeBlock(mFormat.format(3), oFormat.format(laser_pwm)); 814 | } 815 | break; 816 | case 42: 817 | writeBlock(mFormat.format(42), pFormat.format(properties.cutter5_MarlinPin), sFormat.format(laser_pwm)); 818 | break; 819 | } 820 | } 821 | } 822 | 823 | function laserOff() { 824 | // Firmware is Grbl 825 | if (fw == eFirmware.GRBL) { 826 | writeBlock(mFormat.format(5)); 827 | } 828 | 829 | // Default 830 | else { 831 | switch (properties.cutter4_MarlinMode) { 832 | case 106: 833 | writeBlock(mFormat.format(107)); 834 | break; 835 | case 3: 836 | writeBlock(mFormat.format(5)); 837 | break; 838 | case 42: 839 | writeBlock(mFormat.format(42), pFormat.format(properties.cutter5_MarlinPin), sFormat.format(0)); 840 | break; 841 | } 842 | } 843 | } 844 | 845 | //---------------- on Entry Points ---------------- 846 | 847 | // Called in every new gcode file 848 | function onOpen() { 849 | fw = properties.job0_SelectedFirmware; 850 | 851 | // Output anything special to start the GCode 852 | if (fw == eFirmware.GRBL) { 853 | writeln("%"); 854 | } 855 | 856 | // Configure the GCode G commands 857 | if (fw == eFirmware.GRBL) { 858 | gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... 859 | } 860 | else { 861 | gMotionModal = createModal({ force: true }, gFormat); // modal group 1 // G0-G3, ... 862 | } 863 | 864 | // Configure how the feedrate is formatted 865 | if (properties.fr2_EnforceFeedrate) { 866 | fOutput = createVariable({ force: true }, fFormat); 867 | } 868 | 869 | // Set the starting sequence number for line numbering 870 | sequenceNumber = properties.job6_SequenceNumberStart; 871 | 872 | // Set the seperator used between text 873 | if (!properties.job8_SeparateWordsWithSpace) { 874 | setWordSeparator(""); 875 | } 876 | 877 | // Determine the safeZHeight to do rapids 878 | parseSafeZProperty(); 879 | } 880 | 881 | // Called at end of gcode file 882 | function onClose() { 883 | writeComment(eComment.Important, " *** STOP begin ***"); 884 | 885 | flushMotions(); 886 | 887 | if (properties.gcodeStopFile == "") { 888 | onCommand(COMMAND_COOLANT_OFF); 889 | if (properties.job9_GoOriginOnFinish) { 890 | rapidMovementsXY(0, 0); 891 | } 892 | onCommand(COMMAND_STOP_SPINDLE); 893 | 894 | end(true); 895 | 896 | writeComment(eComment.Important, " *** STOP end ***"); 897 | } else { 898 | loadFile(properties.gcodeStopFile); 899 | } 900 | 901 | if (fw == eFirmware.GRBL) { 902 | writeln("%"); 903 | } 904 | } 905 | 906 | var forceSectionToStartWithRapid = false; 907 | 908 | function onSection() { 909 | // Every section needs to start with a Rapid to get to the initial location. 910 | // In the hobby version Rapids have been elliminated and the first command is 911 | // a onLinear not a onRapid command. This results in not current position being 912 | // that same as the cut to position which means wecan't determine the direction 913 | // of the move. Without a direction vector we can't scale the feedrate or convert 914 | // onLinear moves back into onRapids. By ensuring the first onLinear is treated as 915 | // a onRapid we have a currentPosition that is correct. 916 | 917 | forceSectionToStartWithRapid = true; 918 | 919 | // Write Start gcode of the documment (after the "onParameters" with the global info) 920 | if (isFirstSection()) { 921 | writeFirstSection(); 922 | } 923 | 924 | writeComment(eComment.Important, " *** SECTION begin ***"); 925 | 926 | // Print min/max boundaries for each section 927 | vectorX = new Vector(1, 0, 0); 928 | vectorY = new Vector(0, 1, 0); 929 | writeComment(eComment.Info, " X Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMinimum()) + " - X Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMaximum())); 930 | writeComment(eComment.Info, " Y Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMinimum()) + " - Y Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMaximum())); 931 | writeComment(eComment.Info, " Z Min: " + xyzFormat.format(currentSection.getGlobalZRange().getMinimum()) + " - Z Max: " + xyzFormat.format(currentSection.getGlobalZRange().getMaximum())); 932 | 933 | // Determine the Safe Z Height to map G1s to G0s 934 | safeZforSection(currentSection); 935 | 936 | // Do a tool change if tool changes are enabled and its not the first section and this section uses 937 | // a different tool then the previous section 938 | if (properties.toolChange0_Enabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) { 939 | if (properties.gcodeToolFile == "") { 940 | // Post Processor does the tool change 941 | 942 | writeComment(eComment.Important, " --- Tool Change Start") 943 | toolChange(); 944 | writeComment(eComment.Important, " --- Tool Change End") 945 | } else { 946 | // Users custom tool change gcode is used 947 | loadFile(properties.gcodeToolFile); 948 | } 949 | } 950 | 951 | // Machining type 952 | if (currentSection.type == TYPE_MILLING) { 953 | // Specific milling code 954 | writeComment(eComment.Info, " " + sectionComment + " - Milling - Tool: " + tool.number + " - " + tool.comment + " " + getToolTypeName(tool.type)); 955 | } 956 | 957 | else if (currentSection.type == TYPE_JET) { 958 | var jetModeStr; 959 | var warn = false; 960 | 961 | // Cutter mode used for different cutting power in PWM laser 962 | switch (currentSection.jetMode) { 963 | case JET_MODE_THROUGH: 964 | cutterOnCurrentPower = properties.cutter2_OnThrough; 965 | jetModeStr = "Through" 966 | break; 967 | case JET_MODE_ETCHING: 968 | cutterOnCurrentPower = properties.cutter3_OnEtch; 969 | jetModeStr = "Etching" 970 | break; 971 | case JET_MODE_VAPORIZE: 972 | jetModeStr = "Vaporize" 973 | cutterOnCurrentPower = properties.cutter1_OnVaporize; 974 | break; 975 | default: 976 | jetModeStr = "*** Unknown ***" 977 | warn = true; 978 | } 979 | 980 | if (warn) { 981 | writeComment(eComment.Info, " " + sectionComment + ", Laser/Plasma Cutting mode: " + getParameter("operation:cuttingMode") + ", jetMode: " + jetModeStr); 982 | writeComment(eComment.Important, "Selected cutting mode " + currentSection.jetMode + " not mapped to power level"); 983 | } 984 | else { 985 | writeComment(eComment.Info, " " + sectionComment + ", Laser/Plasma Cutting mode: " + getParameter("operation:cuttingMode") + ", jetMode: " + jetModeStr + ", power: " + cutterOnCurrentPower); 986 | } 987 | } 988 | 989 | // Adjust the mode 990 | if (fw == eFirmware.REPRAP) { 991 | if (machineMode != currentSection.type) { 992 | switch (currentSection.type) { 993 | case TYPE_MILLING: 994 | writeBlock(properties.DuetMillingMode); 995 | break; 996 | case TYPE_JET: 997 | writeBlock(properties.DuetLaserMode); 998 | break; 999 | } 1000 | } 1001 | } 1002 | 1003 | machineMode = currentSection.type; 1004 | 1005 | onCommand(COMMAND_START_SPINDLE); 1006 | onCommand(COMMAND_COOLANT_ON); 1007 | 1008 | // Display section name in LCD 1009 | display_text(" " + sectionComment); 1010 | } 1011 | 1012 | // Called in every section end 1013 | function onSectionEnd() { 1014 | resetAll(); 1015 | writeComment(eComment.Important, " *** SECTION end ***"); 1016 | writeComment(eComment.Important, ""); 1017 | } 1018 | 1019 | function onComment(message) { 1020 | writeComment(eComment.Important, message); 1021 | } 1022 | 1023 | var pendingRadiusCompensation = RADIUS_COMPENSATION_OFF; 1024 | 1025 | function onRadiusCompensation() { 1026 | pendingRadiusCompensation = radiusCompensation; 1027 | } 1028 | 1029 | // Rapid movements 1030 | function onRapid(x, y, z) { 1031 | forceSectionToStartWithRapid = false; 1032 | 1033 | rapidMovements(x, y, z); 1034 | } 1035 | 1036 | // Feed movements 1037 | function onLinear(x, y, z, feed) { 1038 | // If we are allowing Rapids to be recovered from Linear (cut) moves, which is 1039 | // only required when F360 Personal edition is used, then if this Linear (cut) 1040 | // move is the first operationin a Section (milling operation) then convert it 1041 | // to a Rapid. This is OK because Sections normally begin with a Rapid to move 1042 | // to the first cutting location but these Rapids were changed to Linears by 1043 | // the personal edition. If this Rapid is not recovered and feedrate scaling 1044 | // is enabled then the first move to the start of a section will be at the 1045 | // slowest cutting feedrate, generally Z's feedrate. 1046 | 1047 | if (properties.mapD_RestoreFirstRapids && (forceSectionToStartWithRapid == true)) { 1048 | writeComment(eComment.Important, " First G1 --> G0"); 1049 | 1050 | forceSectionToStartWithRapid = false; 1051 | onRapid(x, y, z); 1052 | } 1053 | else if (isSafeToRapid(x, y, z)) { 1054 | writeComment(eComment.Important, " Safe G1 --> G0"); 1055 | 1056 | onRapid(x, y, z); 1057 | } 1058 | else { 1059 | linearMovements(x, y, z, feed, true); 1060 | } 1061 | } 1062 | 1063 | function onRapid5D(_x, _y, _z, _a, _b, _c) { 1064 | forceSectionToStartWithRapid = false; 1065 | 1066 | error(localize("Multi-axis motion is not supported.")); 1067 | } 1068 | 1069 | function onLinear5D(_x, _y, _z, _a, _b, _c, feed) { 1070 | forceSectionToStartWithRapid = false; 1071 | 1072 | error(localize("Multi-axis motion is not supported.")); 1073 | } 1074 | 1075 | function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { 1076 | forceSectionToStartWithRapid = false; 1077 | 1078 | if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { 1079 | error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); 1080 | return; 1081 | } 1082 | circular(clockwise, cx, cy, cz, x, y, z, feed) 1083 | } 1084 | 1085 | // Called on waterjet/plasma/laser cuts 1086 | var powerState = false; 1087 | 1088 | function onPower(power) { 1089 | if (power != powerState) { 1090 | if (power) { 1091 | writeComment(eComment.Important, " >>> LASER Power ON"); 1092 | 1093 | laserOn(cutterOnCurrentPower); 1094 | } else { 1095 | writeComment(eComment.Important, " >>> LASER Power OFF"); 1096 | 1097 | laserOff(); 1098 | } 1099 | powerState = power; 1100 | } 1101 | } 1102 | 1103 | // Called on Dwell Manual NC invocation 1104 | function onDwell(seconds) { 1105 | writeComment(eComment.Important, " >>> Dwell"); 1106 | if (seconds > 99999.999) { 1107 | warning(localize("Dwelling time is out of range.")); 1108 | } 1109 | 1110 | seconds = clamp(0.001, seconds, 99999.999); 1111 | 1112 | // Firmware is Grbl 1113 | if (fw == eFirmware.GRBL) { 1114 | writeBlock(gFormat.format(4), "P" + secFormat.format(seconds)); 1115 | } 1116 | 1117 | // Default 1118 | else { 1119 | writeBlock(gFormat.format(4), "S" + secFormat.format(seconds)); 1120 | } 1121 | } 1122 | 1123 | // Called with every parameter in the documment/section 1124 | function onParameter(name, value) { 1125 | 1126 | // Write gcode initial info 1127 | // Product version 1128 | if (name == "generated-by") { 1129 | writeComment(eComment.Important, value); 1130 | writeComment(eComment.Important, " Posts processor: " + FileSystem.getFilename(getConfigurationPath())); 1131 | } 1132 | 1133 | // Date 1134 | else if (name == "generated-at") { 1135 | writeComment(eComment.Important, " Gcode generated: " + value + " GMT"); 1136 | } 1137 | 1138 | // Document 1139 | else if (name == "document-path") { 1140 | writeComment(eComment.Important, " Document: " + value); 1141 | } 1142 | 1143 | // Setup 1144 | else if (name == "job-description") { 1145 | writeComment(eComment.Important, " Setup: " + value); 1146 | } 1147 | 1148 | // Get section comment 1149 | else if (name == "operation-comment") { 1150 | sectionComment = value; 1151 | } 1152 | 1153 | else { 1154 | writeComment(eComment.Debug, " param: " + name + " = " + value); 1155 | } 1156 | } 1157 | 1158 | function onMovement(movement) { 1159 | var jet = tool.isJetTool && tool.isJetTool(); 1160 | var id; 1161 | 1162 | switch (movement) { 1163 | case MOVEMENT_RAPID: 1164 | id = "MOVEMENT_RAPID"; 1165 | break; 1166 | case MOVEMENT_LEAD_IN: 1167 | id = "MOVEMENT_LEAD_IN"; 1168 | break; 1169 | case MOVEMENT_CUTTING: 1170 | id = "MOVEMENT_CUTTING"; 1171 | break; 1172 | case MOVEMENT_LEAD_OUT: 1173 | id = "MOVEMENT_LEAD_OUT"; 1174 | break; 1175 | case MOVEMENT_LINK_TRANSITION: 1176 | id = jet ? "MOVEMENT_BRIDGING" : "MOVEMENT_LINK_TRANSITION"; 1177 | break; 1178 | case MOVEMENT_LINK_DIRECT: 1179 | id = "MOVEMENT_LINK_DIRECT"; 1180 | break; 1181 | case MOVEMENT_RAMP_HELIX: 1182 | id = jet ? "MOVEMENT_PIERCE_CIRCULAR" : "MOVEMENT_RAMP_HELIX"; 1183 | break; 1184 | case MOVEMENT_RAMP_PROFILE: 1185 | id = jet ? "MOVEMENT_PIERCE_PROFILE" : "MOVEMENT_RAMP_PROFILE"; 1186 | break; 1187 | case MOVEMENT_RAMP_ZIG_ZAG: 1188 | id = jet ? "MOVEMENT_PIERCE_LINEAR" : "MOVEMENT_RAMP_ZIG_ZAG"; 1189 | break; 1190 | case MOVEMENT_RAMP: 1191 | id = "MOVEMENT_RAMP"; 1192 | break; 1193 | case MOVEMENT_PLUNGE: 1194 | id = jet ? "MOVEMENT_PIERCE" : "MOVEMENT_PLUNGE"; 1195 | break; 1196 | case MOVEMENT_PREDRILL: 1197 | id = "MOVEMENT_PREDRILL"; 1198 | break; 1199 | case MOVEMENT_EXTENDED: 1200 | id = "MOVEMENT_EXTENDED"; 1201 | break; 1202 | case MOVEMENT_REDUCED: 1203 | id = "MOVEMENT_REDUCED"; 1204 | break; 1205 | case MOVEMENT_HIGH_FEED: 1206 | id = "MOVEMENT_HIGH_FEED"; 1207 | break; 1208 | case MOVEMENT_FINISH_CUTTING: 1209 | id = "MOVEMENT_FINISH_CUTTING"; 1210 | break; 1211 | } 1212 | 1213 | if (id == undefined) { 1214 | id = String(movement); 1215 | } 1216 | 1217 | writeComment(eComment.Info, " " + id); 1218 | } 1219 | 1220 | var currentSpindleSpeed = 0; 1221 | 1222 | function setSpindeSpeed(_spindleSpeed, _clockwise) { 1223 | if (currentSpindleSpeed != _spindleSpeed) { 1224 | if (_spindleSpeed > 0) { 1225 | spindleOn(_spindleSpeed, _clockwise); 1226 | } else { 1227 | spindleOff(); 1228 | } 1229 | currentSpindleSpeed = _spindleSpeed; 1230 | } 1231 | } 1232 | 1233 | function onSpindleSpeed(spindleSpeed) { 1234 | setSpindeSpeed(spindleSpeed, tool.clockwise); 1235 | } 1236 | 1237 | function onCommand(command) { 1238 | writeComment(eComment.Info, " " + getCommandStringId(command)); 1239 | 1240 | switch (command) { 1241 | case COMMAND_START_SPINDLE: 1242 | onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); 1243 | return; 1244 | case COMMAND_SPINDLE_CLOCKWISE: 1245 | if (!tool.isJetTool()) { 1246 | setSpindeSpeed(spindleSpeed, true); 1247 | } 1248 | return; 1249 | case COMMAND_SPINDLE_COUNTERCLOCKWISE: 1250 | if (!tool.isJetTool()) { 1251 | setSpindeSpeed(spindleSpeed, false); 1252 | } 1253 | return; 1254 | case COMMAND_STOP_SPINDLE: 1255 | if (!tool.isJetTool()) { 1256 | setSpindeSpeed(0, true); 1257 | } 1258 | return; 1259 | case COMMAND_COOLANT_ON: 1260 | if (tool.isJetTool()) { 1261 | // F360 doesn't support coolant with jet tools (water jet/laser/plasma) but we've 1262 | // added a parameter to force a coolant to be selected for jet tool operations. Note: tool.coolant 1263 | // is not used as F360 doesn't define it. 1264 | 1265 | if (properties.cutter7_Coolant != eCoolant.Off) { 1266 | setCoolant(properties.cutter7_Coolant); 1267 | } 1268 | } 1269 | else { 1270 | setCoolant(tool.coolant); 1271 | } 1272 | return; 1273 | case COMMAND_COOLANT_OFF: 1274 | setCoolant(eCoolant.Off); //COOLANT_DISABLED 1275 | return; 1276 | case COMMAND_LOCK_MULTI_AXIS: 1277 | return; 1278 | case COMMAND_UNLOCK_MULTI_AXIS: 1279 | return; 1280 | case COMMAND_BREAK_CONTROL: 1281 | return; 1282 | case COMMAND_TOOL_MEASURE: 1283 | if (!tool.isJetTool()) { 1284 | probeTool(); 1285 | } 1286 | return; 1287 | case COMMAND_STOP: 1288 | writeBlock(mFormat.format(0)); 1289 | return; 1290 | } 1291 | } 1292 | 1293 | function resetAll() { 1294 | xOutput.reset(); 1295 | yOutput.reset(); 1296 | zOutput.reset(); 1297 | fOutput.reset(); 1298 | } 1299 | 1300 | function writeInformation() { 1301 | // Calcualte the min/max ranges across all sections 1302 | var toolZRanges = {}; 1303 | var vectorX = new Vector(1, 0, 0); 1304 | var vectorY = new Vector(0, 1, 0); 1305 | var ranges = { 1306 | x: { min: undefined, max: undefined }, 1307 | y: { min: undefined, max: undefined }, 1308 | z: { min: undefined, max: undefined }, 1309 | }; 1310 | var handleMinMax = function (pair, range) { 1311 | var rmin = range.getMinimum(); 1312 | var rmax = range.getMaximum(); 1313 | if (pair.min == undefined || pair.min > rmin) { 1314 | pair.min = rmin; 1315 | } 1316 | if (pair.max == undefined || pair.max < rmin) { // was pair.min - changed by DG 1/4/2021 1317 | pair.max = rmax; 1318 | } 1319 | } 1320 | 1321 | var numberOfSections = getNumberOfSections(); 1322 | for (var i = 0; i < numberOfSections; ++i) { 1323 | var section = getSection(i); 1324 | var tool = section.getTool(); 1325 | var zRange = section.getGlobalZRange(); 1326 | var xRange = section.getGlobalRange(vectorX); 1327 | var yRange = section.getGlobalRange(vectorY); 1328 | handleMinMax(ranges.x, xRange); 1329 | handleMinMax(ranges.y, yRange); 1330 | handleMinMax(ranges.z, zRange); 1331 | if (is3D()) { 1332 | if (toolZRanges[tool.number]) { 1333 | toolZRanges[tool.number].expandToRange(zRange); 1334 | } else { 1335 | toolZRanges[tool.number] = zRange; 1336 | } 1337 | } 1338 | } 1339 | 1340 | // Display the Range Table 1341 | writeComment(eComment.Info, " "); 1342 | writeComment(eComment.Info, " Ranges Table:"); 1343 | 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)); 1344 | 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)); 1345 | 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)); 1346 | 1347 | // Display the Tools Table 1348 | writeComment(eComment.Info, " "); 1349 | writeComment(eComment.Info, " Tools Table:"); 1350 | var tools = getToolTable(); 1351 | if (tools.getNumberOfTools() > 0) { 1352 | for (var i = 0; i < tools.getNumberOfTools(); ++i) { 1353 | var tool = tools.getTool(i); 1354 | var comment = " T" + toolFormat.format(tool.number) + " D=" + xyzFormat.format(tool.diameter) + " CR=" + xyzFormat.format(tool.cornerRadius); 1355 | if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) { 1356 | comment += " TAPER=" + taperFormat.format(tool.taperAngle) + "deg"; 1357 | } 1358 | if (toolZRanges[tool.number]) { 1359 | comment += " - ZMIN=" + xyzFormat.format(toolZRanges[tool.number].getMinimum()); 1360 | } 1361 | comment += " - " + getToolTypeName(tool.type) + " " + tool.comment; 1362 | writeComment(eComment.Info, comment); 1363 | } 1364 | } 1365 | 1366 | // Display the Feedrate and Scaling Properties 1367 | writeComment(eComment.Info, " "); 1368 | writeComment(eComment.Info, " Feedrate and Scaling Properties:"); 1369 | writeComment(eComment.Info, " Feed: Travel speed X/Y = " + properties.fr0_TravelSpeedXY); 1370 | writeComment(eComment.Info, " Feed: Travel Speed Z = " + properties.fr1_TravelSpeedZ); 1371 | writeComment(eComment.Info, " Feed: Enforce Feedrate = " + properties.fr2_EnforceFeedrate); 1372 | writeComment(eComment.Info, " Feed: Scale Feedrate = " + properties.frA_ScaleFeedrate); 1373 | writeComment(eComment.Info, " Feed: Max XY Cut Speed = " + properties.frB_MaxCutSpeedXY); 1374 | writeComment(eComment.Info, " Feed: Max Z Cut Speed = " + properties.frC_MaxCutSpeedZ); 1375 | writeComment(eComment.Info, " Feed: Max Toolpath Speed = " + properties.frD_MaxCutSpeedXYZ); 1376 | 1377 | // Display the G1->G0 Mapping Properties 1378 | writeComment(eComment.Info, " "); 1379 | writeComment(eComment.Info, " G1->G0 Mapping Properties:"); 1380 | writeComment(eComment.Info, " Map: First G1 -> G0 Rapid = " + properties.mapD_RestoreFirstRapids); 1381 | writeComment(eComment.Info, " Map: G1s -> G0 Rapids = " + properties.mapE_RestoreRapids); 1382 | writeComment(eComment.Info, " Map: SafeZ Mode = " + eSafeZ.prop[safeZMode].name + " : default = " + safeZHeightDefault); 1383 | writeComment(eComment.Info, " Map: Allow Rapid Z = " + properties.mapG_AllowRapidZ); 1384 | 1385 | writeComment(eComment.Info, " "); 1386 | } 1387 | 1388 | function writeFirstSection() { 1389 | // Write out the information block at the beginning of the file 1390 | writeInformation(); 1391 | 1392 | writeComment(eComment.Important, " *** START begin ***"); 1393 | 1394 | if (properties.gcodeStartFile == "") { 1395 | Start(); 1396 | } else { 1397 | loadFile(properties.gcodeStartFile); 1398 | } 1399 | 1400 | writeComment(eComment.Important, " *** START end ***"); 1401 | writeComment(eComment.Important, " "); 1402 | } 1403 | 1404 | // Output a comment 1405 | function writeComment(level, text) { 1406 | if (level <= properties.job3_CommentLevel) { 1407 | if (fw == eFirmware.GRBL) { 1408 | writeln("(" + String(text).replace(/[\(\)]/g, "") + ")"); 1409 | } 1410 | else { 1411 | writeln(";" + String(text).replace(/[\(\)]/g, "")); 1412 | } 1413 | } 1414 | } 1415 | 1416 | // Rapid movements with G1 and differentiated travel speeds for XY 1417 | // Changes F360 current XY. 1418 | // No longer called for general Rapid only for probing, homing, etc. 1419 | function rapidMovementsXY(_x, _y) { 1420 | let x = xOutput.format(_x); 1421 | let y = yOutput.format(_y); 1422 | 1423 | if (x || y) { 1424 | if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { 1425 | error(localize("Radius compensation mode cannot be changed at rapid traversal.")); 1426 | } 1427 | else { 1428 | let f = fOutput.format(propertyMmToUnit(properties.fr0_TravelSpeedXY)); 1429 | writeBlock(gMotionModal.format(0), x, y, f); 1430 | } 1431 | } 1432 | } 1433 | 1434 | // Rapid movements with G1 and differentiated travel speeds for Z 1435 | // Changes F360 current Z 1436 | // No longer called for general Rapid only for probing, homing, etc. 1437 | function rapidMovementsZ(_z) { 1438 | let z = zOutput.format(_z); 1439 | 1440 | if (z) { 1441 | if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { 1442 | error(localize("Radius compensation mode cannot be changed at rapid traversal.")); 1443 | } 1444 | else { 1445 | let f = fOutput.format(propertyMmToUnit(properties.fr1_TravelSpeedZ)); 1446 | writeBlock(gMotionModal.format(0), z, f); 1447 | } 1448 | } 1449 | } 1450 | 1451 | // Rapid movements with G1 uses the max travel rate (xy or z) and then relies on feedrate scaling 1452 | function rapidMovements(_x, _y, _z) { 1453 | 1454 | rapidMovementsZ(_z); 1455 | rapidMovementsXY(_x, _y); 1456 | } 1457 | 1458 | // Calculate the feedX, feedY and feedZ components 1459 | 1460 | function limitFeedByXYZComponents(curPos, destPos, feed) { 1461 | if (!properties.frA_ScaleFeedrate) 1462 | return feed; 1463 | 1464 | var xyz = Vector.diff(destPos, curPos); // Translate the cut so curPos is at 0,0,0 1465 | var dir = xyz.getNormalized(); // Normalize vector to get a direction vector 1466 | var xyzFeed = Vector.product(dir.abs, feed); // Determine the effective x,y,z speed on each axis 1467 | 1468 | // Get the max speed for each axis 1469 | let xyLimit = propertyMmToUnit(properties.frB_MaxCutSpeedXY); 1470 | let zLimit = propertyMmToUnit(properties.frC_MaxCutSpeedZ); 1471 | 1472 | // Normally F360 begins a Section (a milling operation) with a Rapid to move to the beginning of the cut. 1473 | // Rapids use the defined Travel speed and the Post Processor does not depend on the current location. 1474 | // This function must know the current location in order to calculate the actual vector traveled. Without 1475 | // the first Rapid the current location is the same as the desination location, which creates a 0 length 1476 | // vector. A zero length vector is unusable and so a instead the slowest of the xyLimit or zLimit is used. 1477 | // 1478 | // Note: if Map: G1 -> Rapid is enabled in the Properties then if the first operation in a Section is a 1479 | // cut (which it should always be) then it will be converted to a Rapid. This prevents ever getting a zero 1480 | // length vector. 1481 | if (xyz.length == 0) { 1482 | var lesserFeed = (xyLimit < zLimit) ? xyLimit : zLimit; 1483 | 1484 | return lesserFeed; 1485 | } 1486 | 1487 | // Force the speed of each axis to be within limits 1488 | if (xyzFeed.z > zLimit) { 1489 | xyzFeed.multiply(zLimit / xyzFeed.z); 1490 | } 1491 | 1492 | if (xyzFeed.x > xyLimit) { 1493 | xyzFeed.multiply(xyLimit / xyzFeed.x); 1494 | } 1495 | 1496 | if (xyzFeed.y > xyLimit) { 1497 | xyzFeed.multiply(xyLimit / xyzFeed.y); 1498 | } 1499 | 1500 | // Calculate the new feedrate based on the speed allowed on each axis: feedrate = sqrt(x^2 + y^2 + z^2) 1501 | // xyzFeed.length is the same as Math.sqrt((xyzFeed.x * xyzFeed.x) + (xyzFeed.y * xyzFeed.y) + (xyzFeed.z * xyzFeed.z)) 1502 | 1503 | // Limit the new feedrate by the maximum allowable cut speed 1504 | 1505 | let xyzLimit = propertyMmToUnit(properties.frD_MaxCutSpeedXYZ); 1506 | let newFeed = (xyzFeed.length > xyzLimit) ? xyzLimit : xyzFeed.length; 1507 | 1508 | if (Math.abs(newFeed - feed) > 0.01) { 1509 | return newFeed; 1510 | } 1511 | else { 1512 | return feed; 1513 | } 1514 | } 1515 | 1516 | // Linear movements 1517 | function linearMovements(_x, _y, _z, _feed) { 1518 | if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { 1519 | // ensure that we end at desired position when compensation is turned off 1520 | xOutput.reset(); 1521 | yOutput.reset(); 1522 | } 1523 | 1524 | // Force the feedrate to be scaled (if enabled). The feedrate is projected into the 1525 | // x, y, and z axis and each axis is tested to see if it exceeds its defined max. If 1526 | // it does then the speed in all 3 axis is scaled proportionately. The resulting feedrate 1527 | // is then capped at the maximum defined cutrate. 1528 | 1529 | let feed = limitFeedByXYZComponents(getCurrentPosition(), new Vector(_x, _y, _z), _feed); 1530 | 1531 | let x = xOutput.format(_x); 1532 | let y = yOutput.format(_y); 1533 | let z = zOutput.format(_z); 1534 | let f = fOutput.format(feed); 1535 | 1536 | if (x || y || z) { 1537 | if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) { 1538 | error(localize("Radius compensation mode is not supported.")); 1539 | } else { 1540 | writeBlock(gMotionModal.format(1), x, y, z, f); 1541 | } 1542 | } else if (f) { 1543 | if (getNextRecord().isMotion()) { // try not to output feed without motion 1544 | fOutput.reset(); // force feed on next line 1545 | } else { 1546 | writeBlock(gMotionModal.format(1), f); 1547 | } 1548 | } 1549 | } 1550 | 1551 | // Test if file exist/can read and load it 1552 | function loadFile(_file) { 1553 | var folder = FileSystem.getFolderPath(getOutputPath()) + PATH_SEPARATOR; 1554 | if (FileSystem.isFile(folder + _file)) { 1555 | var txt = loadText(folder + _file, "utf-8"); 1556 | if (txt.length > 0) { 1557 | writeComment(eComment.Info, " --- Start custom gcode " + folder + _file); 1558 | write(txt); 1559 | writeComment("eComment.Info, --- End custom gcode " + folder + _file); 1560 | } 1561 | } else { 1562 | writeComment(eComment.Important, " Can't open file " + folder + _file); 1563 | error("Can't open file " + folder + _file); 1564 | } 1565 | } 1566 | 1567 | function propertyMmToUnit(_v) { 1568 | return (_v / (unit == IN ? 25.4 : 1)); 1569 | } 1570 | 1571 | /* 1572 | function mergeProperties(to, from) { 1573 | for (var attrname in from) { 1574 | to[attrname] = from[attrname]; 1575 | } 1576 | } 1577 | 1578 | function Firmware3dPrinterLike() { 1579 | FirmwareBase.apply(this, arguments); 1580 | this.spindleEnabled = false; 1581 | } 1582 | 1583 | Firmware3dPrinterLike.prototype = Object.create(FirmwareBase.prototype); 1584 | Firmware3dPrinterLike.prototype.constructor = Firmware3dPrinterLike; 1585 | */ 1586 | 1587 | function Start() { 1588 | // Is Grbl? 1589 | if (fw == eFirmware.GRBL) { 1590 | writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning 1591 | writeBlock(gFeedModeModal.format(94)); 1592 | writeBlock(gPlaneModal.format(17)); 1593 | writeBlock(gUnitModal.format(unit == IN ? 20 : 21)); 1594 | } 1595 | 1596 | // Default 1597 | else { 1598 | writeComment(eComment.Info, " Set Absolute Positioning"); 1599 | writeComment(eComment.Info, " Units = " + (unit == IN ? "inch" : "mm")); 1600 | writeComment(eComment.Info, " Disable stepper timeout"); 1601 | if (properties.job1_SetOriginOnStart) { 1602 | writeComment(eComment.Info, " Set current position to 0,0,0"); 1603 | } 1604 | 1605 | writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning 1606 | writeBlock(gUnitModal.format(unit == IN ? 20 : 21)); // Set the units 1607 | writeBlock(mFormat.format(84), sFormat.format(0)); // Disable steppers timeout 1608 | 1609 | if (properties.job1_SetOriginOnStart) { 1610 | writeBlock(gFormat.format(92), xFormat.format(0), yFormat.format(0), zFormat.format(0)); // Set origin to initial position 1611 | } 1612 | 1613 | if (properties.probe1_OnStart && tool.number != 0 && !tool.isJetTool()) { 1614 | onCommand(COMMAND_TOOL_MEASURE); 1615 | } 1616 | } 1617 | } 1618 | 1619 | function end() { 1620 | // Is Grbl? 1621 | if (fw == eFirmware.GRBL) { 1622 | writeBlock(mFormat.format(30)); 1623 | } 1624 | 1625 | // Default 1626 | else { 1627 | display_text("Job end"); 1628 | } 1629 | } 1630 | 1631 | function spindleOn(_spindleSpeed, _clockwise) { 1632 | // Is Grbl? 1633 | if (fw == eFirmware.GRBL) { 1634 | writeComment(eComment.Important, " >>> Spindle Speed " + speedFormat.format(_spindleSpeed)); 1635 | writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed)); 1636 | } 1637 | 1638 | // Default 1639 | else { 1640 | if (properties.job2_ManualSpindlePowerControl) { 1641 | // For manual any positive input speed assumed as enabled. so it's just a flag 1642 | if (!this.spindleEnabled) { 1643 | writeComment(eComment.Important, " >>> Spindle Speed: Manual"); 1644 | askUser("Turn ON " + speedFormat.format(_spindleSpeed) + "RPM", "Spindle", false); 1645 | } 1646 | } else { 1647 | writeComment(eComment.Important, " >>> Spindle Speed " + speedFormat.format(_spindleSpeed)); 1648 | writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed)); 1649 | } 1650 | this.spindleEnabled = true; 1651 | } 1652 | } 1653 | 1654 | function spindleOff() { 1655 | // Is Grbl? 1656 | if (fw == eFirmware.GRBL) { 1657 | writeBlock(mFormat.format(5)); 1658 | } 1659 | 1660 | //Default 1661 | else { 1662 | if (properties.job2_ManualSpindlePowerControl) { 1663 | writeBlock(mFormat.format(300), sFormat.format(300), pFormat.format(3000)); 1664 | askUser("Turn OFF spindle", "Spindle", false); 1665 | } else { 1666 | writeBlock(mFormat.format(5)); 1667 | } 1668 | this.spindleEnabled = false; 1669 | } 1670 | } 1671 | 1672 | function display_text(txt) { 1673 | // Firmware is Grbl 1674 | if (fw == eFirmware.GRBL) { 1675 | // Don't display text 1676 | } 1677 | 1678 | // Default 1679 | else { 1680 | writeBlock(mFormat.format(117), (properties.job8_SeparateWordsWithSpace ? "" : " ") + txt); 1681 | } 1682 | } 1683 | 1684 | function circular(clockwise, cx, cy, cz, x, y, z, feed) { 1685 | if (!properties.job4_UseArcs) { 1686 | linearize(tolerance); 1687 | return; 1688 | } 1689 | 1690 | var start = getCurrentPosition(); 1691 | 1692 | // Firmware is Grbl 1693 | if (fw == eFirmware.GRBL) { 1694 | if (isFullCircle()) { 1695 | if (isHelical()) { 1696 | linearize(tolerance); 1697 | return; 1698 | } 1699 | switch (getCircularPlane()) { 1700 | case PLANE_XY: 1701 | 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)); 1702 | break; 1703 | case PLANE_ZX: 1704 | 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)); 1705 | break; 1706 | case PLANE_YZ: 1707 | 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)); 1708 | break; 1709 | default: 1710 | linearize(tolerance); 1711 | } 1712 | } else { 1713 | switch (getCircularPlane()) { 1714 | case PLANE_XY: 1715 | 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)); 1716 | break; 1717 | case PLANE_ZX: 1718 | 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)); 1719 | break; 1720 | case PLANE_YZ: 1721 | 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)); 1722 | break; 1723 | default: 1724 | linearize(tolerance); 1725 | } 1726 | } 1727 | } 1728 | 1729 | // Default 1730 | else { 1731 | // Marlin supports arcs only on XY plane 1732 | if (isFullCircle()) { 1733 | if (isHelical()) { 1734 | linearize(tolerance); 1735 | return; 1736 | } 1737 | switch (getCircularPlane()) { 1738 | case PLANE_XY: 1739 | writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed)); 1740 | break; 1741 | default: 1742 | linearize(tolerance); 1743 | } 1744 | } else { 1745 | switch (getCircularPlane()) { 1746 | case PLANE_XY: 1747 | 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)); 1748 | break; 1749 | default: 1750 | linearize(tolerance); 1751 | } 1752 | } 1753 | } 1754 | } 1755 | 1756 | function askUser(text, title, allowJog) { 1757 | // Firmware is RepRap? 1758 | if (fw == eFirmware.REPRAP) { 1759 | var v1 = " P\"" + text + "\" R\"" + title + "\" S3"; 1760 | var v2 = allowJog ? " X1 Y1 Z1" : ""; 1761 | writeBlock(mFormat.format(291), (properties.job8_SeparateWordsWithSpace ? "" : " ") + v1 + v2); 1762 | } 1763 | 1764 | // Default 1765 | else { 1766 | writeBlock(mFormat.format(0), (properties.job8_SeparateWordsWithSpace ? "" : " ") + text); 1767 | } 1768 | } 1769 | 1770 | function toolChange() { 1771 | // Grbl tool change? 1772 | if (fw == eFirmware.GRBL) { 1773 | 1774 | writeBlock(mFormat.format(6), tFormat.format(tool.number)); 1775 | writeBlock(gFormat.format(54)); 1776 | } 1777 | 1778 | // Default tool change 1779 | else 1780 | { 1781 | flushMotions(); 1782 | 1783 | // Go to tool change position 1784 | onRapid(propertyMmToUnit(properties.toolChange1_X), propertyMmToUnit(properties.toolChange2_Y), propertyMmToUnit(properties.toolChange3_Z)); 1785 | 1786 | flushMotions(); 1787 | 1788 | // turn off spindle and coolant 1789 | onCommand(COMMAND_COOLANT_OFF); 1790 | onCommand(COMMAND_STOP_SPINDLE); 1791 | if (!properties.job2_ManualSpindlePowerControl) { 1792 | // Beep 1793 | writeBlock(mFormat.format(300), sFormat.format(400), pFormat.format(2000)); 1794 | } 1795 | 1796 | // Disable Z stepper 1797 | if (properties.toolChange4_DisableZStepper) { 1798 | askUser("Z Stepper will disabled. Wait for STOP!!", "Tool change", false); 1799 | writeBlock(mFormat.format(17), 'Z'); // Disable steppers timeout 1800 | } 1801 | // Ask tool change and wait user to touch lcd button 1802 | askUser("Tool " + tool.number + " " + tool.comment, "Tool change", true); 1803 | 1804 | // Run Z probe gcode 1805 | if (properties.probe2_OnToolChange && tool.number != 0) { 1806 | onCommand(COMMAND_TOOL_MEASURE); 1807 | } 1808 | } 1809 | } 1810 | 1811 | function probeTool() { 1812 | // Is Grbl? 1813 | if (fw == eFirmware.GRBL) { 1814 | writeComment(eComment.Important, " >>> WARNING: No probing implemented for GRBL"); 1815 | } 1816 | 1817 | // Default 1818 | else { 1819 | writeComment(eComment.Important, " Probe to Zero Z"); 1820 | writeComment(eComment.Info, " Ask User to Attach the Z Probe"); 1821 | writeComment(eComment.Info, " Do Probing"); 1822 | writeComment(eComment.Info, " Set Z to probe thickness: " + zFormat.format(propertyMmToUnit(properties.probe3_Thickness))) 1823 | if (properties.toolChange3_Z != "") { 1824 | writeComment(eComment.Info, " Retract the tool to " + propertyMmToUnit(properties.toolChange3_Z)); 1825 | } 1826 | writeComment(eComment.Info, " Ask User to Remove the Z Probe"); 1827 | 1828 | askUser("Attach ZProbe", "Probe", false); 1829 | // refer http://marlinfw.org/docs/gcode/G038.html 1830 | if (properties.probe4_UseHomeZ) { 1831 | writeBlock(gFormat.format(28), 'Z'); 1832 | } else { 1833 | writeBlock(gMotionModal.format(38.3), fFormat.format(propertyMmToUnit(properties.probe6_G38Speed)), zFormat.format(propertyMmToUnit(properties.probe5_G38Target))); 1834 | } 1835 | 1836 | let z = zFormat.format(propertyMmToUnit(properties.probe3_Thickness)); 1837 | writeBlock(gFormat.format(92), z); // Set origin to initial position 1838 | 1839 | resetAll(); 1840 | if (properties.toolChange3_Z != "") { // move up tool to safe height again after probing 1841 | rapidMovementsZ(propertyMmToUnit(properties.toolChange3_Z), false); 1842 | } 1843 | 1844 | flushMotions(); 1845 | 1846 | askUser("Detach ZProbe", "Probe", false); 1847 | } 1848 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Fusion 360 CAM posts processor for MPCNC 3 | ==== 4 | 5 | This is modified fork of https://github.com/guffy1234/mpcnc_posts_processor that was originally forked https://github.com/martindb/mpcnc_posts_processor. 6 | 7 | CAM posts processor for use with Fusion 360 and [MPCNC](https://www.v1engineering.com). 8 | 9 | Supported firmware: 10 | - Marlin 2.0 11 | - Repetier firmware 1.0.3 (not tested. gcode is same as for Marlin) 12 | - GRBL 1.1 13 | - RepRap firmware (Duet3d) 14 | 15 | Installation: 16 | - The post processor consists of a single file, mpcnc.cps. 17 | - 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 18 | - The desired post processor can be selected during a post using the Setup button and selecting Use Personal Post Library 19 | - Use the Job: CNC Firmware property to select between Marlin 2.x, Grbl 1.1 and RepRap firmware 20 | 21 | Some design points: 22 | - Setup operation types: Milling, Water/Laser/Plasma 23 | - Support mm and Inches units (**but all properties MUST be set in MM**) 24 | - 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. 25 | - Arcs support on XY plane (Marlin/Repetier/RepRap) or all panes (Grbl) 26 | - Tested with LCD display and SD card (built in tool change require printing from SD and LCD to restart) 27 | - Support for 3 different laser power using "cutting modes" (through, etch, vaporize) 28 | - Support 2 coolant channels. You may attach relays to control external devices - as example air jet valve. 29 | - Customizable level of verbosity of comments 30 | - Support line numbers 31 | - 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)**) 32 | 33 | ![screenshot](/screenshot.jpg "screenshot") 34 | 35 | # Properties 36 | 37 | > 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). 38 | > 39 | >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. 40 | > 41 | >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. 42 | 43 | ## Group 1: Job Properties 44 | Use these properties to control overall aspects of the job. 45 | 46 | |Title|Description|Default| 47 | |---|---|---| 48 | Job: CNC Firmware|Dialect of GCode to create|**Marlin 2.x**| 49 | Job: Job: Zero Starting Location (G92)|On start set the current location as 0,0,0 (G92).|**true**| 50 | 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**| 51 | Job: Comment Level|Controls a increasing level of comments to be included: Off, Important, Info, Debug|**Info**| 52 | Job: Use Arcs|Use G2/G3 g-codes for circular movements.|**true**| 53 | Job: Enable Line #s|Show sequence numbers.|**false**| 54 | Job: First Line #|First sequence number.|**1**| 55 | Job: Line # Increment|Sequence number increment.|**10**| 56 | Job: Include Whitespace|Includes whitespace seperation between text.|**true**| 57 | Job: At end go to 0,0|Go to X0 Y0 at gcode end, Z remains unchanged.|**true**| 58 | 59 | ## Group 2: Travel Speed and Feedrate Scaling Properties 60 | Use these properties to set the speed used for G0 Rapids and to scale the feedrate used 61 | for G1 cuts. 62 | 63 | [Feed: Travel Speed X/Y] and [Feed: Travel Speed Z] are always used for G0 Rapids. 64 | 65 | Scaling of the G1 cut feedrates will only occur if [Feed:Scaled Feedrate] is true. 66 | 67 | Scaling ensures that no G1 cut exceeds the speed capablities of the X, Y, or Z axes. 68 | The cut's toolpath feedrate is projected onto the X, Y and Z axes. In turn each axis is tested 69 | to see if its cut speed is within the limits of that axis. If not, then all axes feedrates are 70 | scaled proportionatly to bring it within limits. This is repeated for all axes. The three axis 71 | feedrates are then merged to create a new toolpath feedrate which is then limited to ensure it 72 | doesn't exceed [Feed: Max Toolpath Speed]. 73 | 74 | Note: Because scaling considered 3 dimensional movement a resulting toolpath's feedrate may be 75 | greater then one or all of the X, Y or Z limits. For example, a small movement in Z compared to 76 | a much larger movement in XY may result in a feedrate that appears to exceed the capability of 77 | Z but in reality since Z is moving a much smaller distance for the same time period its actual 78 | feedrate is within the established limits. 79 | 80 | |Title|Description|Default| 81 | |---|---|---| 82 | Feed: Travel Speed X/Y|High speed for travel movements X & Y (mm/min).|**2500 mm/min**| 83 | Feed: Travel Speed Z|High speed for travel movements Z (mm/min).|**300 mm/min**| 84 | Feed: Enforce Feedrate|Forces the Fxxx to be include even if hasn't changed, useful for Marlin.|**true**| 85 | Feed: Scaled Feedrate|Scale feedrate based on X, Y, Z axis maximums.|**false**| 86 | Feed: Max Cut Speed X or Y|Maximum X or Y axis cut speed (mm/min).|**900 mm/min**| 87 | Feed: Max Cut Speed Z|Maximum Z axis cut speed (mm/min).|**180 mm/min**| 88 | Feed: Max Toolpath Speed|Maximum scaled feedrate for toolpath (mm/min).|**1000 mm/min**| 89 | 90 | ## Group 3: Map G1->G0 Properties 91 | 92 | Allows G1 cuts to be converted to G0 Rapid movements in specific cases: 93 | 94 | If [Map: First G1 -> G0 Rapid] is true the post processor resolves the lost 95 | initial positioning movement at the beginning of a cut toolpath. This problem is often 96 | identified in forums as the tool being initially dragged across the work surface. 97 | 98 | If [Map: G1s -> G0s] is true then allows G1 XY cut movements (i.e. no change in Z) that occur 99 | at a height greater or equal to [Map: Safe Z to Rapid] to be converted to G0 Rapids. 100 | Note: this assumes that any Z above [Map: Safe Z to Rapid] is a movement in the air and clear of 101 | obstacles. Can be defined as a number or one of F360's planes (Feed, Retract or Clearance). 102 | 103 | Map: Safe Z for Rapids may be defined as: 104 | * As a constant numeric value - safe Z will then always be this value for all sections, or 105 | * 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. 106 | 107 | If [Map: Allow Rapid Z] is true then G1 Z cut movements that either move straight up 108 | and end above [Map: Safe Z to Rapid], or straight down with the start and end positions both 109 | above [Map: Safe Z to Rapid] are included. Only occurs if [Map: G1s -> G0s] is also true. 110 | 111 | |Title|Description|Default|Format| 112 | |---|---|---|---| 113 | Map: First G1 -> G0 Rapid|Converts the first G1 of a cut to G0 Rapid|**false**| | 114 | Map: G1s -> G0s|Allow G1 cuts to be converted to Rapid G0 moves when safe and appropriate.|**false**| | 115 | 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| 116 | Map: Allow Rapid Z|Include the mapping of vertical cuts if they are safe.|**false**| 117 | 118 | ## Group 4: Tool change Properties 119 | 120 | |Title|Description|Default| 121 | |---|---|---| 122 | Tool Change: Enable|Include tool change code when tool changes (bultin tool change requires LCD display|**false**| 123 | Tool Change: X|X position for built-in tool change|**0**| 124 | Tool Change: Y|Y position for built-in tool change|**0**| 125 | Tool Change: Z|Z position for built-in tool change|**40**| 126 | Tool Change: Disable Z stepper|Disable Z stepper after reaching tool change location|**false**| 127 | 128 | ## Group 5: Z Probe Properties 129 | 130 | |Title|Description|Default| 131 | |---|---|---| 132 | Probe: On job start|Execute probe gcode on job start|**false**| 133 | Probe: After Tool Change|Z probe after tool change|**false**| 134 | Probe: Plate thickness|Plate thickness|**0.8**| 135 | Probe: Use Home Z (G28)|Probe with G28 (Yes) or G38 (No)|**true**| 136 | Probe: G38 target|G38 Probing's furthest Z position|**-10**| 137 | Probe: G38 speed|G38 Probing's speed|**30**| 138 | 139 | ## Group 6: Laser/Plasma Properties 140 | 141 | Fusion 360 defines four levels of Through cut, currently these all map to power level "On - Through". 142 | 143 | The firmware selected in the parameter [Job: CNC Firmware] determines if the Grbl or Marlin/Reprap laser parameters are used. 144 | 145 | 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). 146 | 147 | |Title|Description|Default|Values| 148 | |---|---|---|---| 149 | Laser: On - Vaporize|Persent of power to turn on the laser/plasma cutter in vaporize mode|**100**|| 150 | Laser: On - Through|Persent of power to turn on the laser/plasma cutter in through mode|**80**|| 151 | Laser: On - Etch|Persent of power to turn on the laser/plasma cutter in etch mode|**40**|| 152 | 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}"| 153 | Laser: Marlin M42 Pin|Marlin custom pin number for the laser/plasma cutter|**4**|| 154 | 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"| 155 | Laser: Coolant|Force a coolant to be used|**Off**|off, flood, mist, throughTool, air, airThroughTool, suction, floodMist, floodThroughTool| 156 | 157 | ## Group 7: Override Behaviour by External File Properties 158 | 159 | |Title|Description|Default| 160 | |---|---|---| 161 | Extern: Start File|File with custom Gcode for header/start (in nc folder)|| 162 | Extern: Stop File|File with custom Gcode for footer/end (in nc folder)|| 163 | Extern: Tool File|File with custom Gcode for tool change (in nc folder)|| 164 | Extern: Probe File|File with custom Gcode for tool probe (in nc folder)|| 165 | 166 | ## Group 7: Coolant Control Pin Properties 167 | 168 | 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. 169 | 170 | 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. 171 | 172 | 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. 173 | 174 | For coolant requests, like "Flood and Mist" or "Flood and Through Tool" you may want to enable one or 175 | 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). 176 | 177 | Four custom coolant text strings can be defined for both Channel A and B's on and off values. Use these if the predefine values do not match your hardware. To enable, set the corresponding coolant channel to 'Use custom'. 178 | 179 | |Title|Description|Default|Values| 180 | |---|---|---|---| 181 | Coolant: A Mode|Enable channel A when tool is set this coolant|**off**|off, flood, mist, throughTool, air, airThroughTool, suction, floodMist, floodThroughTool| 182 | Coolant: B Mode|Enable channel B when tool is set this coolant|**off**|off, flood, mist, throughTool, air, airThroughTool, suction, floodMist, floodThroughTool| 183 | 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)", "Use custom"| 184 | Coolant: A Disable|GCode to turn Off coolant channel A|**Mrln: M42 P6 S0**|"Mrln: M42 P6 S0", "Mrln: M42 P11 S0", "Grbl: M9 (off)", "Use custom"| 185 | 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)", "Use custom"| 186 | Coolant: B Disable|GCode to turn Off coolant channel B|**Mrln: M42 P11 S0**|"Mrln: M42 P11 S0", "Mrln: M42 P6 S0", "Grbl: M9 (off)", "Use custom"| 187 | Coolant: Custom A Enable|Custom GCode to turn On coolant channel A|empty| | 188 | Coolant: Custom A Disable|Custom GCode to turn Off coolant channel A|empty| | 189 | Coolant: Custom B Enable|Custom GCode to turn On coolant channel B|empty| | 190 | Coolant: Custom B Disable|Custom GCode to turn Off coolant channel B|empty| | 191 | 192 | ## Group 9: Duet Properties 193 | 194 | |Title|Description|Default| 195 | |---|---|---| 196 | Duet: Milling mode|GCode command to setup Duet3d milling mode|**M453 P2 I0 R30000 F200**| 197 | Duet: Laser mode|GCode command to setup Duet3d laser mode|**M452 P2 I0 R255 F200**| 198 | 199 | # Sample of issued code blocks 200 | 201 | ## Gcode of milling with manually control spindle 202 | 203 | ```G-code 204 | To be updated 205 | ``` 206 | 207 | # Resources 208 | 209 | [Marlin G-codes](http://marlinfw.org/meta/gcode/) 210 | 211 | [PostProcessor Class Reference](https://cam.autodesk.com/posts/reference/classPostProcessor.html) 212 | 213 | [Post Processor Training Guide (PDF document)](https://cam.autodesk.com/posts/posts/guides/Post%20Processor%20Training%20Guide.pdf) 214 | 215 | [Enhancements to the post processor property definitions](https://forums.autodesk.com/t5/hsm-post-processor-forum/enhancements-to-the-post-processor-property-definitions/td-p/7325350) 216 | 217 | [Dumper PostProcessor](https://cam.autodesk.com/hsmposts?p=dump) 218 | 219 | [Library of exist post processors](https://cam.autodesk.com/hsmposts) 220 | 221 | [Post processors forum](https://forums.autodesk.com/t5/hsm-post-processor-forum/bd-p/218) 222 | 223 | [How to set up a 4/5 axis machine configuration](https://forums.autodesk.com/t5/hsm-post-processor-forum/how-to-set-up-a-4-5-axis-machine-configuration/td-p/6488176) 224 | 225 | [Beginners Guide to Editing Post Processors in Fusion 360! FF121 (Youtube video)](https://www.youtube.com/watch?v=5EodQIY25tU) 226 | -------------------------------------------------------------------------------- /Test/1001_laser.gcode: -------------------------------------------------------------------------------- 1 | ;Fusion 360 CAM 2.0.4860 2 | ; Posts processor: MPCNC_Mill_Laser.cps 3 | ; Gcode generated: Sunday, December 2, 2018 2:07:32 PM GMT 4 | ; Document: cam_testpp v5 5 | ; Setup: Setup2 6 | ; 7 | ; Ranges table: 8 | ; X: Min=-25 Max=25 Size=50 9 | ; Y: Min=-25.5 Max=25 Size=50.5 10 | ; Z: Min=0 Max=15 Size=15 11 | ; 12 | ; Tools table: 13 | ; T1 D=0 CR=0 - ZMIN=0 - laser cutter 14 | 15 | ; *** START begin *** 16 | G90 17 | G21 18 | M84 S0 19 | G92 X0 Y0 Z0 20 | ; COMMAND_TOOL_MEASURE 21 | ; *** START end *** 22 | 23 | ; *** SECTION begin *** 24 | ;2D Profile1 - Laser/Plasma - Cutting mode: auto 25 | ; X Min: -25 - X Max: 25 26 | ; Y Min: -25.5 - Y Max: 25 27 | ; Z Min: 0 - Z Max: 15 28 | M400 29 | ; COMMAND_START_SPINDLE 30 | ; COMMAND_SPINDLE_COUNTERCLOCKWISE 31 | ; COMMAND_COOLANT_ON 32 | M117 2D Profile1 33 | G0 Z15 F300 34 | G0 X-9.94 Y-10.5 F2500 35 | G0 Z0 F300 36 | ; >>> LASER Power ON 37 | M106 S200 38 | ; COMMAND_POWER_ON 39 | ; MOVEMENT_LEAD_IN 40 | G1 Y-10 F1000 41 | G1 X-9.95 42 | ; MOVEMENT_CUTTING 43 | G1 X-10 44 | G1 Y10 45 | G1 X10 46 | G1 Y-10 47 | G1 X-9.95 48 | ; MOVEMENT_LEAD_OUT 49 | G1 X-9.96 50 | G1 Y-10.5 51 | ; >>> LASER Power OFF 52 | M107 53 | ; COMMAND_POWER_OFF 54 | ; MOVEMENT_RAPID 55 | G0 Z5 F300 56 | G0 X-9.99 Y-25.5 F2500 57 | G0 Z0 F300 58 | ; >>> LASER Power ON 59 | M106 S200 60 | ; COMMAND_POWER_ON 61 | ; MOVEMENT_LEAD_IN 62 | G1 Y-25 F1000 63 | G1 X-10 64 | ; MOVEMENT_CUTTING 65 | G1 X-25 66 | G1 Y25 67 | G1 X25 68 | G1 Y-25 69 | G1 X-10 70 | ; MOVEMENT_LEAD_OUT 71 | G1 X-10.01 72 | G1 Y-25.5 73 | ; >>> LASER Power OFF 74 | M107 75 | ; COMMAND_POWER_OFF 76 | ; MOVEMENT_RAPID 77 | G0 Z15 F300 78 | ; *** SECTION end *** 79 | 80 | ; *** STOP begin *** 81 | M400 82 | ; COMMAND_COOLANT_OFF 83 | ; COMMAND_STOP_SPINDLE 84 | G0 X0 Y0 F2500 85 | M117 Job end 86 | ; *** STOP end *** 87 | -------------------------------------------------------------------------------- /Test/1001_m3m5.gcode: -------------------------------------------------------------------------------- 1 | ;Fusion 360 CAM 2.0.4860 2 | ; Posts processor: MPCNC_Mill_Laser.cps 3 | ; Gcode generated: Sunday, December 2, 2018 1:56:26 PM GMT 4 | ; Document: cam_testpp v5 5 | ; Setup: Setup1 6 | ; 7 | ; Ranges table: 8 | ; X: Min=2.588 Max=36 Size=33.412 9 | ; Y: Min=2.588 Max=36 Size=33.412 10 | ; Z: Min=-1 Max=15 Size=16 11 | ; 12 | ; Tools table: 13 | ; T1 D=3.175 CR=0 - ZMIN=-1 - flat end mill 14 | ; T2 D=1.5 CR=0 - ZMIN=-1 - flat end mill 15 | 16 | ; *** START begin *** 17 | G90 18 | G21 19 | M84 S0 20 | G92 X0 Y0 Z0 21 | ; COMMAND_TOOL_MEASURE 22 | ; --- PROBE TOOL begin --- 23 | M0 Attach ZProbe 24 | G28 Z 25 | G92 Z0.8 26 | G0 Z50 F300 27 | M0 Detach ZProbe 28 | ; --- PROBE TOOL end --- 29 | ; *** START end *** 30 | 31 | ; *** SECTION begin *** 32 | ;2D Contour1 - Milling - Tool: 1 - 1/8inch flat end mill 33 | ; X Min: 2.588 - X Max: 49.412 34 | ; Y Min: 2.588 - Y Max: 49.412 35 | ; Z Min: -1 - Z Max: 15 36 | M400 37 | ; COMMAND_START_SPINDLE 38 | ; COMMAND_SPINDLE_CLOCKWISE 39 | ; >>> Spindle Speed 21000 40 | M3 S21000 41 | ; COMMAND_COOLANT_ON 42 | M117 2D Contour1 43 | G0 Z15 44 | G0 X49.412 Y26 F2500 45 | G0 Z5 F300 46 | ; MOVEMENT_PLUNGE 47 | G1 Z1 F100 48 | G1 Z-1 49 | ; 14 50 | G1 Y49.412 F300 51 | G1 X2.588 52 | G1 Y2.588 53 | G1 X49.412 54 | G1 Y26 55 | ; MOVEMENT_RAPID 56 | G0 Z15 57 | ; *** SECTION end *** 58 | 59 | ; *** SECTION begin *** 60 | ;2D Contour2 - Milling - Tool: 1 - 1/8inch flat end mill 61 | ; X Min: 9.587 - X Max: 42.412 62 | ; Y Min: 9.587 - Y Max: 42.412 63 | ; Z Min: -1 - Z Max: 15 64 | M400 65 | ; COMMAND_START_SPINDLE 66 | ; COMMAND_SPINDLE_CLOCKWISE 67 | ; >>> Spindle Speed 20000 68 | M3 S20000 69 | ; COMMAND_COOLANT_ON 70 | M117 2D Contour2 71 | G0 Z15 F300 72 | G0 X42.412 Y26 F2500 73 | G0 Z5 F300 74 | ; MOVEMENT_PLUNGE 75 | G1 Z1 F100 76 | G1 Z-1 77 | ; 14 78 | G1 Y42.412 F300 79 | G1 X9.587 80 | G1 Y9.587 81 | G1 X42.412 82 | G1 Y26 83 | ; MOVEMENT_RAPID 84 | G0 Z15 85 | ; *** SECTION end *** 86 | 87 | ; *** SECTION begin *** 88 | ; --- CHANGE TOOL begin --- 89 | ; COMMAND_COOLANT_OFF 90 | M400 91 | M300 S400 P2000 92 | G0 Z50 F300 93 | G0 X0 Y0 F2500 94 | ; COMMAND_STOP_SPINDLE 95 | M5 96 | M0 Put tool 2 - 1.5mm 97 | ; COMMAND_TOOL_MEASURE 98 | ; --- PROBE TOOL begin --- 99 | M0 Attach ZProbe 100 | G28 Z 101 | G92 Z0.8 102 | G0 Z50 F300 103 | M0 Detach ZProbe 104 | ; --- PROBE TOOL end --- 105 | ; --- CHANGE TOOL end --- 106 | ;Trace1 - Milling - Tool: 2 - 1.5mm flat end mill 107 | ; X Min: 16 - X Max: 36 108 | ; Y Min: 16 - Y Max: 36 109 | ; Z Min: -1 - Z Max: 15 110 | M400 111 | ; COMMAND_START_SPINDLE 112 | ; COMMAND_SPINDLE_CLOCKWISE 113 | ; >>> Spindle Speed 21000 114 | M3 S21000 115 | ; COMMAND_COOLANT_ON 116 | M117 Trace1 117 | G0 Z15 118 | G0 X36 Y36 F2500 119 | G0 Z4 F300 120 | ; MOVEMENT_LEAD_IN 121 | G1 Z-1 F300 122 | ; MOVEMENT_CUTTING 123 | G1 Y16 124 | G1 X16 125 | G1 Y36 126 | G1 X36 127 | ; MOVEMENT_LEAD_OUT 128 | G1 Z4 129 | ; MOVEMENT_RAPID 130 | G0 Z15 131 | ; *** SECTION end *** 132 | 133 | ; *** STOP begin *** 134 | M400 135 | ; COMMAND_COOLANT_OFF 136 | ; COMMAND_STOP_SPINDLE 137 | M5 138 | G0 X0 Y0 F2500 139 | M117 Job end 140 | ; *** STOP end *** 141 | -------------------------------------------------------------------------------- /Test/1001_m3m5_coolant.gcode: -------------------------------------------------------------------------------- 1 | ;Fusion 360 CAM 2.0.4860 2 | ; Posts processor: MPCNC_Mill_Laser.cps 3 | ; Gcode generated: Sunday, December 2, 2018 2:06:54 PM GMT 4 | ; Document: cam_testpp v5 5 | ; Setup: Setup1 6 | ; 7 | ; Ranges table: 8 | ; X: Min=2.588 Max=36 Size=33.412 9 | ; Y: Min=2.588 Max=36 Size=33.412 10 | ; Z: Min=-1 Max=15 Size=16 11 | ; 12 | ; Tools table: 13 | ; T1 D=3.175 CR=0 - ZMIN=-1 - flat end mill 14 | ; T2 D=1.5 CR=0 - ZMIN=-1 - flat end mill 15 | 16 | ; *** START begin *** 17 | G90 18 | G21 19 | M84 S0 20 | G92 X0 Y0 Z0 21 | ; COMMAND_TOOL_MEASURE 22 | ; --- PROBE TOOL begin --- 23 | M0 Attach ZProbe 24 | G28 Z 25 | G92 Z0.8 26 | G0 Z50 F300 27 | M0 Detach ZProbe 28 | ; --- PROBE TOOL end --- 29 | ; *** START end *** 30 | 31 | ; *** SECTION begin *** 32 | ;2D Contour1 - Milling - Tool: 1 - 1/8inch flat end mill 33 | ; X Min: 2.588 - X Max: 49.412 34 | ; Y Min: 2.588 - Y Max: 49.412 35 | ; Z Min: -1 - Z Max: 15 36 | M400 37 | ; COMMAND_START_SPINDLE 38 | ; COMMAND_SPINDLE_CLOCKWISE 39 | ; >>> Spindle Speed 21000 40 | M3 S21000 41 | ; COMMAND_COOLANT_ON 42 | ; >>> Coolant A ON 43 | M42 P11 S255 44 | M117 2D Contour1 45 | G0 Z15 46 | G0 X49.412 Y26 F2500 47 | G0 Z5 F300 48 | ; MOVEMENT_PLUNGE 49 | G1 Z1 F100 50 | G1 Z-1 51 | ; 14 52 | G1 Y49.412 F300 53 | G1 X2.588 54 | G1 Y2.588 55 | G1 X49.412 56 | G1 Y26 57 | ; MOVEMENT_RAPID 58 | G0 Z15 59 | ; *** SECTION end *** 60 | 61 | ; *** SECTION begin *** 62 | ;2D Contour2 - Milling - Tool: 1 - 1/8inch flat end mill 63 | ; X Min: 9.587 - X Max: 42.412 64 | ; Y Min: 9.587 - Y Max: 42.412 65 | ; Z Min: -1 - Z Max: 15 66 | M400 67 | ; COMMAND_START_SPINDLE 68 | ; COMMAND_SPINDLE_CLOCKWISE 69 | ; >>> Spindle Speed 20000 70 | M3 S20000 71 | ; COMMAND_COOLANT_ON 72 | ; >>> Coolant A OFF 73 | M42 P11 S0 74 | ; >>> Coolant B ON 75 | M42 P6 S255 76 | M117 2D Contour2 77 | G0 Z15 F300 78 | G0 X42.412 Y26 F2500 79 | G0 Z5 F300 80 | ; MOVEMENT_PLUNGE 81 | G1 Z1 F100 82 | G1 Z-1 83 | ; 14 84 | G1 Y42.412 F300 85 | G1 X9.587 86 | G1 Y9.587 87 | G1 X42.412 88 | G1 Y26 89 | ; MOVEMENT_RAPID 90 | G0 Z15 91 | ; *** SECTION end *** 92 | 93 | ; *** SECTION begin *** 94 | ; --- CHANGE TOOL begin --- 95 | M400 96 | G0 Z50 F300 97 | G0 X0 Y0 F2500 98 | ; COMMAND_COOLANT_OFF 99 | ; >>> Coolant B OFF 100 | M42 P6 S0 101 | ; COMMAND_STOP_SPINDLE 102 | M5 103 | M300 S400 P2000 104 | M0 Put tool 2 - 1.5mm 105 | ; COMMAND_TOOL_MEASURE 106 | ; --- PROBE TOOL begin --- 107 | M0 Attach ZProbe 108 | G28 Z 109 | G92 Z0.8 110 | G0 Z50 F300 111 | M0 Detach ZProbe 112 | ; --- PROBE TOOL end --- 113 | ; --- CHANGE TOOL end --- 114 | ;Trace1 - Milling - Tool: 2 - 1.5mm flat end mill 115 | ; X Min: 16 - X Max: 36 116 | ; Y Min: 16 - Y Max: 36 117 | ; Z Min: -1 - Z Max: 15 118 | M400 119 | ; COMMAND_START_SPINDLE 120 | ; COMMAND_SPINDLE_CLOCKWISE 121 | ; >>> Spindle Speed 21000 122 | M3 S21000 123 | ; COMMAND_COOLANT_ON 124 | M117 Trace1 125 | G0 Z15 126 | G0 X36 Y36 F2500 127 | G0 Z4 F300 128 | ; MOVEMENT_LEAD_IN 129 | G1 Z-1 F300 130 | ; MOVEMENT_CUTTING 131 | G1 Y16 132 | G1 X16 133 | G1 Y36 134 | G1 X36 135 | ; MOVEMENT_LEAD_OUT 136 | G1 Z4 137 | ; MOVEMENT_RAPID 138 | G0 Z15 139 | ; *** SECTION end *** 140 | 141 | ; *** STOP begin *** 142 | M400 143 | ; COMMAND_COOLANT_OFF 144 | ; COMMAND_STOP_SPINDLE 145 | M5 146 | G0 X0 Y0 F2500 147 | M117 Job end 148 | ; *** STOP end *** 149 | -------------------------------------------------------------------------------- /Test/1001_manual.gcode: -------------------------------------------------------------------------------- 1 | ;Fusion 360 CAM 2.0.4860 2 | ; Posts processor: MPCNC_Mill_Laser.cps 3 | ; Gcode generated: Sunday, December 2, 2018 1:57:21 PM GMT 4 | ; Document: cam_testpp v5 5 | ; Setup: Setup1 6 | ; 7 | ; Ranges table: 8 | ; X: Min=2.588 Max=36 Size=33.412 9 | ; Y: Min=2.588 Max=36 Size=33.412 10 | ; Z: Min=-1 Max=15 Size=16 11 | ; 12 | ; Tools table: 13 | ; T1 D=3.175 CR=0 - ZMIN=-1 - flat end mill 14 | ; T2 D=1.5 CR=0 - ZMIN=-1 - flat end mill 15 | 16 | ; *** START begin *** 17 | G90 18 | G21 19 | M84 S0 20 | G92 X0 Y0 Z0 21 | ; COMMAND_TOOL_MEASURE 22 | ; --- PROBE TOOL begin --- 23 | M0 Attach ZProbe 24 | G28 Z 25 | G92 Z0.8 26 | G0 Z50 F300 27 | M0 Detach ZProbe 28 | ; --- PROBE TOOL end --- 29 | ; *** START end *** 30 | 31 | ; *** SECTION begin *** 32 | ;2D Contour1 - Milling - Tool: 1 - 1/8inch flat end mill 33 | ; X Min: 2.588 - X Max: 49.412 34 | ; Y Min: 2.588 - Y Max: 49.412 35 | ; Z Min: -1 - Z Max: 15 36 | M400 37 | ; COMMAND_START_SPINDLE 38 | ; COMMAND_SPINDLE_CLOCKWISE 39 | M0 Turn ON spindle 40 | ; COMMAND_COOLANT_ON 41 | M117 2D Contour1 42 | G0 Z15 43 | G0 X49.412 Y26 F2500 44 | G0 Z5 F300 45 | ; MOVEMENT_PLUNGE 46 | G1 Z1 F100 47 | G1 Z-1 48 | ; 14 49 | G1 Y49.412 F300 50 | G1 X2.588 51 | G1 Y2.588 52 | G1 X49.412 53 | G1 Y26 54 | ; MOVEMENT_RAPID 55 | G0 Z15 56 | ; *** SECTION end *** 57 | 58 | ; *** SECTION begin *** 59 | ;2D Contour2 - Milling - Tool: 1 - 1/8inch flat end mill 60 | ; X Min: 9.587 - X Max: 42.412 61 | ; Y Min: 9.587 - Y Max: 42.412 62 | ; Z Min: -1 - Z Max: 15 63 | M400 64 | ; COMMAND_START_SPINDLE 65 | ; COMMAND_SPINDLE_CLOCKWISE 66 | ; COMMAND_COOLANT_ON 67 | M117 2D Contour2 68 | G0 Z15 F300 69 | G0 X42.412 Y26 F2500 70 | G0 Z5 F300 71 | ; MOVEMENT_PLUNGE 72 | G1 Z1 F100 73 | G1 Z-1 74 | ; 14 75 | G1 Y42.412 F300 76 | G1 X9.587 77 | G1 Y9.587 78 | G1 X42.412 79 | G1 Y26 80 | ; MOVEMENT_RAPID 81 | G0 Z15 82 | ; *** SECTION end *** 83 | 84 | ; *** SECTION begin *** 85 | ; --- CHANGE TOOL begin --- 86 | ; COMMAND_COOLANT_OFF 87 | M400 88 | M300 S400 P2000 89 | G0 Z50 F300 90 | G0 X0 Y0 F2500 91 | ; COMMAND_STOP_SPINDLE 92 | M0 Turn OFF spindle 93 | M0 Put tool 2 - 1.5mm 94 | ; COMMAND_TOOL_MEASURE 95 | ; --- PROBE TOOL begin --- 96 | M0 Attach ZProbe 97 | G28 Z 98 | G92 Z0.8 99 | G0 Z50 F300 100 | M0 Detach ZProbe 101 | ; --- PROBE TOOL end --- 102 | ; --- CHANGE TOOL end --- 103 | ;Trace1 - Milling - Tool: 2 - 1.5mm flat end mill 104 | ; X Min: 16 - X Max: 36 105 | ; Y Min: 16 - Y Max: 36 106 | ; Z Min: -1 - Z Max: 15 107 | M400 108 | ; COMMAND_START_SPINDLE 109 | ; COMMAND_SPINDLE_CLOCKWISE 110 | M0 Turn ON spindle 111 | ; COMMAND_COOLANT_ON 112 | M117 Trace1 113 | G0 Z15 114 | G0 X36 Y36 F2500 115 | G0 Z4 F300 116 | ; MOVEMENT_LEAD_IN 117 | G1 Z-1 F300 118 | ; MOVEMENT_CUTTING 119 | G1 Y16 120 | G1 X16 121 | G1 Y36 122 | G1 X36 123 | ; MOVEMENT_LEAD_OUT 124 | G1 Z4 125 | ; MOVEMENT_RAPID 126 | G0 Z15 127 | ; *** SECTION end *** 128 | 129 | ; *** STOP begin *** 130 | M400 131 | ; COMMAND_COOLANT_OFF 132 | ; COMMAND_STOP_SPINDLE 133 | M0 Turn OFF spindle 134 | G0 X0 Y0 F2500 135 | M117 Job end 136 | ; *** STOP end *** 137 | -------------------------------------------------------------------------------- /Test/cam_testpp.f3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flyfisher604/mpcnc_post_processor/dfcb050d4f16dcb755c931ce35db3af12e3d5a23/Test/cam_testpp.f3d -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flyfisher604/mpcnc_post_processor/dfcb050d4f16dcb755c931ce35db3af12e3d5a23/screenshot.jpg --------------------------------------------------------------------------------