fix: latch the watchdog trip until an explicit clear - #14
Open
CameronBrooks11 wants to merge 1 commit into
Open
Conversation
on_crumbs_message ran for every CRC-valid inbound command frame and cleared wdTripped. Any traffic at all released the trip. The controller's e-stop ladder drives its safe state as ordinary command frames. So on a board holding a watchdog brake, pressing e-stop cleared the trip, motorControlLogic left the `eStop || wdTripped` hold, and the OPEN_LOOP write(0) that followed released the brake — LMD18200::write() calls releaseBrake() before driving. The emergency stop left the machine LESS stopped than it found it (anolishq/anolis#261). A trip means the master went away while this board was driving something. Traffic arriving afterwards proves the master is back; it does not prove anyone decided it is safe to release a brake holding a load. Those are different facts and only the second should release the hold. Keep stamping wdLastRxMs so liveness still tracks real traffic — the watchdog must not re-trip spuriously once the master returns. Only the trip latch changes. Cleared now by SET_WATCHDOG (handler_set_watchdog) or a local serial command, both deliberate acts. anolis-provider-bread arms on startup (runtime_state.cpp:99,167) and again on address-recovery (bread_provider_runtime.cpp:38), so a controller restart or a recovered bus fault still clears it with no operator action. Reply builders are unaffected: they stamp wdLastRxMs only and never touched the trip, so the health poll's GET_WATCHDOG read cannot clear it either. Builds clean on all four board variants. Not yet validated on hardware — the decisive test is pressing e-stop on a tripped board and checking the shaft by hand, which is what the 08-08 session could not reach because that board predates #11 and coasted instead of braking. Refs anolishq/anolis#261
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.
Firmware half of anolishq/anolis#261.
The bug
on_crumbs_message(firmware/src/main.cpp:236-244) ran for every CRC-valid inbound command frame and did:The controller's e-stop ladder drives its safe state as ordinary command frames. So on a board holding a watchdog brake:
set_modearrives →wdTripped = falsemotorControlLogicleaves theif (local.eStop || wdTrippedNow)holdset_open_loop{0,0}follows →LMD18200::write()callsreleaseBrake()before driving → brake drops, motor free-wheelsThe emergency stop left the machine less stopped than it found it.
The fix
Stop clearing the trip there. Keep stamping
wdLastRxMs, so liveness still tracks real traffic and the watchdog cannot re-trip spuriously once the master returns — only the trip latch changes.The reasoning: a trip means the master went away while this board was driving something. Traffic arriving afterwards proves the master is back; it does not prove anyone decided it is safe to release a brake holding a load. Those are different facts, and only the second should release the hold.
What still clears it
SET_WATCHDOG→handler_set_watchdog(dcmt_handlers.cpp:152)serialCommands.cpp:61,217)wdTrippedinitialises false, watchdog boots disarmedanolis-provider-breadarms on startup (src/core/runtime_state.cpp:99,167) and again on address-recovery (src/core/bread_provider_runtime.cpp:38), so a controller restart or a recovered bus fault still clears the latch with no operator action.Reply builders are unaffected — they stamp
wdLastRxMsonly and never touched the trip, so the health poll'sGET_WATCHDOGread cannot clear it either. Verified: the only writers ofwdTrippedare the four above pluswatchdogLogicsetting it true.State of validation
Builds clean on all four board variants (
gen1_nano,gen2_nano,gen1_nanoevery,gen2_nanoevery).Not yet validated on hardware. The decisive test is pressing the e-stop on a tripped board and checking the shaft by hand. The 2026-08-08 bench session could not reach that step: the board on the rig predates #11 and coasted on a watchdog trip, so there was no brake for the e-stop to release. This change is going onto the rig together with #11 for the first time.
Two desk traces on this same safety path have already been wrong this campaign (anolishq/anolis#259 and #260 were both closed after review), so this should be treated as a hypothesis until the shaft is checked by hand.
The risk this introduces
A latched trip is, by design, sticky. The failure mode worth watching for is the opposite of #261: a board stuck braked because nothing re-arms it. The provider's two arming paths should cover it, but the bench procedure explicitly checks recovery after the e-stop test and treats "does not recover without a power cycle" as worse than the bug being fixed.
Attribution
module=1.0.0will not change —DCMT_MODULE_VER_*lives inbread-crumbs-contracts(pinned from the PlatformIO registry at^0.4.5), andbread_check_module_compatignores patch anyway. Making the flashed build self-identifying needs a contracts release; until then the git SHA must be recorded by hand at flash time. That is #13.