Skip to content

fix: latch the watchdog trip until an explicit clear - #14

Open
CameronBrooks11 wants to merge 1 commit into
mainfrom
fix/latch-watchdog-trip-until-explicit-clear
Open

fix: latch the watchdog trip until an explicit clear#14
CameronBrooks11 wants to merge 1 commit into
mainfrom
fix/latch-watchdog-trip-until-explicit-clear

Conversation

@CameronBrooks11

Copy link
Copy Markdown
Member

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:

wdLastRxMs = millis();
wdTripped   = false;      // <- 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:

  1. set_mode arrives → wdTripped = false
  2. motorControlLogic leaves the if (local.eStop || wdTrippedNow) hold
  3. set_open_loop{0,0} follows → LMD18200::write() calls releaseBrake() before driving → brake drops, motor free-wheels

The 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

path deliberate?
SET_WATCHDOGhandler_set_watchdog (dcmt_handlers.cpp:152) yes — an explicit arm
local serial command (serialCommands.cpp:61,217) yes — an operator at the board
board reboot wdTripped initialises false, watchdog boots disarmed

anolis-provider-bread arms 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 wdLastRxMs only and never touched the trip, so the health poll's GET_WATCHDOG read cannot clear it either. Verified: the only writers of wdTripped are the four above plus watchdogLogic setting 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.0 will not change — DCMT_MODULE_VER_* lives in bread-crumbs-contracts (pinned from the PlatformIO registry at ^0.4.5), and bread_check_module_compat ignores 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.

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
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