millis() overflow -- what happens???

I often use millis() to do certain things every few seconds. Like the following example:
(LCDtime is unsigned long)

    if (millis() - LCDtime > 3000) { //Has it been 3 seconds yet?
        LCDtime = millis(); //update the timer
        UpdateLCD(); // Change the LCD messege
    }

Doesn't Millis() eventually overflow and return to zero? Will this code still function, or will it no longer update the LCD every 3 seconds?
I do lots of different things with this style...

As long as you perform a subtraction operation on the time interval test calculation (as you do in your example), the 'rollover' situation at 55 days (or so) is handled fine. There was a 'famous' posting here in the past explaining how the subtraction overflow bit handles the roll over condition just fine as long as you use subtraction for your time interval test calculation.

Lefty

I'd guess it would just continue counting as it should. In the example below I used a fake millis() counter, same variable type, starting at 20 secs before overrun (2^32).

unsigned long virtualmillis;
unsigned long LCDtime = 0;
void setup()
{
  Serial.begin(9600);
}
void loop (){
  // fake counter, to not have 
  // to wait 50 days
  virtualmillis = millis() + 4294947296;  
  if (virtualmillis - LCDtime > 3000) { 
    //Has it been 3 seconds yet?
    LCDtime = virtualmillis; //update the timer
    //UpdateLCD(); // Change the LCD messege
    Serial.print ("LCDtime : ");
    Serial.print(LCDtime); 
    Serial.print (", virtualmillis : ");
    Serial.println(virtualmillis);         
  }

}

Good to know!

retrolefty:
There was a 'famous' posting here in the past explaining how the subtraction overflow bit handles the roll over condition just fine as long as you use subtraction for your time interval test calculation.

If this is the post you're referring to, then I'll take that as high praise indeed.

crimony:

retrolefty:
There was a 'famous' posting here in the past explaining how the subtraction overflow bit handles the roll over condition just fine as long as you use subtraction for your time interval test calculation.

If this is the post you're referring to, then I'll take that as high praise indeed.

Yes, you and Coding Badly nailed the dirty details for that one. However due to passing time, Arduino reference docs that still talk about the 'roll over' interval, etc the subject still comes up from time to time. Probably an indication that the subject and proven solution should be made either a stick or included in the 'official' Arduino reference for the millis() function.

Lefty

retrolefty:
Probably an indication that the subject and proven solution should be made either a stick

Great idea!

Let's finish the first sticky. Pick a set and post comments...
http://arduino.cc/forum/index.php/topic,67509.0.html

retrolefty:
... Arduino reference docs that still talk about the 'roll over' interval, etc the subject still comes up from time to time. Probably an indication that the subject and proven solution should be made either a stick or included in the 'official' Arduino reference for the millis() function.

Totally OT, but I have the same uneasy feeling about the use of delay(). The number of posts where an OP is looking for a solution to "doing two things at once" is very high. I wonder whether the use of delay() should be discouraged. I would go so far as to suggest delay() should be considered an advanced technique on par with goto.

Perhaps the subject of a separate thread...

Two things you might want to consider as well:

  • This only works if the delay interval is less than rollover - for example, if you use microseconds, and expect to time things >70 seconds, then you might miss a complete rollover from time to time.

  • Are there cases where optimizing of the compiler affect the if statement? Not sure, but I'd put parenthesis around it just in case:

if ( (millis()-LCDtime) > 3000)

Optimizing won't affect operation order. Operator precedence can't be optimized into some other precedence.

The precedence for addition/subtraction is higher than less-than/greater-than, so you are OK.

The point is that the optimizer knows that a - b > c is different from a > c + b for 2's complement arithmetic.

I don't see how the desired interval can be preserved at rollover.

for simplicity lets say rollover happens at 5,000,000. if your interval is millis-current<=threshold, current gets updated to the previous millis when the threshold is reached (and some event happens), it is getting to be a bigger and bigger number as we get closer to rollover time (both are).

just before rollover, millis goes from a huge number back down to zero before the threshold is reached.
after that the interval is tested as a very small millis minus a very large current (say 10 millis - 4,999,000 millis). won't we crash ? won't we be in negative numbers ? won't the loop just go on forever with no more events taking place at the threshold ?

The note on this thread said it hadn't been responded to in more than 120 days, and should i start a new topic. but if i did, i figure i would just get sent back to this thread with a suggestion to read what is already here.

@pratto

Because one uses unsigned variables, negative numbers don't exist and that is why it works.

You could have started a new thread and refer to this thread in the new one.

so in my example, at rollover it is 10 - 4,999,000, the unsigned value of this is > threshold and the event happens.
then current becomes 10, and millis is greater than 10. when the threshold is hit, say at 1000, event happens and current = 1000, and millis gets bigger, and so on.

if that is right, i get it.

This uses a 8 bit value (0..255) to describe how the roll over math works:

1: Last millis = 100, current millis = 200, elapsed = 200-100 = 100
2: Last millis = 200, current millis = 44, elapsed = 44-200 = 100

In the second example, you will cause the roll over with a subtraction of 45. This leaves 155 that needs to be subtracted from the maximum value and 255 - 155 = 100.

Using signed numbers to describe it: 45 - 200 = -155. Now you must add the maximum value to remove the sign which gives: -155 + 255 = 100.

So even if a roll over happens within a timed period, doing the math right with the right data types will still yield the correct number of millis elapsed.

EDIT: 0 is also a value..

1 Like

when you subtract a large unsigned integer from a smaller unsigned integer and store the result in the same size unsigned integer, the truncated result of the subtraction results in the expected difference.

     50     50      0
     50    100     50
     50    150    100
     50    200    150
     50    250    200
     50     44    250
     50     94     44
     50    144     94
     50    194    144
     50    244    194
    uint8_t  a = 0, b = 0, c;

    for (int n = 10; n > 0; n--)  {
        a += 50;
        c  = a - b;
        printf (" %6u %6d %6d\n", c, a, b);
        b = a;
    }

More graphically;

ok. thanks to you all.