fix(filter-wheel): reset W/W2 range to full after home (rotary axis) — fixes intermittent post-home CMD_EXECUTION_ERROR#560
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the Teensy filter-wheel (W/W2) homing state machine to guarantee a fresh hardware latch capture on every home cycle, addressing an intermittent post-home MOVETO_W rejection (CMD_EXECUTION_ERROR) caused by a stale X_LATCH_RD when homing begins with the switch already pressed.
Changes:
- Add
is_backing_off_W/is_backing_off_W2global state to represent a new “back off first” homing phase. - Implement
back_off_homing_w()/back_off_homing_w2()and invoke them each loop before the existingprepare_homing_*phase. - Update HOME command entry for W/W2 to start in back-off when the switch is pressed, and clear the new flags on RESET; add accompanying design/spec documentation.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| software/docs/superpowers/specs/2026-06-16-filter-wheel-home-latch-edge-design.md | Adds approved design writeup describing the latch-edge mismatch and the back-off-first approach. |
| software/docs/superpowers/plans/2026-06-16-filter-wheel-home-latch-edge.md | Adds implementation plan and validation steps for the firmware change. |
| firmware/controller/src/globals.cpp | Defines new global homing-state flags for W/W2 back-off. |
| firmware/controller/src/globals.h | Exposes new back-off flags via extern declarations. |
| firmware/controller/src/operations.h | Declares new back-off phase functions. |
| firmware/controller/src/operations.cpp | Implements back_off_homing_w/w2() to transition from pressed-start to normal prepare homing. |
| firmware/controller/main_controller_teensy41.ino | Runs back-off phase functions each loop prior to prepare_homing_*. |
| firmware/controller/src/commands/stage_commands.cpp | Switches pressed-start HOME entry to set back-off state (and resets flag at entry). |
| firmware/controller/src/commands/commands.cpp | Clears new back-off flags on RESET alongside existing homing flags. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
8a26f64 to
a12dff6
Compare
| // range-rejected. Safe: W xmin/xmax are written only in check_homing_w and | ||
| // consumed only by the moveTo range check (check_limits clamps X/Y/Z only). |
There was a problem hiding this comment.
[Claude Code] Fixed in 0475d2f — reworded to: "check_limits acts on X/Y/Z only, stopping them on a limit-switch event, and never touches W/W2". You're right: it doesn't clamp positions, it stops X/Y/Z motion on a limit-switch event and ignores W/W2.
| // Version 1.4 = filter-wheel (W/W2) home resets xmin/xmax to full range (rotary | ||
| // axis has no travel end-stop); fixes intermittent post-home | ||
| // MOVETO_W CMD_EXECUTION_ERROR (home left xmin = L - C > offset) |
There was a problem hiding this comment.
[Claude Code] Updated the PR title + description to match the code. The back-off state machine (is_backing_off_, back_off_homing_) was intentionally removed: hardware testing showed it only fixed the pressed-at-entry subcase — a released-at-entry home still failed (firmware v1.4 back-off build) — so it was dropped in favor of this version-independent root-cause fix (reset the rotary wheel's xmin/xmax to full range after home). The implemented diff is exactly that + the version bump; no missing code.
… v1.4 Intermittent: the filter-wheel home reports success but the post-home offset move (absolute +102 usteps) is rejected with CMD_EXECUTION_ERROR. Root cause: the home anchors its coordinate frame from the limit-switch PRESS latch (X_LATCH_RD) but detects "home found" on the switch RELEASE, leaving xmin = L - C (L = press-latch position, C = release position). tmc4361A_moveTo is the sole reject gate (x_pos < xmin), so the abort means xmin > 102, which happens whenever a home reaches release without a fresh, correctly-ordered in-cycle press (stale/mis-ordered latch). Depends on the wheel's resting state, hence intermittent. Fix at the right altitude: the [xmin,xmax] range check is linear-stage infrastructure (physical end-stops, SET_LIM, moveToExtreme). The filter wheel is rotary with no travel end-stop. finalize_homing_w/_w2 now reset xmin/xmax to full int32 range after anchoring home, so a latch-derived xmin can never reject a valid rotary move -- every entry path. Safe: W/W2 xmin/xmax are written only in check_homing_w/w2 and consumed only by the moveTo gate (check_limits clamps X/Y/Z only; SET_LIM targets the stage). FIRMWARE_VERSION 1.3 -> 1.4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a12dff6 to
0475d2f
Compare
Problem
Intermittently, a filter-wheel home succeeds but the immediate post-home offset move (
MOVETO_W, an absolute+102ustep target) is rejected withCMD_EXECUTION_ERROR, crashing startup. Observed across runs; ~1 in N power-ups.Root cause
The home anchors its coordinate frame from the limit-switch PRESS latch (
X_LATCH_RD) but declares "home found" on the switch RELEASE, leaving finalxmin = L − C(L = press-latch position, C = release position).tmc4361A_moveTois the sole rejection gate (x_pos < xmin || x_pos > xmax; no transient path;enable_filterwheel == falseruled out post-INITFILTERWHEEL). So the abort is exactlyxmin > 102, which happens whenever a home reaches release without a fresh, correctly-ordered in-cycle press (stale/mis-ordered latch). It's intermittent becauseL − Cdepends on the wheel's resting/switch state at power-up.(Confirmed by a multi-agent adversarial root-cause analysis:
xmin = L − C, absolute+102target, sole gate — all verified against source.)Fix
The
[xmin,xmax]range check is linear-stage infrastructure (physical end-stops,SET_LIM,moveToExtreme). The filter wheel is rotary — no travel end-stop; the home switch is a reference mark. Sofinalize_homing_w/_w2now reset the wheel's range to full int32 after anchoring home, and a latch-derivedxmincan never reject a valid rotary move — on every home entry path.Safe: W/W2
xmin/xmaxare written only incheck_homing_w/w2and consumed only by themoveTorange gate;check_limitsacts on X/Y/Z only and never touches W/W2;SET_LIMtargets the stage.FIRMWARE_VERSION1.3 → 1.4 so logs (Detected firmware version) confirm the flashed build.Changes
firmware/controller/src/operations.cpp—finalize_homing_w/_w2resetxmin/xmaxto full rangefirmware/controller/src/constants.h— version 1.4 + changelogNote
An earlier iteration tried a "back-off, then re-approach" homing phase to force a fresh press-latch. Hardware testing showed it only fixed the pressed-at-entry subcase — a released-at-entry home still failed — so it was dropped in favor of this version-independent, root-cause fix.
Testing
pio run -e teensy41builds clean.Detected firmware version: (1, 4), then power-cycle ~10+ times (varying the wheel's resting position) and confirm every home completes with noCMD_EXECUTION_ERROR.🤖 Generated with Claude Code