Go Down

Topic: unsigned long - how to continue counting after 49 days overflow? (Read 1 time)previous topic - next topic

nathanas

Jun 17, 2012, 11:47 pmLast Edit: Jun 17, 2012, 11:49 pm by nathanas Reason: 1
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?

Code: [Select]
`unsigned long  time= 0;void loop(){time=millis();}`

Nick Gammon

In what units? Let's say seconds.

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

Code: [Select]
`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.
Please post technical questions on the forum, not by personal message. Thanks!

http://www.gammon.com.au/electronics

cr0sh

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)...
I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

No doubt someone will say if I am wrong.

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

Nick Gammon

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:

Code: [Select]
`lastSecond = 250;lastSecond += 10;`

lastSecond is now 4.

The comparison:

Code: [Select]
`if (millis () - lastSecond >= 10)`

would be met for millis() of 251, 252, 253, 254, 255.
Please post technical questions on the forum, not by personal message. Thanks!

http://www.gammon.com.au/electronics

#5
Jun 18, 2012, 01:28 amLast Edit: Jun 18, 2012, 01:32 am by Coding Badly Reason: 1
My example is wrong.

It's correct.

Quote
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:

[font=Courier New]  lastSecond = 250;[/font]

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

[font=Courier New]  lastSecond += 10;[/font]

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

[font=Courier New]  if (millis () - lastSecond >= 10)[/font]

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

[font=Courier New]
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     "[/font]

Nick Gammon

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:

Code: [Select]
`#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:

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

Once we reach lastSecond = 250, seconds gets stuck on 25.
Please post technical questions on the forum, not by personal message. Thanks!

http://www.gammon.com.au/electronics

[font=Courier New]  if ( (byte)(myMillis - lastSecond) >= (byte)(10) )[/font]

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

Nick Gammon

So I was right all along? How does that work?
Please post technical questions on the forum, not by personal message. Thanks!

http://www.gammon.com.au/electronics

So I was right all along?

That's what I've been saying.

Quote
How does that work?

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

Go Up

Please enter a valid email to subscribe