amyboard web: warn + abort when the board doesn't respond; detect + reboot a wedged board#1060
amyboard web: warn + abort when the board doesn't respond; detect + reboot a wedged board#1060bwhitman wants to merge 3 commits into
Conversation
In control mode, "Write to AMYboard" against a disconnected/unpowered
board (or one that needs RST after a firmware flash) appeared to succeed:
it spun, then returned with no error. check_amyboard_firmware() got no zI
reply and deliberately `return true` ("connection issue, not warning"),
so the fire-and-forget sketch transfer ran against nothing and surfaced
no failure.
Two fixes in check_amyboard_firmware():
- Move the wait_for_board_ready() check ABOVE the _fw_warned gate, so the
board's responsiveness is verified on every Write/Read — not skipped
for the rest of the session once a firmware notice was acknowledged.
- On no reply, show a 'disconnected' warning modal and abort (return
false) instead of proceeding. The modal reads "Can't reach your
AMYboard — make sure it's connected and you've pressed RST after
upgrading the firmware, then try again." Its dismiss button is "OK"
(aborts; never sets _fw_warned, so the next attempt re-checks), and
"Update Firmware" still jumps to the Upgrade tab.
This covers both write_sketch_to_amyboard and read_sketch_from_amyboard,
which share check_amyboard_firmware. When the board IS connected,
wait_for_board_ready resolves on the first zI (sub-second on the macOS
happy path), so the added per-op cost is negligible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🔌 AMYboard PR previewEditor + flasher: https://amyboard-pr-1060.vercel.app/editor/ This preview bundles this PR's firmware — its flasher only flashes this build (not the release). Rebuilt on every push; removed when the PR closes. The hardware CI has been kicked off and should return within a few minutes, stand by! |
A board wedged by a runaway sketch loop() still answers the zI ping (handled in pure C on the MIDI task) but never ACKs a zP, because the MicroPython scheduler is starved. The pre-Write firmware check therefore passed the reachability gate, timed out on the report_version probe, and misreported the stuck board as "firmware out of date" — seen in the field on a board running the latest firmware. Continuing past that modal then "wrote" at 5s per un-ACKed chunk and silently changed nothing. Add a zP liveness gate between the zI check and the version probe, mirroring ping()/recover() in tools/amyworld_recorder/amyboard_link.py: - zp_liveness_ping(): send an inert zPpassZ and require the AK ACK (2.5s, one retry). Runs on every Write/Read, before the _fw_warned gate, since a board can wedge mid-session. - New 'wedged' branch in show_firmware_warning: "Your AMYboard is stuck", with a Reboot AMYboard action that sends zB (also pure C, so it gets through while wedged), waits for re-enumeration via wait_for_board_ready(), and resumes the pending op — a Write then replaces the stuck sketch. Cancel aborts and never sets _fw_warned. - Windows Chrome can't survive the zB USB reboot in-document, so the recovery reuses _zb_then_reload_with_upload_context to stash the pending Write's sketch across the reload; _upload_sketch_post_bootloader grows a restart:'transfer_done' variant so the resumed Write keeps environment_transfer_done semantics (sequencer restart included). - write_sketch_to_amyboard now finalizes the sketch text (knob-log flushes) before the board check so the recovery path can carry it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🎛️ HW CI (physical bench)AMYboard (USB-MIDI + AMY Tulip (TULIP4_R11; serial-REPL audio + WiFi screenshot): ✅ PASS — flashed this PR’s firmware; all checks matched the references. ⬇️ Artifacts: recordings · screenshot · serial logs · run logs Self-hosted bench. Audio spectral-compared to |
What
In control mode, doing Write to AMYboard (or Read) against a board that's unplugged / unpowered / hasn't been RST'd after a firmware flash currently looks like it works: it spins for a few seconds and returns with no error.
Now it shows a clear warning and aborts:
Why
check_amyboard_firmware()waited for the board'szIready reply, got none, and deliberately didreturn true("connection issue, not warning"). The caller then ran the fire-and-forget sketch transfer over sysex — which has no ACK — so it completed silently against a board that wasn't there. From the JS console in the report:How
Two changes in
check_amyboard_firmware()(tulip/amyboardweb/static/spss.js):wait_for_board_ready()check above the_fw_warnedgate. Previously, once any firmware notice was acknowledged this session, the entire check (including connection) was short-circuited — so a board that dropped offline mid-session was never caught. Responsiveness is now verified on every Write/Read.disconnectedwarning and abort (return false) instead of proceeding. New branch inshow_firmware_warning:_fw_warnedso the next attempt re-checks.fwWarnModalelement — no HTML change.Covers both
write_sketch_to_amyboardandread_sketch_from_amyboard(they sharecheck_amyboard_firmware).Cost when connected
wait_for_board_readyresolves on the firstzI(sub-second on the macOS happy path), so running it on every op adds negligible latency. Only a genuinely silent board waits the full 5s — which is the case we're now surfacing.Testing
node --checkpasses.🤖 Generated with Claude Code
Follow-up commit: detect a wedged board and offer a zB reboot recovery
The disconnected check above catches a fully silent board — but a board wedged by a runaway sketch
loop()still answerszI(handled in pure C on the MIDI task) while never ACKing azP(the MicroPython scheduler is starved). Such a board passed the reachability gate, timed out on thereport_versionprobe, and got misreported as "firmware out of date" — seen in the field on a board running the latest firmware. Continuing past that modal then "wrote" at 5 s per un-ACKed chunk and silently changed nothing.This adds a zP liveness gate between the zI check and the version probe, mirroring
ping()/recover()intools/amyworld_recorder/amyboard_link.py:zp_liveness_ping()— sends an inertzPpassZand requires theAKACK (2.5 s, one retry). Runs on every Write/Read, before the_fw_warnedgate (a board can wedge mid-session). Sub-second on a healthy board.wedgedmodal branch — "Your AMYboard is stuck" with a Reboot AMYboard action: sendszB(also pure C, so it gets through while wedged), waits for USB re-enumeration via the existingwait_for_board_ready(), then resumes the pending op — a Write then replaces the stuck sketch. Cancel aborts and never sets_fw_warned._zb_then_reload_with_upload_contextto stash the pending Write's sketch across the page reload;_upload_sketch_post_bootloadergrows arestart:'transfer_done'variant so the resumed Write keepsenvironment_transfer_donesemantics (sequencer restart included).write_sketch_to_amyboardnow finalizes the sketch text (knob-log flushes) before the board check so the recovery path can carry it.Testing (follow-up)
node --checkpasses.while True: passinloop()to wedge the board, then click Write to AMYboard → expect the "Your AMYboard is stuck" modal (NOT the firmware notice). Reboot AMYboard → board reboots skipping the sketch, and the pending Write completes, replacing the stuck sketch. Cancel → write aborted; next Write re-detects.