Handling millis() rollover

I’m looking at many examples that track millis() as a way of keeping time between events. For example, this code take from Tutorial: Getting Started with Spark.publish().

void loop() {
    unsigned long now = millis();
    //Every 15 seconds publish uptime
    if (now-lastTime>15000UL) {
        lastTime = now;
        // now is in milliseconds
        unsigned nowSec = now/1000UL;
        unsigned sec = nowSec%60;
        unsigned min = (nowSec%3600)/60;
        unsigned hours = (nowSec%86400)/3600;
        sprintf(publishString,"%u:%u:%u",hours,min,sec);
        Spark.publish("Uptime",publishString);
    }
}

I have read, and been thinking about, the point at which millis() becomes too big for even an unsigned long. I hear it will then roll over back to 0. If that’s the case, how do you handle that for cases when it needs to be somewhat accurate? If my lastTime variable is only 500 millis away from rollover and millis() rolls over to 0, I still need to 14500 millis before doing this operation again. How do I account for that? Any chance someone could put this in a tutorial? If not, I will write one up if I can get the hang of it, as long as you kind folk explain it to me.

Thanks,
damccull

@damccull, you may want to do a forum search. Here is one topic I picked up from a search on "millis roll"

That’s the beauty of using unsigned integers. If all of the numbers that you are dealing with are of the same unsigned long type (which it looks like they are), and it actually rolls over properly, aka integer overflow, not other method overflow (which millis() does… I believe micros() at least on the Core rolls over before integer overflow), then you don’t need to worry about it. This may provide more info

Edit: @peekay123 referenced the micros() case, but millis() should work fine

1 Like

I did see that post, but I didn’t see any examples in there of how to actually handle the rollover. Going back and looking again, I did find Micros() rolling over after 59.652323 seconds, not 71 minutes [SOLVED], but now I don’t understand how it works.

I understand that the function takes in 2 unsigned longs and then subtracts the old from the new. What I don’t understand is how there could be a result of < 0 to check against if the longs are unsigned. From everything I understand about them, there should never be a negative value on an unsigned number.

@mumblepins After reading that link you posted, and your own post, can I assume that it should just ‘magic’ when I do millis() - lastTime if millis = 500 and lastTime = [MAX]-14500?

That's the charming thing about unsigned rollover. As @mumblepins already said, as long it is a propper binary rollover you can do smallValue - bigValue and you will get the same result as if you had an extra (33rd) bit set on smallValue.

Have a look at the stackoverflow link above or do the binary subtract with only four bit unsigned numbers.

Meh, thought I’d make an example online (using unsigned 8-bit for simplicity) http://cpp.sh/7wmr

The way the C++ standard is defined, unsigned integers automatically (magically :smile: ) rollover on overflow, and ‘underflow’ , which is what takes place when you subtract a larger unsigned number from a smaller unsigned number. Basically, if the result of an addition in unsigned long is greater than UINT32_MAX, the return value is answer - UINT32_MAX, and if the result of subtraction is less than zero, the return value is answer + UINT32_MAX

As to that example, the cast to long results in the possible negative number, as all that takes place is the binary value gets reassigned a signed type. So if the leading digit of unsigned value is a 1, then the signed value is negative. As in,

uint8_t a =255;
int8_t b= (int8_t) a;  // b= -1
a =128;
b=(int8_t) b; // b=-128;
1 Like