I am new to Sheetcam still, but I have created a Post Processor for the JD’s Garage Plasma Cutter Gen 2 and similar machines that use grblHAL on an ESP32, with the Z-probe. I started with JD’s recommended “GRBL THC with scribe” post processor, and then I made several modifications that made the post-processor work a bit better for me and machines like mine. Also, being a very nerdy software engineer, I also made the g-code a little bit easier for me to follow and modify - and especially to create variants when I need to fix just one part of a cut. The comments in the g-code include a Cut # that matches up to Sheetcam’s numbers (S1, S2, etc.). I’m sure this post-processor is not perfect, I already have seen a few things I’d like to fix, but it works exceptionally well for me - better than the Fusion 360 GRBL post processor (though I don’t attribute that so much to my changes but more to the original that I modified.)
To provide a few more details that may be helpful. My machine is based on the JD’s Garage Generation 2 machine (which for anyone who is not aware comes from a set of plans that may be purchased for $25 US and which appears to be a clone of the entry-level Langmuir Systems Crossfire CNC plasma machine - right down to identical looking 3D printed parts.) I originally built the Gen 1 JD’s garage machine but had a ton of problems with it from problems with the 3D printed parts holding threads, to friction on the suspended arm, to the worst problem of all EMI on the Z-probe. I eventually gave up on their GEN 1 and started modifying my machine. I made it much bigger, added linear rails and a second Y rail and motor, and replaced their Z axis with one from Amazon. I threw out ALL of the 3D printed parts and made everything from metal - except that I did 3D print a breakaway magnetic torch holder. I added a DANI THC11DPL Torch Height Controller while I patiently waited for JD’s Garage to release their THC. (I bought theirs but have never hooked it up, the DANI THC works great, and in my experience, he is much easier to work with and get support from than the JD’s garage guys were before they decided I was too much of pain to deal with because I asked questions.) Because I also had problems with the JD’s garage Z-axis binding up and causing the probe switch to sometimes stick and other problems, when I replaced it with an 100mm Linear Stage Actuator for the Z-axis, I needed to make a new probe, so I designed an ohmic sensor that clamps to the torch and feeds a signal into a CNC4PC PTS-1 Plasma Touch Probe.
It was about the time that I decided to tackle the relentless EMI problems with the Z-probe (that still happened with the PTS-1) that JD’s Garage announced their Gen 2 machine. I bought the plans and converted my electronics over to match (mostly), but using the DANI THC and the PTS-1. I also made several other changes to fix issues with JD’s garage Gen 2 electronics, such as burning the e-fuse that lets you get rid of the otherwise completely unnecessary 2nd relay - which is only there because pin 12 on the ESP32 is a “strapping pin” which can’t be directly connected to the X PUL- on the X motor driver during the first 1ms of ESP32 startup without dropping the voltage to the internal SD storage to an unusable level – unless you burn the e-fuse freeing up the pin, and I modified the grblHAL + Blackbox driver code to send an enable signal on PIN 25 of the ESP32 for the PTS-1 during a probe operation). While redoing the electronics, I also put copper tape around EVERY wire, and I added a bypass capacitor to the Z-probe signal and several ferrite cores to the Z-probe signal inside the electronics box and near the torch. This fixed my EMI problems, and after 2 years of struggling with JD’s garage plans I finally have a working CNC Plasma Cutter!
I switched to Sheetcam because I have been considering selling some of my work (DXF files and/or physical pieces), but Fusion 360’s license is too restrictive and too costly for someone just starting up. A one-time fee for Sheetcam gets me in the clear and as I mentioned previously seems to work better anyway. (I have been doing my designs with Bend-Tech Pro’s Sheet Metal designer - also because it was a one-time license fee.)
Anyway - after my long rambling… The post-processor is attached. The only variables you need to worry about are listed and as follows:
- switchOffset - This defaults to 0.0 because my ohmic sensor doesn’t have an offset, but the stock JD’s Garage z-axis needs an offset to account for the distance you have to lift Z to get the switch to release and have the torch just barely touch the metal. Enter this in mm.
- firstPierceTime - This is left over from the GRBL THC with scriber post-processor, defaults to 0, but might be useful for some machines
- maxZProbeDistance - This is the maximum length of the Z-probe. I set it to the full-travel length in mm of my Z axis. You probably don’t need to change it for a stock JD’s Garage machine, but it is there just in case.
One of my modifications was to make sure there is a Z-probe before every cut. I also added a lot of comments to help make sense of what the g-codes do especially around probes and oddities of the grblHAL (such as the error 33 after a probe on Arcs.)
I hope you find this useful, and I want to make sure that everyone knows that I couldn’t have done it without the excellent work of the author of the GRBL THC with scriber.scpost.
Since I am a new user, I’m not allowed to upload the post-processor, so here it is in text:
grblHALPlasmaZProbe.scpost
--************************************************
--*** Set these values up to suit your machine ***
--************************************************
--Put your switch offset value here in MILLIMETRES
--For Switch-based probes:
-- Put a sheet of metal on your machine and place a sheet of paper on top.
-- Slowly jog the torch down onto the paper until the touch-off switch just operates.
-- Zero the Z axis then pull gently on the paper and slowly jog up until the paper slides out.
-- The Z axis position is your switch offset.
--For Ohmic Probes:
-- Set to 0.0. If you need to back-off from the point of probe contact put the distance in MILLIMETERS here
switchOffset = 0.0
--this is an extra delay added to the first pierce as needed by some machines
firstPierceTime = 0
--Maximum Z-probe distance in MILLIMETERS
maxZProbeDistance = 100
--************************************************
--*** End of settings ***
--************************************************
--************************************************
--*** GLOBAL VARIABLES ***
--************************************************
--Cut Counter, just to keep track
cutCount = 0
--Last cutCount where Cut Height Comment was shown
cutCountHeightComment = 0
function OnAbout(event)
ctrl = event:GetTextCtrl()
ctrl:AppendText("grblHAL Plasma Cutter with Z probe\n")
ctrl:AppendText("Written by Andrew L. Sandoval - based on GRBL THC with scriber.scpost\n")
ctrl:AppendText("\n")
ctrl:AppendText("Modal G-codes and coordinates\n")
ctrl:AppendText("Comments enclosed with ( and )\n")
ctrl:AppendText("M03/M05 turn the torch on/off\n")
ctrl:AppendText("The torch is referenced with a Z-probe before each pierce\n")
ctrl:AppendText("Designed for use with grblHal and a z-probe\n")
ctrl:AppendText("Post variables:\n")
ctrl:AppendText("switchOffset - set your net switch offset amount\n")
ctrl:AppendText("maxZProbeDistance - set the maximum Z-probe distance\n")
end
-- Modified 10/14/24 by Andrew L. Sandoval
-- Based on GRBL THC with scribe
post.DefineVariable("switchOffset",sc.unitLINEAR,-1e17,1e17)
post.DefineVariable("maxZProbeDistance",sc.unitLINEAR,0,1e17)
function OnInit()
offX = 0
offY = 0
offZ = 0
post.SetCommentChars ("()", "[]") --make sure ( and ) characters do not appear in system text
commentText = "Post: " .. postName .. " Andrew L. Sandoval"
OnComment()
commentText = fileNameOnly .. " " .. " " .. date .. " " .. time
OnComment()
if(scale == metric) then
commentText = "Metric Mode"
OnComment()
post.Text ("G21\n") --metric mode
else
commentText = "Inch Mode"
OnComment()
post.Text ("G20\n") --inch mode
end
post.Text ("G53 G90 G40\n")
minArcSize = 0.2 --arcs smaller than this are converted to moves
firstRef = true
currentZAxis = "Z"
dist = 9999999
lastz = 0
thcstate = 1
firstPierce = firstPierceTime;
commentText = "Feedrate: " .. var.JetOperation0_FeedRate .. " mm/min " .. (var.JetOperation0_FeedRate * (1/25.4)) .. " inches/min"
OnComment()
commentText = "Plunge rate: " .. var.JetOperation0_PlungeRate .. " mm/min " .. (var.JetOperation0_PlungeRate * (1/25.4)) .. " inches/min"
OnComment()
commentText = "Plunge Saftey: " .. plungeSafety .. " mm " .. (plungeSafety * (1/25.4)) .. " inches"
OnComment()
commentText = "Safe Z: " .. safeZ .. " mm " .. (safeZ * (1/25.4)) .. " inches"
OnComment()
end
function OnNewLine()
post.Text ("N")
post.Number (lineNumber, "0000")
post.Text(" ")
lineNumber = lineNumber + 10
end
function OnFinish()
endZ = safeZ
OnRapid()
endX = 0
endY = 0
offX = 0
offY = 0
offZ = 0
OnRapid()
post.Text ("M05\n") -- Torch Off
post.ModalText ("G00 X0 Y0") -- Go Home, with Z at safe height
if(currentZ ~= safeZ) then
post.ModalNumber (" " .. currentZAxis, safeZ * scale, "0.0000")
post.Eol()
end
post.Text("M30\n") -- Reset
cutCount = 0
end
function OnRapid()
if(endX > 1e17 and endY > 1e17) then return end
local len = math.hypot((endX + offX)-currentX , (endY + offY)-currentY)
dist = dist + len
post.ModalText ("G00 ")
post.ModalNumber ("X", (endX + offX) * scale, "0.0000")
post.ModalNumber (" Y", (endY + offY) * scale, "0.0000")
if(offZ and firstRef == false and currentZ ~= safeZ) then
post.ModalNumber (" " .. currentZAxis, (endZ + offZ) * scale, "0.0000")
end
post.Eol()
end
function OnMove()
post.CancelModaltext()
post.CancelModalNumbers()
local len = math.hypot(endX - currentX , endY - currentY)
dist = dist + len
post.ModalText ("G01")
post.ModalNumber (" X", (endX + offX) * scale, "0.0000")
post.ModalNumber (" Y", (endY + offY) * scale, "0.0000")
if(offZ) then
post.ModalNumber (" " .. currentZAxis, (endZ + offZ) * scale, "0.0000")
end
post.ModalNumber (" F", feedRate * scale, "0.0###")
if(cutCountHeightComment < cutCount) then
commentText = "Cut Height: " .. cutHeight .. " mm " .. (cutHeight * (1/25.4)) .. " inches"
OnCommentSpace()
cutCountHeightComment = cutCount
end
post.Eol()
end
function OnArc()
post.CancelModaltext()
post.CancelModalNumbers()
local radius = math.hypot(currentX - arcCentreX, currentY - arcCentreY)
dist = dist + radius * math.abs(arcAngle)
if(arcAngle <0) then
post.ModalText ("G03")
else
post.ModalText ("G02")
end
post.ModalNumber (" X", (endX + offX) * scale, "0.0000")
post.ModalNumber (" Y", (endY + offY) * scale, "0.0000")
if(offZ) then
post.ModalNumber (" " .. currentZAxis, (endZ + offZ) * scale, "0.0000")
end
post.Text (" I")
post.Number ((arcCentreX - currentX) * scale, "0.0000")
post.Text (" J")
post.Number ((arcCentreY - currentY) * scale, "0.0000")
post.ModalNumber (" F", feedRate * scale, "0.0###")
post.Eol()
end
function OnPenDown()
cutCount = cutCount + 1
-- Always Reference() before starting a cut, regardless of dist
-- this matches how the Fusion 360 GRBL post processor works and ensure each new
-- cut starts at the proper height
commentText = "Cut " .. cutCount .. " X=" .. (scale*currentX) .. " Y=" .. (scale*currentY)
OnComment()
Reference(); -- Z-probe
post.ModalText ("G00")
post.Text(" Z")
post.Number (pierceHeight * scale, "0.0000")
commentText = "Z @ pierce height: " .. pierceHeight .. " mm " .. (pierceHeight * (1/25.4)) .. " inches"
OnCommentSpace()
post.Eol()
post.Text ("M03\n")
if (pierceDelay + firstPierce > 0.001) then
post.Text ("G04 P")
post.Number (pierceDelay + firstPierce,"0.0##")
firstPierce = 0
commentText = "Pierce Delay: " .. pierceDelay .. " seconds"
OnCommentSpace()
post.Eol()
end
-- Reset G0 after G38.2 to avoid Error 33 from GRBL
post.Text("G00 X")
post.Number(currentX * scale, "0.0000")
post.Text(" Y")
post.Number(currentY * scale, "0.0000")
post.Text(" Z")
post.Number(pierceHeight * scale, "0.0000")
post.Text(" F")
post.Number (plungeRate * scale, "0.0###")
commentText = "Reset position after probe to prevent GRBL error 33"
OnCommentSpace()
post.Eol()
-- ^^^
end
function OnNewOperation()
commentText = "Operation: " .. operationName .. " OpIndex: " .. operationIndex
OnComment()
end
function OnNewPart()
commentText = "Part: " .. partName .. " partIndex: " .. partIndex
OnComment()
end
function OnSetFeed()
post.ModalNumber ("F", feedRate * scale, "0.0###")
commentText = "Feedrate: " .. feedRate .. " mm/min " .. (feedRate * (1/25.4)) .. " inches/min"
OnCommentSpace()
post.Eol()
end
function Reference()
firstRef = false
post.ModalText("G38.2 Z")
post.Number((-1 * maxZProbeDistance) * scale, "0.0##")
post.ModalNumber (" F", plungeRate * scale, "0.0###")
commentText = "Z-probe with Feedrate: " .. plungeRate .. " mm/min " .. (plungeRate * (1/25.4)) .. " inches/min"
OnCommentSpace()
post.Eol()
post.ModalText("G92 Z0.0") -- Z0 is now where the Z-probe triggers (touches)
commentText = "Z = Probe Touch/Trigger"
OnCommentSpace()
post.ModalText ("G00")
post.Text(" Z")
post.Number (switchOffset * scale, "0.0000")
commentText = "switchOffset " .. switchOffset .. " mm " .. (switchOffset * scale) .. " inches"
OnCommentSpace()
post.Eol()
post.ModalText("G92 Z0.0") -- Z0 is now where the Z-probe has backed off to the point where the nozzle just touches
commentText = "Z = nozzle touching metal (e.g. adjusted by switch Offset)"
OnCommentSpace()
end
function OnPenUp()
post.Text ("M05\n")
if (endDelay > 0) then
post.Text ("G04 P")
post.Number (endDelay,"0.###")
post.Eol()
end
end
function OnToolChange()
offX = 0
offY = 0
offZ = 0
end
function OnDrill()
OnRapid()
currentX = endX
currentY = endY
OnPenDown()
endZ = drillZ
OnMove()
OnPenUp()
endZ = safeZ
OnRapid()
end
function OnComment()
post.Text("(",commentText,")\n")
end
function OnCommentSpace()
post.Text(" (",commentText,")\n")
end