Mach.home: defer external A homing until AVR axes finish

Previously the homing loop iterated zxyabc and processed each axis
in turn, but the AVR axes (Z/X/Y) just queue G-code to the planner
and return immediately - the gantry keeps moving in the background.
The external A homing was then driven synchronously on the same
loop iteration, which meant the W stepper started its limit-seek
*while the gantry was still actively homing Z/X/Y*. Visually
confusing and unsafe.

Split into two phases:

  1. The HTTP handler thread queues every AVR axis (no change) and
     collects external axes into a deferred list.
  2. A background thread polls cycle until it returns to 'idle'
     (signalling the AVR finished its queued homing). It then runs
     each external axis home in order, blocking on the ESP serial
     RPC. Post-home bookkeeping (set_axis to AVR, planner G92, cycle
     reset) is scheduled back onto the IOLoop via add_callback so
     gplan and the AVR command queue are only touched from one
     thread.

A guard prevents overlapping threads if Home is clicked again while
the previous deferred run is still waiting.
This commit is contained in:
2026-05-03 13:24:54 +02:00
parent ca7e30aa05
commit 4a494a101d
3 changed files with 178 additions and 62 deletions

View File

@@ -215,8 +215,6 @@ script#control-view-template(type="text/x-template")
div Position
div Absolute
div Offset
div State
div Toolpath
.actions-cell
// Master Home All. Each row's Actions cell has a per-axis
// home button; this header-level button homes every
@@ -230,31 +228,24 @@ script#control-view-template(type="text/x-template")
each axis in 'xyzabc'
.dro-row(:class=`${axis}.klass + ' ' + ${axis}.tklass`,
v-if=`${axis}.enabled`,
:title=`${axis}.title`)
:title=`${axis}.toolmsg ? (${axis}.title + ' — ' + ${axis}.toolmsg) : ${axis}.title`)
.dro-axis(:class=`'axis-' + '${axis}'`)= axis.toUpperCase()
.dro-pos: unit-value(:value=`${axis}.pos`, precision=4)
.dro-sec: unit-value(:value=`${axis}.abs`, precision=3)
.dro-sec: unit-value(:value=`${axis}.off`, precision=3)
.dro-state
span.chip(:class=`${axis}.tklass.indexOf('error') !== -1 ? 'chip-red' : (${axis}.homed ? 'chip-green' : 'chip-amber')`)
.fa(:class=`'fa-' + ${axis}.icon`)
|  {{#{axis}.state}}
.dro-toolpath
span.chip(:class=`${axis}.tklass.indexOf('error') !== -1 ? 'chip-red' : (${axis}.tklass.indexOf('warn') !== -1 ? 'chip-amber' : 'chip-green')`,
@click=`showToolpathMessageDialog('${axis}')`)
.fa(:class=`'fa-' + ${axis}.ticon`)
|  {{#{axis}.tstate}}
.actions-cell
button.icon-btn(:disabled="!can_set_axis",
:title=`'Set ${axis.toUpperCase()} axis position.'`,
@click=`show_set_position('${axis}')`)
.fa.fa-gear
button.icon-btn(:disabled="!can_set_axis",
:title=`'Zero ${axis.toUpperCase()} axis offset.'`,
button.icon-btn(:class=`${axis}.tklass.indexOf('error') !== -1 ? 'state-red' : (${axis}.tklass.indexOf('warn') !== -1 ? 'state-amber' : 'state-green')`,
:disabled="!can_set_axis",
:title=`${axis}.toolmsg || ('Zero ${axis.toUpperCase()} axis offset.')`,
@click=`zero('${axis}')`)
.fa.fa-location-dot
button.icon-btn(:disabled="!is_idle",
:title=`'Home ${axis.toUpperCase()} axis.'`,
button.icon-btn(:class=`${axis}.klass.indexOf('error') !== -1 ? 'state-red' : (${axis}.homed ? 'state-green' : 'state-amber')`,
:disabled="!is_idle",
:title=`${axis}.title`,
@click=`home('${axis}')`)
.fa.fa-home
@@ -270,20 +261,13 @@ script#control-view-template(type="text/x-template")
.dro-pos: unit-value(:value="w.pos", precision=4)
.dro-sec: unit-value(:value="w.abs", precision=3)
.dro-sec —
.dro-state
span.chip(:class="w.homed ? 'chip-green' : 'chip-amber'")
.fa(:class="'fa-' + w.icon")
|  {{w.state}}
.dro-toolpath
span.chip.chip-green
.fa(:class="'fa-' + w.ticon")
|  {{w.tstate}}
.actions-cell
button.icon-btn(disabled, style="visibility:hidden")
.fa.fa-gear
button.icon-btn(disabled, style="visibility:hidden")
.fa.fa-location-dot
button.icon-btn(:disabled="!w.enabled",
button.icon-btn(:class="w.homed ? 'state-green' : 'state-amber'",
:disabled="!w.enabled",
title="Home W axis.", @click="aux_home()")
.fa.fa-home