Add a tool touch sensor

A few weeks ago, I made a wooden plate using my mill machine and decided to time how long it would take to mill the whole plate.  I found out that I spent 25 minutes out of 67 doing material Z measurement (zero) and Tool Z adjustement after each tool change (I want a toolchanger!)

I realized that a few thing that I could do to streamline operations (and prevent small tool breakage) would be :

  1. Automated material height sensing (Z).  must work with both conductive and non-conductive materials.
  2. Automated tool length measurement before and after a tool change in order to automatically adjust the current Z position
  3. 3 axis touch sensor (I already have it, just need to make connection easy to do)

I decided that the biggest time saving would be #2 – Automated tool length compensation so here were go!

Requirements :

  • Based on electric contact between tool and a touch plate (all my tools are metallic / conductive)
  • Spring loaded touch plate – the probing should prevent any pressure on the plate but I wanted to make sure there was some ‘give’ to ensure no breakage of small tools (ex : 1/32 end mill)
  • Use either Mach4 tools table lengths or a ‘safe’ length if no length is provided for the tool
  • Fully automated motion from current position to touch plate and then return to original position (with adjusted Z leading to the tip of the tool being to the exact same height as the previous tool
  • Watch for any crash when moving Z down in order to prevent tool breakage and graciously handle unexpected contact
  • Able to be built at home (using mill / lathe / 3D printer) and available material
  • Low-cost
  • Positioned where it is un-obstructive as possible but easy to access (we don’t want to waste precious work-area space!)


Here’s the finished sensor installed and wired on the temporary MDF bed.  I used an aluminum touch plate for now, I plan to turn a steel one and hardened it in the future

Mach4 M6 macro (tool change)

I then proceeded to write a custom M6 macro in mach4 (m6.msc)


–V0.1 – Feb 17 – Initial code – move + probing
–V0.2 – Feb 18 – Added probestart and no tool length code
–v0.3 – Feb 21 – Added machine state push + homed axis check
–v0.3 – Mar 03 – Removed machine state push and use #var instead
–v0.4 – Mar 04 – Added toolChangeZ, saving / restoring feed + force ui refresh of G-Code modals
–TODO : Probe on all -Z moves
–TODO : Ensure probe did strike

function m6()
local inst = mc.mcGetInstance();
local touchPlateXPos = -0.75; –X position (in machine coord) of the touch plate
local touchPlateYPos = 4.15; –Y position (in machine coord) of the touch plate
local toolChangeZ = -2.5; — abs Z height to move the head for manual tool change by operator
local colletToTouchPos = -5.79; –This is the z position where the end of the spinde is about to touch the sensor, leave about 0.1in clearance
local noToolLen = 1.25; –Length to use if tool lenght = 0 (make sure NO tools can be longer than this);
local probeCode = “G31.1″; –G code of the probe to use

–define motors mapping
local xAxisNb = 0;
local yAxisNb = 1;
local zAxisNb = 2;

–Track original X/Y/Z position
local orgX = mc.mcAxisGetPos(inst,xAxisNb);
local orgY = mc.mcAxisGetPos(inst,yAxisNb);
local orgZ = mc.mcAxisGetPos(inst,zAxisNb);

local requestedTool = mc.mcToolGetSelected(inst);
local currenttool = mc.mcToolGetCurrent(inst);
local currentToolLen = mc.mcToolGetData(inst,mc.MTOOL_MILL_HEIGHT,currenttool);

mc.mcCntlSetLastError(inst,”M6 – Processing tool change from”..currenttool..” to “..requestedTool);

–Ensure a tool change is needed
if requestedTool == currenttool
mc.mcCntlSetLastError(inst,”M6 – Current tool and Requested tool are the same, nothing to do”);

–Ensure valid tool length, if not, use default value
if (currentToolLen <= 0.1)
currentToolLen = noToolLen;

–Keep of copy of the current postion mode, units, feed as we are going to change them
local positionMode = mc.mcCntlGetPoundVar(inst,4003);
local units = mc.mcCntlGetPoundVar(inst,4006);
local feedRate = mc.mcCntlGetPoundVar(inst, 2134);

–Ensure we are fully homed on all axises
local xHomed = mc.mcAxisIsHomed(inst,xAxisNb);
local yHomed = mc.mcAxisIsHomed(inst,yAxisNb);
local zHomed = mc.mcAxisIsHomed(inst,zAxisNb);

if (xHomed == 0 or yHomed == 0 or zHomed == 0)
mc.mcCntlSetLastError(inst,”M6 – Tool change : At least one axis is not homed”);

–Compute the z position we need to move the head before probing
local probeStart = colletToTouchPos + currentToolLen;

mc.mcCntlGcodeExecuteWait(inst, “G20 G90 G53 G0 Z0.0”);–Move Z to safe pos
mc.mcCntlGcodeExecuteWait(inst, “G53 G0 X”..touchPlateXPos..” Y”..touchPlateYPos); –Goto x/y of touch pad

–Move Z close the touch pad
–TODO : use probing to stop on contact (safety / prevent tool breakage)
mc.mcCntlGcodeExecuteWait(inst, “G53 G0 Z”..probeStart); — Move to probing pos

–Probe current tool Z pos
mc.mcCntlGcodeExecuteWait(inst,”G91 “..probeCode..” Z-2.0 F10″); –Probe to a max of 2 inches

local didStrike = mc.mcCntlProbeGetStrikeStatus(inst);
–TODO : Ensure probe did touch

local currentToolZ = mc.mcAxisGetPos(inst,2);
local newToolDesc = mc.mcToolGetDesc(inst,requestedTool);

mc.mcCntlGcodeExecuteWait(inst, “G90 G53 G0 Z”..toolChangeZ);–Move Z back to safe pos for tool change

–Prompt user
wx.wxMessageBox(“Please change to tool number “..requestedTool..” (“..newToolDesc..”) and press ENTER to continue”);

–At this point, the tool has been changed, recalc new values
currenttool = requestedTool;
currentToolLen = mc.mcToolGetData(inst,mc.MTOOL_MILL_HEIGHT,currenttool);

if (currentToolLen <= 0.1)
currentToolLen = noToolLen;

–Calculate new tool probe Z
probeStart = colletToTouchPos + currentToolLen;

–Move Z close the touch pad
–TODO : use probing to stop on contact (safety / prevent tool breakage)
mc.mcCntlGcodeExecuteWait(inst, “G90 G53 G0 Z”..probeStart); — Move to probing pos

mc.mcCntlGcodeExecuteWait(inst,”G91 “..probeCode..” Z-2.0 F10″); –Probe to a max of 1/2 inch

mc.mcAxisSetPos(inst,2,currentToolZ); — Set Z to old tool probed Z

local rc = mc.mcToolSetCurrent(inst,requestedTool); — Change tool
local newlyToolIdx = mc.mcToolGetCurrent(inst);

mc.mcCntlGcodeExecuteWait(inst,”G90 G53 G0 Z0.0″); — Move Z back to abs 0
mc.mcCntlGcodeExecuteWait(inst,”G90 G0 X”..orgX..” Y”..orgY); — Move X/Y back to org
mc.mcCntlGcodeExecuteWait(inst,”G90 G0 Z”..orgZ); — Move Z to org pos

–Restore Position mode, units & feed

— Execute some G code to force modal label UI to refresh
— else, ui and interal settings are out of sync which can cause operator errors


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

Leave a Reply

Your email address will not be published. Required fields are marked *