Skip to content

fix: transfer-mode exit bugs + refresh wasm sim harness (4.0.1) - #141

Merged
TheAngryRaven merged 2 commits into
BETAfrom
claude/transfer-mode-reboot-bugs-q008ew
Aug 12, 2026
Merged

fix: transfer-mode exit bugs + refresh wasm sim harness (4.0.1)#141
TheAngryRaven merged 2 commits into
BETAfrom
claude/transfer-mode-reboot-bugs-q008ew

Conversation

@TheAngryRaven

@TheAngryRaven TheAngryRaven commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Summary

Two 4.0.0 transfer-mode exit bugs plus a browser-sim harness refresh, slated for 4.0.1 (see the new [Unreleased] CHANGELOG section):

  • USB exit could hang and come back via the watchdog instead of the clean reboot. USB_MSC_DISABLE() raced the USBD task: setUnitReady(false) only refuses new SCSI commands, and the pre-reset quiesce stamped write-callback entry times only — reads were never tracked at all. An in-flight READ10/WRITE10 (a single writeSectors() can stall 100 ms–2 s inside SD garbage collection, blowing past both the 100 ms quiet window and the 1 s cap) was still driving SdFat on the USBD task while the main loop ran syncDevice() on the same SPI bus — two tasks in SdFat wedged the card, and the ~4 s WDT fired. The exit now detaches USB first (so host traffic actually stops), tracks an in-flight flag + completion time around all three block callbacks (reads and flush included), and drains WDT-fed for up to 4 s before syncing and resetting.
  • Manual Bluetooth exit now reboots. Only a phone disconnect triggered the transfer auto-reboot; the on-device Exit button just BLE_STOP()'d back to the menu, so settings written over BLE silently didn't apply until the next power cycle. New bleExitTransferMode() (BLE_STOP() + the same 100 ms-delay NVIC_SystemReset()) is wired into both exit sites, so both ways out of both transfer modes reboot. The SIM stub returns after stopping the radio so the golden menu walk still exits the Bluetooth page.
  • wasm sim harness caught up with the firmware (sim/wasm/test.html, no firmware code): dovex playback filtered rows to exactly 13 columns, so 4.0.0 logs (16 columns after Temp1/Junction1/Temp2) injected nothing — now accepts ≥13. And a new "GPS fix" toggle + mph field streams a deterministic synthetic 25 Hz fix parked on the bundled OKC track's start line (one PVT per ≤40 ms step slice — a burst would starve the creator's ≥8-fix averaging hold), making fix-gated flows reachable in the browser — most usefully the course creator, which previously couldn't be entered in the sim at all.

FIRMWARE_VERSION stays at 4.0.0 — the version stamp happens at release cut per convention.

Type of change

  • Bug fix (no user-visible behavior change beyond the fix)
  • New feature / behavior
  • Refactor (no behavior change)
  • Tests only
  • CI / tooling / docs (wasm harness refresh + build-wasm/ gitignore)
  • Breaking change (track files, log format, BLE protocol, or a removed mode)

How it was verified

  • Host unit tests pass (ctest --test-dir tests/build)
  • clang-tidy clean — left to CI (no local toolchain in this session)
  • Compiles for the XIAO nRF52840 Sense — left to CI (no arduino-cli in this session; changes only use symbols already used in the same files)
  • Tested on real hardware — not tested here; the USB race needs a host mid-transfer at the moment of exit (best repro: press Exit while a large file copy is running)

Also verified: all 16 sim golden fixtures (the walk exits the Bluetooth page through the changed handler), the 60 s boot soak, and both lap oracles (synthetic + hardware .dovex) pass unchanged. The wasm artifact was rebuilt locally with CI's exact toolchain (emsdk 3.1.61, DovesLapTimer BETA): node smoke passes, and a scripted walk using the harness's exact injection pattern reaches the course creator through the OKC track prompt and commits a real 3 s point-averaging hold.

Checklist

  • CHANGELOG.md updated under [Unreleased] (if user-visible)
  • ARCHITECTURE.md / CLAUDE.md updated (if a module or interface changed)
  • New testable logic has a matching test in tests/ — none feasible: the firmware fixes are TinyUSB/Bluefruit-bound glue (matching the existing "no pure unit test" posture for these modules) and the harness is browser-side JS
  • Branch is focused — the two firmware fixes share one concern (transfer-mode exits); the harness refresh is an isolated commit touching only sim/wasm/test.html + docs

Related issues

Field reports: USB-exit crash recovered by watchdog; BLE settings not applying after manual Exit.

🤖 Generated with Claude Code

https://claude.ai/code/session_014rqEnPQUqhMj98sgkefJkz

claude added 2 commits August 11, 2026 23:10
…n manual BLE exit

Two 4.0.0 transfer-mode exit bugs, slated for 4.0.1:

USB: USB_MSC_DISABLE() raced the USBD task. setUnitReady(false) only
refuses NEW SCSI commands, and the exit quiesce tracked write-callback
ENTRY times only — so an in-flight READ10/WRITE10 (reads were never
tracked at all, and a writeSectors() can stall 100 ms–2 s on SD garbage
collection) was still driving SdFat on the USBD task while the main loop
ran syncDevice() on the same SPI bus. The wedge came back via the ~4 s
watchdog instead of the clean reset. Now the exit detaches USB first (so
host traffic actually stops), tracks in-flight state + completion time
around all three block callbacks, and drains WDT-fed for up to 4 s
before syncing and resetting.

BLE: only a phone disconnect triggered the transfer auto-reboot; the
on-device Exit button just BLE_STOP()'d back to the menu, so settings
written over BLE silently didn't apply until the next power cycle. New
bleExitTransferMode() (BLE_STOP + the same 100 ms-delay reset) makes
both ways out of transfer mode reboot, matching USB. The SIM stub
returns after stopping so the golden menu walk still exits the
Bluetooth page.

Both fixes are TinyUSB/Bluefruit-bound (no host-testable pure logic);
goldens, boot soak, and lap oracles verified unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014rqEnPQUqhMj98sgkefJkz
…ggle

The browser test harness (wasm/test.html) had fallen behind the firmware:

- dovex playback filtered rows to exactly 13 columns, so 4.0.0 logs
  (16 columns after Temp1/Junction1/Temp2) injected nothing. Accept >=13
  and read the stable first 13.
- Nothing could satisfy the course creator's fix + time-lock entry gate
  interactively. New "GPS fix" toggle + mph field: a deterministic
  synthetic 25 Hz fix parked on the bundled OKC track's start line,
  injected one PVT per <=40 ms step slice (a burst before one big step
  collapses into a single fix and starves the creator's >=8-fix
  averaging hold). Loading a dovex unchecks the toggle — playback owns
  the GPS feed.

Verified against a fresh emsdk 3.1.61 wasm build (DovesLapTimer BETA,
matching this PR's CI): node smoke passes, and a scripted walk using the
harness's exact injection pattern reaches the creator through the OKC
track prompt and commits a real 3 s point-averaging hold.

Also ignore build-wasm/ (the emcmake build dir CI uses).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014rqEnPQUqhMj98sgkefJkz
@github-actions

Copy link
Copy Markdown

Coverage — host-testable units

📂 Overall coverage

Metric Coverage
Lines 🟢 1248/1270 (98.3%)
Functions 🟢 133/133 (100.0%)
Branches 🟡 949/1056 (89.9%)

📄 File coverage

File Lines Functions Branches
BirdsEye/camera_fsm.cpp 🟢 238/246 (96.7%) 🟢 20/20 (100.0%) 🟡 142/160 (88.8%)
BirdsEye/course_creator.cpp 🟢 209/217 (96.3%) 🟢 21/21 (100.0%) 🟡 114/131 (87.0%)
BirdsEye/course_prune.cpp 🟢 37/37 (100.0%) 🟢 5/5 (100.0%) 🟢 47/50 (94.0%)
BirdsEye/crc32.cpp 🟢 30/30 (100.0%) 🟢 4/4 (100.0%) 🟢 24/24 (100.0%)
BirdsEye/crossing_pattern.cpp 🟢 15/15 (100.0%) 🟢 1/1 (100.0%) 🟢 12/12 (100.0%)
BirdsEye/dovex_header.cpp 🟢 106/107 (99.1%) 🟢 7/7 (100.0%) 🔴 62/88 (70.5%)
BirdsEye/filename_validator.cpp 🟢 14/14 (100.0%) 🟢 1/1 (100.0%) 🟢 30/30 (100.0%)
BirdsEye/gps_stats.cpp 🟢 25/25 (100.0%) 🟢 3/3 (100.0%) 🟢 8/8 (100.0%)
BirdsEye/gps_status_page.cpp 🟢 29/29 (100.0%) 🟢 4/4 (100.0%) 🟢 28/28 (100.0%)
BirdsEye/gps_time.cpp 🟢 45/45 (100.0%) 🟢 6/6 (100.0%) 🟢 30/32 (93.8%)
BirdsEye/gps_validation.cpp 🟢 24/24 (100.0%) 🟢 2/2 (100.0%) 🟢 66/66 (100.0%)
BirdsEye/haversine.cpp 🟢 8/8 (100.0%) 🟢 1/1 (100.0%) ⚫ 0/0 (0.0%)
BirdsEye/idle_policy.cpp 🟢 17/17 (100.0%) 🟢 2/2 (100.0%) 🟢 14/14 (100.0%)
BirdsEye/insta360_protocol.cpp 🟢 140/140 (100.0%) 🟢 16/16 (100.0%) 🟡 86/98 (87.8%)
BirdsEye/lap_format.cpp 🟢 18/18 (100.0%) 🟢 1/1 (100.0%) 🟢 9/9 (100.0%)
BirdsEye/sat_bars.cpp 🟢 33/33 (100.0%) 🟢 2/2 (100.0%) 🟢 51/54 (94.4%)
BirdsEye/sd_access_policy.cpp 🟢 9/9 (100.0%) 🟢 3/3 (100.0%) 🟢 18/18 (100.0%)
BirdsEye/sd_format_page.cpp 🟢 25/25 (100.0%) 🟢 3/3 (100.0%) 🟢 25/26 (96.2%)
BirdsEye/sensoregg_protocol.cpp 🟢 44/45 (97.8%) 🟢 7/7 (100.0%) 🟢 33/34 (97.1%)
BirdsEye/sprint_select.cpp 🟢 25/25 (100.0%) 🟢 4/4 (100.0%) 🟢 46/48 (95.8%)
BirdsEye/tach_filter.cpp 🟢 27/27 (100.0%) 🟢 6/6 (100.0%) 🟢 17/18 (94.4%)
BirdsEye/track_json.cpp 🟢 116/120 (96.7%) 🟢 12/12 (100.0%) 🟡 67/88 (76.1%)
BirdsEye/wake_cause.cpp 🟢 14/14 (100.0%) 🟢 2/2 (100.0%) 🟢 20/20 (100.0%)

@TheAngryRaven TheAngryRaven changed the title fix: transfer-mode exits — drain USB block I/O before reset, reboot on manual BLE exit (4.0.1) fix: transfer-mode exit bugs + refresh wasm sim harness (4.0.1) Aug 11, 2026
@TheAngryRaven
TheAngryRaven merged commit 36b06b8 into BETA Aug 12, 2026
8 checks passed
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.

2 participants