millis() software timeout vs Watchdog Timer for graceful cancellation on AVR Arduino
0 reputation · 29 Mar 2020, 18:11 UTC
0 reputation · 29 Mar 2020, 18:11 UTC
A design needs a timeout and cancellation path for operations that may block, while keeping the sketch responsive and preserving RAM state across the timeout.
Arduino core offers two documented mechanisms. A non-blocking millis() timer keeps the loop running and allows manual elapsed-time checks with rollover-safe unsigned arithmetic. The hardware Watchdog Timer provides an automatic reset after a configurable period without polling, but a reset clears RAM and loses intermediate state. Core libraries such as Serial and Wire expose timeout arguments and setTimeout methods, yet there is no unified cancellation token in the core APIs. Aborting an in-progress operation therefore depends on library-specific behavior and on custom flag checks.
The unresolved decision is which approach satisfies a concrete constraint of graceful abort without full reset, given the lack of a common cancellation interface.
Does a millis()-based software timeout provide a documented way to abort a blocking library call without leaving peripherals in an undefined state? Can the Watchdog Timer be used as a safety net while still allowing cleanup code to run before reset? Is there a documented pattern for cancelling an ongoing Wire or Serial transaction without relying on vendor-specific functions?
33775 reputation · 30 Mar 2020, 04:31 UTC
millis() cannot abort a blocking library call. It only measures elapsed time between cooperative checkpoints. The Watchdog Timer (WDT) resets the MCU and does not run cleanup code before setup(); RAM state is lost. No documented core pattern exists to cancel an in-progress Wire or Serial transaction mid-operation without vendor-specific functions.
millis() is non-blocking and rollover-safe when used as (unsigned long)(millis() - start) >= timeout. It detects elapsed time after a call returns or between explicit checks.Serial.setTimeout() for blocking reads and Wire.endTransmission() status codes, but no unified cancellation token or standard mid-transaction abort.wdt_enable(), wdt_reset()) causes a device restart when not serviced. No user code runs between the reset vector and setup().WDIE) can set a flag in an ISR on some AVR parts, but this is register-level, device-specific, and not exposed by the Arduino core as a cancellation pattern.volatile bool cancelFlag and the millis() deadline between library calls, use library-documented timeouts, and re-initialize peripherals only when the bus is idle.volatile bool cancelRequested = false;
unsigned long deadline = 0;
const unsigned long TIMEOUT_MS = 5000;
void startOperation() {
deadline = millis() + TIMEOUT_MS;
cancelRequested = false;
// begin non-blocking or short-blocking step
}
void loop() {
if (cancelRequested || (unsigned long)(millis() - deadline) >= 0) {
cancelRequested = true;
// call library-specific cleanup if available
// e.g., Serial.end(); Wire.end();
// re-initialize only after bus idle
return;
}
// perform next cooperative step
}start = millis(), run the blocking call, print (unsigned long)(millis() - start). Output appears only after return → millis() did not abort it.wdt_reset() during a long operation, save MCUSR early in setup(). Confirm WDT reset flag set and no cleanup ran.HardwareSerial::setTimeout, TwoWire::setTimeout, Wire.endTransmission() return values; behavior varies by core version.Is the blocking call a core Serial/Wire operation that honors setTimeout() or returns status, or a third-party routine with no cancellation point? The answer determines whether cooperative checks suffice or a hardware-level recovery (bus reset, power cycle) is required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.