Repository navigation
SD format page: survive the WDT across a soft reset, verify the mount, show the boot cause - #163
Merged
Merged
Conversation
…, show the boot cause Field report (2026-09): a new unit claimed its SD card was unformatted, the on-device format reported OK and rebooted, and the same page came straight back — until a full power cycle, after which the card mounted fine with no further format. The format had worked; the boot after it had not. Root cause: the nRF52 hardware watchdog survives NVIC_SystemReset() (only a pin, brown-out, power-on or System OFF reset clears it). Every firmware-initiated reboot therefore hands the next boot a running ~4 s WDT that setup() never fed until wdtSetup() at its very end. A clean boot fits; a slow SD init (SdFat's 2 s ACMD41 timeout, three attempts) does not, and the WDT reset the device mid-SD-transaction — leaving a card that firmware cannot reset (CS grounded, no power switch) reading as "card answers, no FAT volume" on every following soft boot. - setup() now calls wdtBootCheck() first: a WDT already running is fed immediately (wdtCarriedOver), setup() pets between every slow step (display delays, each SD.begin() attempt, the probe settle, GPS probe, camera/egg/strip init — all no-ops on a clean boot), and wdtSetup() leaves a running, register-locked WDT alone. - sdPerformFormat() requires the fresh volume to mount before "Format OK": a mount failure stays on the confirm page as "Formatted: no mount / Power-cycle the unit" instead of rebooting into an identical loop. sdFormatLastFailed becomes sdFormatFailure (NONE/ERASE/MOUNT). - SD_SETUP() re-probes the volume once after a 250 ms settle and only declares the card unformatted on consecutive no-volume probes with the card layer answering both times; a card-layer flap is "dead" (FAULT, never an erase offer). The rule is the new host-tested sd_probe unit. The recovered-on-probe path also records the SPI clock it had been leaving at 0. - wake_cause::shortName() puts the boot cause on the debug line, the format page's last line (boot:WDT err:20, with SdFat's error code) and the SD FAULT page, so a watchdog-induced loop is visible. Sim: sd_probe added to the core sources, sdErrorCode()/sdErrorData() stubbed on the SdFat shim, wdtBootCheck prototype added. Goldens unchanged (the GPS status page is untouched). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VhDTiCUTGJhV7XvogdU9CQ
Coverage — host-testable units📂 Overall coverage
📄 File coverage
|
This was referenced Sep 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Field report (2026-09): a newly wired unit claimed its SD card was unformatted, the on-device format reported OK and rebooted, and the same page came straight back. A full power cycle fixed it with no further format, so the format had worked and the boot after it had not.
Root cause: the nRF52 hardware watchdog survives
NVIC_SystemReset(). Only a pin, brown-out, power-on or System OFF reset clears it. Every firmware-initiated reboot (transfer exits, OTA, the format page, the reboot combo) therefore hands the next boot a running ~4 s WDT thatsetup()never fed untilwdtSetup()at its very end. A clean boot fits in the budget; a slow SD init (SdFat's 2 s ACMD41 timeout, three attempts) does not, and the WDT reset the device mid-SD-transaction. With CS grounded and no power switch, nothing in firmware can reset the card afterwards, so every following soft boot found "card answers, no FAT volume" and offered to format a good card.Four changes, all QOL-safe on a healthy unit:
setup()now callswdtBootCheck()first: a WDT already running is fed immediately andwdtCarriedOveris set.setup()pets between every slow step (display delays, eachSD.begin()attempt, the probe settle, GPS probe, camera/egg/strip init). Those pets are no-ops on a clean boot.wdtSetup()leaves a running, register-locked WDT alone instead of silently failing to reconfigure it.sdPerformFormat()requires the fresh volume to mount before "Format OK". A mount failure stays on the confirm page asFormatted: no mount / Power-cycle the unitinstead of rebooting into an identical loop.sdFormatLastFailedbecomessdFormatFailure(NONE / ERASE / MOUNT).SD_SETUP()re-probes the volume once after a 250 ms settle and only declares the card unformatted when the card layer answered both times and the volume mounted neither time. A card-layer flap between probes is "dead" (FAULT, never an erase offer). The rule is the new host-testedsd_probeunit. The recovered-on-probe path also records the active SPI clock it had been leaving at 0.wake_cause::shortName()puts the cause on the debug line, on the format page's last line (boot:WDT err:20, with SdFat's last card error code) and on the SD FAULT page. AWDTtag there is the confirming evidence for this failure mode.Type of change
How it was verified
ctest --test-dir tests/build): 623 cases, 327632 assertions, including the newsd_probeandwake_cause::shortNametestsclang-tidyclean onsd_probe.cppandwake_cause.cpp.inosources and its full suite passes (boot soak, determinism, golden fixtures unchanged, both lap oracles, two-session carryover)Checklist
CHANGELOG.mdupdated under[Unreleased]ARCHITECTURE.md/CLAUDE.mdupdated (file map, subsystem 4 format flow, subsystem 10 WDT note, key constants)tests/(sd_probe_test.cpp,wake_cause_test.cpp)Related issues
None filed. Reference for the WDT behaviour: Nordic DevZone, "use initialized WDT after software reset on a NRF52" (a Nordic engineer: "Soft reset doesn't clear WDT").
🤖 Generated with Claude Code
https://claude.ai/code/session_01VhDTiCUTGJhV7XvogdU9CQ
Generated by Claude Code