unsigned long - how to continue counting after 49 days overflow?

Hi there!

I have an arduino board with lcd and I want to display the total arduino running time, since I plugged it into power.

So i am using an unsigned long. What will happen if it overflows? Will the code given below,work after 49 days correctly?

unsigned long  time= 0;

void loop(){
time=millis();
}

In what units? Let's say seconds.

Make another variable, and add to it when 1000+ milliseconds pass. Something like this:

unsigned long seconds = 0;
unsigned long lastSecond = 0;

void setup () {}

void loop ()
  {
  if (millis () - lastSecond >= 1000)
    {
    seconds++;
    lastSecond += 1000;
    }

  // display seconds

  }  // end of loop

That will work for 49710 days. Should be enough before the battery runs out.

I think that will handle overflow correctly. No doubt someone will say if I am wrong.

I don't know the answer to your question off the top of my head, and I am not even going to attempt to find out. Instead, I want to encourage you to think about the problem slightly differently:

You have a variable that is holding an incrementing value, and, at some point, will "roll over". You want it to continue counting, though, for a much longer period. So what should you do? How about:

  1. Set up -two- variables, both unsigned longs - one called "counter", the other "rollover"
  2. Increment counter as normal (however you do that is up to you)
  3. When "counter" rolls over (ie, hits its highest number and resets to zero), increment "rollover"
  4. Number of count = rollover * max_value + counter

If both are unsigned longs, and you are counting milliseconds, then you'll have about 213503982334 days until it "fails" (even if my math is slightly wrong - believe me, you and possibly humanity itself will be long dead)...

Condition not met. Consider this post the else-clause. 8)

My example is wrong. It doesn't handle the rollover. Eg. If we were using byte rather than unsigned long, and adding 10 each time and not 1000, consider:

lastSecond = 250;

lastSecond += 10;

lastSecond is now 4.

The comparison:

if (millis () - lastSecond >= 10)

would be met for millis() of 251, 252, 253, 254, 255.

It's correct.

It doesn't handle the rollover. Eg. If we were using byte rather than unsigned long, and adding 10 each time and not 1000, consider:

lastSecond = 250;

The only way for this line of code to execute...

lastSecond += 10;

...is if this condition is true...

if (millis () - lastSecond >= 10)

...which means millis has to already be 4 or greater. This is the timeline...

millis lastSecond delta result


255 250 5 condition not met
0 250 6 condition not met
1 250 7 "
2 250 8 "
3 250 9 "
4 250 10 condition met; lastSecond incremented
5 4 1 condition not met
6 4 2 "

Much as I hate to try to prove myself wrong, try this, which substitutes byte for unsigned long, and "makes up" a one-byte millis:

#include <Streaming.h>

byte seconds = 0;
byte lastSecond = 0;
byte myMillis = 0;

int count;

void setup () 
  {
  Serial.begin (115200);
  Serial.println ();
  }

void loop ()
  {
  myMillis++;
  
  if (myMillis - lastSecond >= 10)
    {
    seconds++;
    lastSecond += 10;
    }

  // display seconds

  Serial << "myMillis = " << _DEC (myMillis) << endl;
  Serial << "lastSecond = " << _DEC (lastSecond) << endl;
  Serial << "seconds = " << _DEC (seconds) << endl;
  
  // stop if enough done
  if (count++ > 300)
    while (true) {}
    
  }  // end of loop

Output:

myMillis = 1
lastSecond = 0
seconds = 0


... blah blah ...

lastSecond = 250
seconds = 25
myMillis = 251
lastSecond = 250
seconds = 25
myMillis = 252
lastSecond = 250
seconds = 25
myMillis = 253
lastSecond = 250
seconds = 25
myMillis = 254
lastSecond = 250
seconds = 25
myMillis = 255
lastSecond = 250
seconds = 25
myMillis = 0
lastSecond = 250
seconds = 25
myMillis = 1
lastSecond = 250
seconds = 25
myMillis = 2
lastSecond = 250
seconds = 25
myMillis = 3
lastSecond = 250
seconds = 25
myMillis = 4
lastSecond = 250
seconds = 25
myMillis = 5
lastSecond = 250
seconds = 25
myMillis = 6
lastSecond = 250
seconds = 25
myMillis = 7
lastSecond = 250
seconds = 25
myMillis = 8
lastSecond = 250
seconds = 25
myMillis = 9
lastSecond = 250
seconds = 25
myMillis = 10
lastSecond = 250
seconds = 25
myMillis = 11
lastSecond = 250
seconds = 25
myMillis = 12
lastSecond = 250
seconds = 25
myMillis = 13
lastSecond = 250
seconds = 25
myMillis = 14
lastSecond = 250
seconds = 25
myMillis = 15
lastSecond = 250
seconds = 25
myMillis = 16
lastSecond = 250
seconds = 25
myMillis = 17
lastSecond = 250
seconds = 25
myMillis = 18
lastSecond = 250
seconds = 25
myMillis = 19
lastSecond = 250
seconds = 25
myMillis = 20
lastSecond = 250
seconds = 25
myMillis = 21
lastSecond = 250
seconds = 25
myMillis = 22
lastSecond = 250
seconds = 25
myMillis = 23
lastSecond = 250
seconds = 25
myMillis = 24
lastSecond = 250
seconds = 25
myMillis = 25
lastSecond = 250
seconds = 25
myMillis = 26
lastSecond = 250
seconds = 25
myMillis = 27
lastSecond = 250
seconds = 25
myMillis = 28
lastSecond = 250
seconds = 25
myMillis = 29
lastSecond = 250
seconds = 25
myMillis = 30
lastSecond = 250
seconds = 25
myMillis = 31
lastSecond = 250
seconds = 25
myMillis = 32
lastSecond = 250
seconds = 25
myMillis = 33
lastSecond = 250
seconds = 25
myMillis = 34
lastSecond = 250
seconds = 25
myMillis = 35
lastSecond = 250
seconds = 25
myMillis = 36
lastSecond = 250
seconds = 25
myMillis = 37
lastSecond = 250
seconds = 25
myMillis = 38
lastSecond = 250
seconds = 25
myMillis = 39
lastSecond = 250
seconds = 25
myMillis = 40
lastSecond = 250
seconds = 25
myMillis = 41
lastSecond = 250
seconds = 25
myMillis = 42
lastSecond = 250
seconds = 25
myMillis = 43
lastSecond = 250
seconds = 25
myMillis = 44
lastSecond = 250
seconds = 25
myMillis = 45
lastSecond = 250
seconds = 25
myMillis = 46
lastSecond = 250
seconds = 25

Once we reach lastSecond = 250, seconds gets stuck on 25.

if ( (byte)(myMillis - lastSecond) >= (byte)(10) )

...otherwise the operation is promoted to int. Type-casting is not necessary for unsigned long because there is no promotion.

So I was right all along? How does that work?

That's what I've been saying.

How does that work?

Don't ask me. You're the one who convinced yourself you were wrong.