Well. An update.
I have written a very successful LUA script that attempts to load the head 5 times, putting it back if the connection is not found. It works extremely well. I will release a final version in time but there is a story on this.
I changed the magnet arrangements in the UTM head and redesigned the magnet arrangement on the tool head and raised the screws to make better connection with the Pogo pins. It now snaps into place with a satisfying click.
I wrote a soak test script which was fun. 100 head mounts and dismounts. This is where I discovered the find_home() LUA script is coded backwards performing X,Y then Z instead of Z,Y,then X as per the documentation. I found this out after finding damaged plants as the head was dragged across the garden bed as I had a find_home() every 5 mounts.
The script ran successfully 70 times then suffered X axis stall (Watching the log files). I found that one of the wheels on the bottom of the gantry had shattered, so I never make it to 100 successful sequential mounts and dismounts. I still think 70 is a new record for me. The combination of the new magnet arrangement and LUA code that retrys in a very nice way really made the difference.
I have wondered, are there lots of people with this unreliable head detection issue or is it only a few that suffer.
For those who are interested, here is a version of the code I have written. I will be publishing it on its own, when I have completed 100 successful tests.
-- === CONFIGURATION ===
local safe_z = 0 -- Safe Z height before mount attempt
local max_retries = 5 -- Max number of mount attempts
local fail_x = 0 -- Fallback position if all retries fail
local fail_y = 1206
local debug = true
-- === READ TOOL FROM VARIABLE (Tool type only) ===
local tool_input = variable("tool_to_mount")
if not tool_input or not tool_input.tool_id then
toast("ERROR: No valid tool slot provided")
emergency_lock()
return
end
-- Extract the actual tool’s ID from the ToolSlot table
local requested_tool_id = tool_input.tool_id
local slot_x = tool_input.x
local slot_y = tool_input.y
local slot_z = tool_input.z
if not (slot_x and slot_y and slot_z) then
toast("ERROR: Tool slot coords invalid")
emergency_lock()
return
end
-- === PRE-CHECK: IS THE REQUESTED TOOL ALREADY MOUNTED? ===
local raw = get_device("mounted_tool_id")
local mounted_tool_id = (type(raw) == "table") and raw.value or raw
if mounted_tool_id then
if mounted_tool_id == requested_tool_id then
toast("Requested tool already mounted.")
return
else
toast("Other tool mounted. Dismounting...")
dismount_tool()
wait(1000)
end
end
-- === FUNCTION: FORCE TOOL BACK INTO SLOT TO RESET PHYSICAL STATE ===
function force_unmount_tool()
toast("Forcing tool into slot...")
move_absolute(slot_x, slot_y, safe_z)
wait(500)
move_absolute(slot_x, slot_y, slot_z)
wait(500)
move_absolute(slot_x, slot_y, 100)
wait(500)
end
-- === FUNCTION: MOUNT TOOL WITH SAFE RETRIES ===
function mount_tool_safely()
local attempts = 0
local mounted = false
while attempts < max_retries do
attempts = attempts + 1
if debug then
toast("Mount attempt " .. tostring(attempts))
end
local pos = get_position()
if pos and pos.x and pos.y then
move_absolute(pos.x, pos.y, safe_z)
else
toast("ERROR: Unable to get pos")
emergency_lock()
return
end
mount_tool(tool_input)
wait(500)
if verify_tool() then
toast("Tool mounted OK")
mounted = true
break
else
toast("Fail " .. tostring(attempts))
wait(1000)
if attempts < max_retries then
force_unmount_tool()
wait(1000)
end
end
end
if not mounted then
toast("Failed after " .. tostring(max_retries))
move_absolute(fail_x, fail_y, safe_z)
wait(500)
toast("Emergency stop")
emergency_lock()
end
end
-- === EXECUTE ===
mount_tool_safely()