- add versioning
- store units (mm or inches) and restore later
- switch from mc.mcCntlGcodeExecute to mc.mcCntlGcodeExecuteWait
- add the "initial probe, back off and probe slower" strategy
- fix some tostring() typos
function m6()
-- Manual Tool Change script with optional tool length probing
-- V0.53 6 Feb 24
-- DG from FNR RecceDG@gmail.com
local inst = mc.mcGetInstance() -- normal lua script context switch
-- Constants
local toolprobe_machine_x = 0.023 -- X coordinate of centre of tool length probe in machine coordinates
local toolprobe_machine_y = 1.123 -- X coordinate of centre of tool length probe in machine coordinates
local toolprobe_clearance = 9.0 -- distance between spindle face and top of tool length probe
local toolprobe_gcode = "G31.1" -- The Gcode that probes our toolsetter. GET THIS RIGHT!
local toolprobe_variable = 5073 -- Mach 4 variable for probed position
-- Function Start
local currentTool = mc.mcToolGetCurrent(inst) -- get tool number of the tool currently in the spindle
local selectedTool = mc.mcToolGetSelected(inst) -- get tool number of the tool selected for the change
local posmode = mc.mcCntlGetPoundVar(inst, mc.SV_MOD_GROUP_3) -- get the current mode so we can return to it when macro ends
--Get current positions before moving to do tool change
local valX, rc = mc.mcAxisGetMachinePos(inst, mc.X_AXIS) -- Get the position of the X axis in Machine Position
local valY, rc = mc.mcAxisGetMachinePos(inst, mc.Y_AXIS) -- Get the position of the Y axis in Machine Position
local valZ, rc = mc.mcAxisGetMachinePos(inst, mc.Z_AXIS) -- Get the position of the Z axis in Machine Position
-- If the spindle is on, stop it
local spindleState, rc = mc.SpindleGetDirection(inst)
if not (spindleState == mc.SPINDLE_OFF) then
mc.spindleSetDirection(inst, mc.SPINDLE_OFF)
end
-- check coolant state and turn it off if it is on
local sigh = mc.mcSignalGetHandle(inst, mc.OSIG_COOLANTON);
local coolantState = mc.mcSignalGetState(sigh);
if (coolantState == 1) then
local SigCool = mc.mcSignalGetHandle (inst,mc.OSIG_COOLANTON)
mc.mcSignalSetState(SigCool,0)
end
-- Ditto mist
local sigh = mc.mcSignalGetHandle(inst, mc.OSIG_MISTON);
local mistState = mc.mcSignalGetState(sigh);
if (mistState == 1) then
local SigMist = mc.mcSignalGetHandle (inst,mc.OSIG_MISTON)
mc.mcSignalSetState(SigMist,0)
end
-- Get the current machine units
local machineUnits = mc.mcCntlGetUnitsCurrent(inst)
-- Get the tool length of the new tool from the tool table
-- DANGER - DANGER- DANGER
-- YOU HAD BETTER HAVE THIS SET OR CRASHY CRASHY!!!!
local toolTableHeight, rc = mc.mcToolGetData(inst, mc.MTOOL_MILL_HEIGHT, selectedTool)
-- Changing to current tool? NOOP
if (selectedTool == currentTool) then
mc.mcCntlSetLastError(inst, "M6 Tool Change to tool already mounted - NOOP")
return
else
-- We are going to change a tool
-- We we homed? If not, we don't know where we are
local zhomed, rc = mc.mcAxisIsHomed(inst, mc.Z_AXIS) -- Get the homed state of the Z axis
local xhomed, rc = mc.mcAxisIsHomed(inst, mc.X_AXIS) -- Get the homed state of the X axis
local yhomed, rc = mc.mcAxisIsHomed(inst, mc.Y_AXIS) -- Get the homed state of the Y axis
-- Maybe we were homed in place...
local zhomeip, rc = mc.mcAxisGetHomeInPlace(inst, mc.Z_AXIS) -- Get the HIP status of the Z axis
local xhomeip, rc = mc.mcAxisGetHomeInPlace(inst, mc.X_AXIS) -- Get the HIP status of the X axis
local yhomeip, rc = mc.mcAxisGetHomeInPlace(inst, mc.Y_AXIS) -- Get the HIP status of the Y axis
if not (zhomed and xhomed and yhomed) then
-- We aren't homed!
local ackNotHomed = wx.wxMessageBox("MACHINED NOT HOMED", "Machine was not homed so no safe tool change position.\nPlease close this window and jog to a safe location.\nThen press CYCLE START", 4)
-- Execute the tool change. This could be a function...
mc.mcCntlSetLastError(inst, "Change to tool " .. tostring(selectedTool) .. " and press CYCLE START to continue") -- Message to prompt operator to change tool
mc.mcCntlToolChangeManual(inst) -- This will pause the tool change here and wait for a press of cycle start to continue
mc.mcToolSetCurrent(inst, selectedTool) -- sets the current tool displayed in mach to the selected tool
mc.mcCntlSetLastError(inst, "Current tool == " .. tostring(selectedTool) .. " Previous Tool == " .. tostring(currentTool)) -- Message that shows after Cycle Start
mc.mcCntlGcodeExecuteWait(inst, "G43 H" .. toString(selectedTool) .." ") -- APPLIES THE TOOL LENGTH FROM THE TOOL TABLE
return
elseif (zhomeip or xhomeip or yhomeip) then
-- We were homed in place, which is effectively not homed at all
local ackNotHomed = wx.wxMessageBox("MACHINED HOMED IN PLACE", "Machine was homed in place so no safe tool change position.\nPlease close this window and jog to a safe location.\nThen press CYCLE START", 4)
-- Execute the tool change. This could be a function...
mc.mcCntlSetLastError(inst, "Change to tool " .. tostring(selectedTool) .. " and press CYCLE START to continue") -- Message to prompt operator to change tool
mc.mcCntlToolChangeManual(inst) -- This will pause the tool change here and wait for a press of cycle start to continue
mc.mcToolSetCurrent(inst, selectedTool) -- sets the current tool displayed in mach to the selected tool
mc.mcCntlSetLastError(inst, "Current tool == " .. tostring(selectedTool) .. " Previous Tool == " .. tostring(currentTool)) -- Message that shows after Cycle Start
mc.mcCntlGcodeExecuteWait(inst, "G43 H" .. toString(selectedTool) .." ") -- APPLIES THE TOOL LENGTH FROM THE TOOL TABLE
return
else
-- We have a machine home position so we are safe (?) to move the machine around
-- ASSUMES INCHES
mc.mcCntlGcodeExecuteWait(inst, "G20 G90 G53 G0 Z0.0"); -- Move the Z axis all the way up in machine coordinates
-- Execute the tool change. Could be a function...
mc.mcCntlSetLastError(inst, "Change to tool " .. tostring(selectedTool) .. " and press CYCLE START to continue") -- Message to prompt operator to change tool
mc.mcCntlToolChangeManual(inst) -- This will pause the tool change here and wait for a press of cycle start to continue
mc.mcToolSetCurrent(inst, selectedTool) -- sets the current tool displayed in mach to the selected tool
mc.mcCntlSetLastError(inst, "Current tool == " .. tostring(selectedTool) .. " Previous Tool == " .. tostring(currentTool)) -- Message that shows after Cycle Start
-- Ask user if they want to be probed
local probeCheck = wx.wxMessageBox("Probe this tool?", "YES to probe tool length, NO to continue without probing", 2)
if (probeCheck == 2) then --2 is the wx constant for YES
-- WE ARE PROBING
rc = mc.mcCntlSetLastError(inst, 'Probing tool length')
mc.mcCntlGcodeExecuteWait(inst, "G53 G01 X" ..tostring(toolprobe_machine_x) .." Y" .. tostring(toolprobe_machine_y) .." F100"); -- Move the X axis and Y axis to the tool setter machine coordinates.
-- TODO Change this to a rapid move once the move is proven
local initialMove = -1 * (toolTableHeight + 1) -- Sets the z height to rapid down to so we are 1" above the tool setter
-- HOLY BALLS IS THIS DANGEROUS - FOR DEBUGGING MAKE THIS A G1 MOVE AND SLOW
-- TODO - how do we handle drills, boring heads, and other bull********* where the tool table length might be wrong?
-- Answer is probably to make sure tool table is updated before running job, but maybe we can put a flag in the
-- tool table that says "this is a variable length tool" and probes right from Z0
mc.mcCntlGcodeExecuteWait(inst, "G53 G1" .. toString(initialMove) .. "F10") -- Moves the z axis down to 1 inch above the tool heigh probe I THINK
-- Change this to rapid once it has been proven out
mc.mcCntlGcodeExecuteWait(inst, "G53 G91 " .. toolprobe_gcode .. " Z-1.063 F15") -- Probes z axis to the tool setter. Only moves an extra 1/16" to limit overtravel, although good tool probes will have an overtravel switch
mc.mcCntlGcodeExecuteWait(inst, "G90 G0 Z0.2") -- Come up 0.2"
mc.mcCntlGcodeExecuteWait(inst, "G53 G91 " .. toolprobe_gcode .. " Z-.263 F4") -- Probe again, but slower
mc.mcCntlGcodeExecuteWait(inst, "G90 G53 G0 Z0") -- Move the head all the way up again
-- MATH TIME!
local probedPosition = mc.mcCntlGetPoundVar(inst, toolprobe_variable) -- get the probed position from the Mach variable (#5073) in machine coordinates
local expectedProbePosition = toolprobe_clearance - toolTableHeight -- work out where we should have tripped the probe if the tool table was correct
local lengthDelta = expectedProbePosition - probedPosition -- get the delta between expected and actual
local probedToolLength = toolTableHeight + lengthDelta -- correct the length value
-- Debug message
local ackProbed = wx.wxMessageBox("Length Probe Sanity Check", "Expected "..tostring(expectedProbePosition).." got "..tostring(probedPosition).." delta "..tostring(lengthDelta).." table "..tostring(toolTableHeight).." new "..tostring(probedToolLength).." ", 4)
if (math.abs(lengthDelta) > 0.25) then
-- Our probed length is more than 0.25" off, so something is seriously wrong
local ackExcessDelta = wx.wxMessageBox("Length Delta Too Large", "Probed tool length delta is too large. Aborting!", 4)
mc.mcCntlGcodeExecuteWait(inst, "M30") -- Is there a better way to abort cleanly?
return
else
-- We are safe to update the tool table
mc.mcCntlSetLastError(inst, "Probed Tool Length" .. tostring(probedToolLength))
mc.mcToolSetData(inst, mc.MTOOL_MILL_HEIGHT, selectedTool, probedToolLength) -- this sets the tool length value into the tool table into the selected tool position number
mc.mcCntlGcodeExecuteWait(inst, "G43 H" .. toString .. (selectedTool) .." ") -- APPLIES THE TOOL LENGTH FROM THE TOOL TABLE
end
elseif (probeCheck == 8) then -- 8 is "NO"
-- NO PROBE
rc = mc.mcCntlSetLastError(inst, 'Tool Length Probe Skipped')
mc.mcCntlGcodeExecuteWait(inst, "G43 H" .. toString .. (selectedTool) .." ") -- APPLIES THE TOOL LENGTH FROM THE TOOL TABLE
end
mc.mcCntlGcodeExecuteWait(inst, "G90 G53 G0 X" .. tonumber(valX) .. " Y" .. tonumber(valY)) -- Move back to X & Y initial location. This should be safe because we are all the way up
mc.mcCntlGcodeExecute(inst, "G0 Z" .. tonumber(valZ)) -- Move back to Z initial location. This SHOULD be safe, because we have applied the new tool length offset
-- with the G43 call, and as long as the tool length is correct, we should be at the same place as when we
-- called the M6, and sane CAM called the tool change from the retract height... but maybe this is fraught
-- with peril?
mc.mcCntlGcodeExecuteWait(inst, string.format('G ' .. posmode)) -- return to pre macro movement mode, G90 or G91
mc.mcCntlSetLastError(inst, "Tool Change Complete H ".. tostring(selectedTool) .. " set to ".. tostring(ToolLength).." inches")
end
end
-- Restore the spindle state.
-- Is this necessary?
mc.spindleSetDirection(inst, spindleState)
-- Restore the coolant & mist states
-- Is this necessary?
if (coolantState == 1) then
local SigCool = mc.mcSignalGetHandle (inst,mc.OSIG_COOLANTON)
mc.mcSignalSetState(SigCool,1)
end
-- Ditto mist
if (mistState == 1) then
local SigMist = mc.mcSignalGetHandle (inst,mc.OSIG_MISTON)
mc.mcSignalSetState(SigMist,1)
end
-- Restore machine units
if (machineUnits == 200) then -- Ugh, this is terrible. Is there a #DEFINE that identifies mc.MC_INCH or something?
mc.mcCntlGcodeExecuteWait(inst, "G20") -- Maybe use API instead?
else
mc.mcCntlGcodeExecuteWait(inst, "G21")
end
end
if (mc.mcInEditor() == 1) then
m6()
end
> How do you handle ‘manual’ tool changes?
Well strictly speaking, all my tool changes are "manual", in that my mill has no ATC.
Assuming that you mean "how do you handle tool changes where the tool isn't in the tool table" (or "cannot be in the tool table, because collet") I currently have a code path that changes the tool but doesn't probe length - although it still pulls the tool data from the tool table.
I suppose I could add a test to see if the tool exists in the tool table, and if it does not, have the option to probe all the way down from Z home....
My current assumption is that setting up the tool table is an essential part of program preparation.
> Another question: how are you handling the start and end of a job/day of working?
I'm a little early on the curve for that, but I expect that it would be checking the job sheet against the tool table and making sure that they lined up.
I have my tools defined in Mastercam, and on my TODO list is to duplicate that tool table in Mach.