Hello Guest it is April 28, 2024, 07:03:22 AM

Author Topic: Yet Another Tool Change Script  (Read 537 times)

0 Members and 1 Guest are viewing this topic.

Yet Another Tool Change Script
« on: January 08, 2024, 12:04:02 PM »
I'm in the process of writing my M6 macro (that incorporates tool length probing) and all the scripts I have seen so far have bugs in them.

So this is to document my attempt at the be-all, end-all, cover-all-edge-cases tool change script.

Psudocode

M6(tool_number) {

      constant tool_probe_x = "0.250"                            # Machine coords of tool probe centre
      constant tool_probe_y = "-1.250"

      constant probe_clearence = "9"                              # Distance between spindle and top of tool probe at G53 Z0

     get current_tool_number
     return if (tool_number == current_tool_number)      # We are trying to change to the already installed tool. This is a NO-OP

     get spindle_state                                                    # Shut off spindle if it is on
     GCode("M5") if (spindle_state != "off")

     get flood_coolant_state                                           # Shut off coolant if it is on
     get mist_coolant_state
     GCode ("M9") if ((flood_coolant_state != "off") || (mist_coolant_state != "off")

     get current_x_position                                              # Work coordinates
     get current_y_position
     get current_z_position

     get tool_desc = Tool_Table_Description (tool_number)
     get tool_length = Tool_Table_Length (tool_number)

     GCode ("G53 G0 Z0.0")                                                 # Lift head all the way up
     StatusMsg("Change to tool $tool_number $tool_desc")
     Mach4_Wait_On_Tool_Change                                        # Mach4 built in manual tool change function. Flash UI, wait on CYCLE START

     UserPrompt("Probe this tool?")                                       # Open dialogue box
     if ("no") {
          GCode("G43 H$tool_number")                                   # Apply tool length from tool table
          GCode("G0 Z $current_z_position")                            # I sure hope you got that length right!

     } elsif ("yes") {
          GCode ("G53 G0 X $tool_probe_x Y $tool_probe_y")      # Move tool probe under tool
          rapid_down = -1 *(probe_clearence - tool_length - 2)    # We are going to rapid down to 2" above the tool probe!
          GCode("G53 G0 Z$rapid_down")

          probe_z = -1*(probe_clearence)                                   # The point is negative but the constant is positive

          GCode("G31.1 Z$probe_z F10")                                    # probe the tool

          probe_stop = get_#_variable(probe Z)                          # read the probed end position
          measured_tool_length = probe_clearence + probe_stop  # Probed position should be negative... check this

          if (abs(measured_tool_length - tool_length) > 0.020) {   # If we are over a reasonable threshold for a difference in measured length vs table length, something is wrong!
                Freak_Out                                                              # exact steps TBD

          } else {
                write_tool_table(tool_number, measured_tool_length)   # Update the tool table and activate the offset
                GCode("G43 H$tool_number")

               GCode("G53 Z0")                                                          # Lift head to safe height
               GCode("G0 X$current_x_position Y$current_Y_position")  # Back to where we were XY
               GCode(G0 Z$current_z_position")                                   # Perilous!

         }
     else {
               FreakOut     #Should never get here
      }
         
      GCode ("M7") if (flood_coolant_state == "on")
      GCode ("M8") if (mist_coolant_state == "on")

}

Re: Yet Another Tool Change Script
« Reply #1 on: January 10, 2024, 01:59:50 PM »
Additional edge case: no probing or tool length shenanigans unless the machine has been properly homed.

If machine not homed, or homed in place, tool change ends with head all the way up and no interaction with tool table nor offer to probe length.

Offline Bill_O

*
  •  563 563
    • View Profile
Re: Yet Another Tool Change Script
« Reply #2 on: January 10, 2024, 02:51:05 PM »
Would love to see the code when you are done.
Re: Yet Another Tool Change Script
« Reply #3 on: January 11, 2024, 09:04:36 AM »
Well there are a couple of discussion/debate points I need to come to terms with before I calcify this in code:

1. Who is responsible for the G43 tool length code?

In discussion elsewhere, it appears that industry practice is that the CAM post-processor is normally responsible for issuing the G43 call to enable the tool length offset.

This strikes me as odd - partially because I cam into CAM from lathe, where the tool change call includes the length offset and the call returns with the length offset applied.

So in the lathe world, lets saw Tool 1 is 2" long and Tool 2 is 1" long.

G0 X2
T0202 M6

The DRO now reads X 3.000.

But in the mill world:

G0Z-2
T2 M6

The DRO reads Z -2.000 until we do G43 H2 - and now it reads Z -1.000.

That strikes me as bonkers - now I have to make sure my CAM is tacking the G43 onto each tool change. Wouldn't it make more sense for the M6 to come back with the proper length applied, just like lathe?

2. What Z should I be at when the M6 completes?

In order to be safe, the tool change has to happen with the head all the way up - so we are going there. But when we are done, do we stay at G53 Z0, do we apply the tool length offset and return to the Z position we were at when the M6 was called, or do we pick an arbitrary "Tool Change Home" (common across all jobs) and go there?

I kinda want to apply tool length and go back to our original Z - with the understanding that we are going to call the M6 from a safe retract height.

3. Is it worthwhile to try and calculate the expected distance between the tip of the new tool and the top of the tool probe and rapid down to ~2" above the probe before calling the probe routine, rather than slowly feeding down from G53 Z0?

Offline cncmagic

*
  •  63 63
  • what me worry? heck...it ain't my machine anyway
    • View Profile
Re: Yet Another Tool Change Script
« Reply #4 on: January 11, 2024, 09:39:58 PM »
many many many years ago.. in a dark and unfriendly land called Long Island, New York.. A manufacturing engineer toiled in obscurity making aircraft parts... normally we applied the tool offset at the tool change point.. or since a lot of stuff way back then was done manually, we applied a new G92 position after the tool change. In this way coding G0 (or G1) Z(clearance point depends on whether you used the top of the finished part as zero, or the bottom as zero) always cleared the part the same distance. and made it easy to find errors if you used the rapid override circular do-dad on the front of the control and single blocked to the position during setup. And it was always done this way if the tool in the carousel was changed and a new offset entered. Can't tell you how many times someone forgot a digit or added a digit.  :o
any semblance of information posted to anything remotely  close to accuracy is merely coincidence. Use at you own discretion.. or play the lottery.. same odds

Offline Bill_O

*
  •  563 563
    • View Profile
Re: Yet Another Tool Change Script
« Reply #5 on: January 12, 2024, 08:30:36 AM »
Just my 2 cents

1.  I would put the G43 and the H (followed by the tool number) in the tool change script. Less likely it will get missed.
2.  I would leave it up. The first move after the tool change should be a rapid move to the starting position so no need for you to do it.
3.  If you can that would be great. Shorten the time for the probe move.

Bill
Re: Yet Another Tool Change Script
« Reply #6 on: February 03, 2024, 01:47:37 PM »
OK, here is my first attempt.

DANGER - DANGER - DANGER

This has not been trialed! I haven't event tried compiling it! It is fresh out of my brain!

I would appreciate a code review, especially my math!

Code: [Select]
function m6()

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 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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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
  mc.mcCntlGcodeExecute(inst, "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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(inst, "G53 " .. 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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(inst, "G43 H" .. toString .. (selectedTool) .." ") -- APPLIES THE TOOL LENGTH FROM THE TOOL TABLE
  end
 
mc.mcCntlGcodeExecute(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, string.format('G ' .. posmode)) -- return to pre macro mode G90, or G91

  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.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


end
 
if (mc.mcInEditor() == 1) then
  m6()
end
Re: Yet Another Tool Change Script
« Reply #7 on: February 05, 2024, 03:10:19 PM »
Added a G91 to the probe function... whoops.

Also need to check to see if Mach/ESS supports the "double-tap" probe movement internally, or if I have to implement it in the macro.

Also: don't reset back to the pre-call G90 or G91 mode until after we've stopped moving around.

Code: [Select]
function m6()

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 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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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
  mc.mcCntlGcodeExecute(inst, "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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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.mcCntlGcodeExecute(inst, "G43 H" .. toString .. (selectedTool) .." ") -- APPLIES THE TOOL LENGTH FROM THE TOOL TABLE
  end
 
mc.mcCntlGcodeExecute(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.mcCntlGcodeExecute(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


end
 
if (mc.mcInEditor() == 1) then
  m6()
end

Logged

    Print

Pages: 1   Go Up
« previous next »
« Last Edit: February 05, 2024, 03:14:07 PM by RecceDG »
Re: Yet Another Tool Change Script
« Reply #8 on: February 06, 2024, 11:40:53 AM »
I’m working on my own toolchanger implementation right now actually. Perfect timing.  I have (perhaps out of an abundance of caution because my machine isn’t enclosed) installed an optical sensor to verify that there actually is a tool in the spindle after the tool change (and more importantly, that there ISN’T a tool in there before grabbing the first tool from the tool carousel).

How do you handle ‘manual’ tool changes? (My renishaw probe has a wire, so i have to load it manually)

I was thinking to assign it to tool ‘11’ (my tool carousel has only 10 slots) and whenever i want to probe, i tell the machine to go to tool 11 and there is some code in the script that recognizes it and has a different protocol.

Another question: how are you handling the start and end of a job/day of working? Is it expected that the spindle always has a tool in it and you will always be swapping?

I was planning to store my spindle empty (bad idea?) and have the script check the sensor i mentioned before and skip the ‘replace existing tool’ part of the script.

So many ways to do it.  What is the common practice in industry?
Re: Yet Another Tool Change Script
« Reply #9 on: February 06, 2024, 03:18:55 PM »
- 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

Code: [Select]
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.