Arduino millis() rollover after ~50 days: diagnosing subtraction reliability
0 reputation · 15 May 2020, 22:23 UTC
0 reputation · 15 May 2020, 22:23 UTC
The millis() function on ATmega328P‑based boards increments a 32‑bit unsigned long based on Timer0 overflows with a 64‑prescaler. After 232 increments—roughly 49.7 days—the counter wraps back to zero. Many timing routines rely on computing intervals via (currentMillis - previousMillis) or on absolute comparisons with a stored timestamp.
While unsigned subtraction is mathematically sound across the wrap point, it is unclear whether all common timing patterns (e.g., if (millis() >= triggerTime), while (millis() < endTime)) behave as expected when triggerTime or endTime is set before the rollover and the loop continues past it. Additionally, the interaction of such logic with interrupt‑driven code that updates previousMillis asynchronously is not fully documented.
(currentMillis - previousMillis) < interval remain valid when currentMillis has wrapped but previousMillis has not?if (millis() >= triggerTime), will the comparison trigger correctly after a wrap if triggerTime was set before the overflow?35275 reputation · 16 May 2020, 00:20 UTC
Assuming an AVR-based Arduino core where unsigned long is 32 bits, (currentMillis - previousMillis) < interval remains valid across the millis() wrap provided the true interval is below 2³² ms (~49.7 days). The expression if (millis() >= triggerTime) does not reliably fire after rollover when triggerTime was captured before the overflow. For multiple rollover cycles, prefer interval subtraction over absolute timestamp comparison, or maintain a separate overflow counter.
millis() returns an unsigned 32-bit value that wraps to zero after 2³² milliseconds.>= comparison against a pre-wrap timestamp can produce a false negative after rollover because the counter restarts at zero.millis() updates are not atomic with respect to Timer0 interrupt context.After wrap, currentMillis is small while previousMillis is large. Unsigned subtraction computes the forward distance across the boundary, but a magnitude comparison sees the small current value as less than the stored trigger and skips the action.
unsigned long a = millis();, wait 1000 ms, read unsigned long b = millis();, and confirm b - a is 1000.previousMillis near 0xFFFFFFFF, let millis() pass zero, and confirm the subtraction still reports the expected short interval.previousMillis; if any exist, copy millis() to a local variable with interrupts disabled before subtracting.Please confirm whether previousMillis is ever written by an interrupt after rollover; that determines whether atomic reads are required in addition to wrap-safe subtraction.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.