Skip to content

amyboard web: warn + abort when the board doesn't respond; detect + reboot a wedged board#1060

Closed
bwhitman wants to merge 3 commits into
mainfrom
claude/amyboard-write-disconnected-warning
Closed

amyboard web: warn + abort when the board doesn't respond; detect + reboot a wedged board#1060
bwhitman wants to merge 3 commits into
mainfrom
claude/amyboard-write-disconnected-warning

Conversation

@bwhitman

@bwhitman bwhitman commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

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:

Can't reach your AMYboard
Your AMYboard didn't respond. Make sure it's connected and you've pressed RST after upgrading the firmware, then try again.

Why

check_amyboard_firmware() waited for the board's zI ready reply, got none, and deliberately did return 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:

wait_for_board_ready: timed out after 5000ms
[fwdetect] board not ready (no zI OK) — connection issue, not warning
_send_text_file_to_amyboard: sending header: zT/user/current/sketch.py,265Z
sent 265 bytes to /user/current/sketch.py

How

Two changes in check_amyboard_firmware() (tulip/amyboardweb/static/spss.js):

  1. Move the wait_for_board_ready() check above the _fw_warned gate. 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.
  2. On no reply, show a disconnected warning and abort (return false) instead of proceeding. New branch in show_firmware_warning:
    • Dismiss button relabeled OK → aborts the op, and never sets _fw_warned so the next attempt re-checks.
    • Update Firmware still jumps to the Upgrade tab (handy if they just flashed and forgot RST).
    • Reuses the existing fwWarnModal element — no HTML change.

Covers both write_sketch_to_amyboard and read_sketch_from_amyboard (they share check_amyboard_firmware).

Cost when connected

wait_for_board_ready resolves on the first zI (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 --check passes.
  • Manual (PR preview site): in control mode with the AMYboard unplugged, click Write to AMYboard → expect the "Can't reach your AMYboard" modal and an aborted write (no "success"). Plug in + RST, retry → expect a normal write. Repeat for Read from AMYboard.

🤖 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 answers zI (handled in pure C on the MIDI task) while never ACKing a zP (the MicroPython scheduler is starved). Such a board passed the reachability gate, timed out on the report_version probe, 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() in tools/amyworld_recorder/amyboard_link.py:

  • zp_liveness_ping() — sends an inert zPpassZ and requires the AK ACK (2.5 s, one retry). Runs on every Write/Read, before the _fw_warned gate (a board can wedge mid-session). Sub-second on a healthy board.
  • New wedged modal branch"Your AMYboard is stuck" with a Reboot AMYboard action: sends zB (also pure C, so it gets through while wedged), waits for USB re-enumeration via the existing wait_for_board_ready(), then 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 page 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.

Testing (follow-up)

  • node --check passes.
  • Manual (PR preview): in control mode, write a sketch with while True: pass in loop() 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.
  • Healthy board: Write/Read behave as before (one extra sub-second zP round-trip).

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>
@github-actions

Copy link
Copy Markdown

🔌 AMYboard PR preview

Editor + 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>
@bwhitman bwhitman changed the title amyboard web: warn + abort Write/Read when the board doesn't respond amyboard web: warn + abort when the board doesn't respond; detect + reboot a wedged board Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

🎛️ HW CI (physical bench)

AMYboard (USB-MIDI + AMY zP → audio; built-in tones + AMYboard World sketches acid/house/universal_hair/woodpiano over the SysEx control API): ✅ PASS — flashed this PR’s firmware; all checks matched the references.

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 ref/hwci_basic.wav, the AMYboard World sketch refs (ref/{acid_generator,house_generator,universal_hair,woodpiano}.wav) + ref/tulip_basic.wav; Tulip screenshot pixel-compared to ref/tulip_screenshot.png. Both analog outs share one capture card, so the tests run sequentially.

@bwhitman

bwhitman commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Merged into main as part of #1084 (merge commit 7eacd0a).

@bwhitman bwhitman closed this Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant