Arduino Millis() Rollover Handling

Millis Police

On the Arduino microcontroller, the millis() function counts the number of milliseconds since the program started running. Unfortunately, this count resets to zero after approximately 9 hours and 32 minutes. I have written a millisRollover() function that detects these rollovers, so that programs can respond properly to the overflow event. This can solve problems with servo routines, steppers, timed pauses and a variety of other calculations. In addition, because my millisRollover() function counts the number of times rollover has happened, it is now possible to record total Arduino runtime with a counter that’s good for over 35 years.

You want to blink an LED only on Christmas during leap years? Totally possible now.

(yes, there really is a millis police department)

5 Responses to “Arduino Millis() Rollover Handling”


  • I’m having a little difficulty understanding how I would use your rollover function in a scenario where I tick a clock if 1000 ms has elapsed, like:

    if( millis() – previousMillis > 1000 ) {
    previousMillis = millis();
    // One second has elapsed, tick the clock.
    }

    It seems like this will “hiccup” during a rollover.

  • Glad you asked because it’s worth noting that the millis() function was greatly improved in Arduino 0012:

    * Improved millis(): it now overflows after 49 days instead of 9 hours, but
    now uses slightly more processing power.

    I’m assuming this means that it now fills the full unsigned long variable. So in your case you should just be able to do the math. The rollover will cause your subtraction to overflow an the result would be correct.

  • Just a thought – why not use the older, computationally cheap code for 49 minute roll overs and use a second 16 bit variable as a count for the roll overs and use seconds to return the sum of the two variables as a 64 bit value. Gives a long, long time before rollover.
    eg:
    short int seconds = 0; //
    unsigned long millisLong() {
    return unsigned long int (seconds) * 1000 + unsigned long int (millis());
    }

    I know this requires the ‘second’ variable to have millisecond precise accuracy but that is a relatively small price to pay for a very large, high precision counter.

    Probably best set by a build flag because of the extra interrupts required.

  • typo: short unsigned int seconds = 0;

  • Hi folks,

    There’s a much better generic way of handling rollovers on timers. Instead of using unsigned timer values you use signed timer values. Then to decide whether time1 > time2 you simply calculate (time1-time2>0) which returns correct results for all cases where the period itself is b isn’t equivalent to a-b>0, due to the nature of 2′s compliment arithmetic. Thus, if instead we have:

    long eventTimeout=(long)millis()+1000;
    if((long)millis()-eventTimeout>=0) {
    eventTimeout=(long)millis()+1000;
    }

    We will *never* get a roll-over condition! Consider, in the old code, the if statement would fail when millis=0, and oldMillis was 4294967000. Here, millis-oldMillis (very much) >= 1000 even though the timer should not have expired.

    However, with the new code, we instead calculate the eventTimeout and compare with 0, so in the case above (long)millis – eventTimeout = -704, so there’s no timeout, until millis>=704.

    You might think this technique would fail when the signed values roll-over to unsigned values. However, it doesn’t. Consider when eventTimeout was 2147483000. When millis() reached that value, so millis()-eventTimeout was >=0, eventTimeout was then set to 2147484000 which is actually -2147483296. The timeout looks like it’s in the past, but because we’re doing signed arithmetic (long)millis()-eventTimeout = 2147483000-(-2147483296) = -1000 and so it sees it as being in the past (which it is). Similarly when (long)millis() crosses from 2147483647 to -2147483648 the subtraction will yield -353, then -352 and will only result in >=0 when (long)millis() = -2147483296 as intended.

    This can be combined into a function:

    #define smillis() ((long)millis())

    boolean after(long timeout)
    {
    return smillis()-timeout>0;
    }

    Then your code will always work if you do:

    long timeout=smillis()+1000;


    if(after(timeout)) {
    … do something.
    timeout=1000;
    }

    -cheers from Julz @P

Leave a Reply




Bad Behavior has blocked 741 access attempts in the last 7 days.