5

I am working on a project which requires countdown from 30sec to 0sec. I can't use delay() function because I am taking input from keyboard during countdown. millis() function wont help because my board will be active for more than 50 days. I require something like counter which I can reset after or before completion of 30sec.

  • millis wrap around isn't a problem as long as you only use time difference. So timediff = endtime - starttime. – Gerben Jan 19 '17 at 19:17
15

Background

The function millis () returns an unsigned long, which is the number of milliseconds since the processor was reset (until it overflows).

See: http://arduino.cc/en/Reference/Millis

Example:

unsigned long startTime = millis ();

Since there are 232 bits in an unsigned long it can count from 0 to 4294967295.

Computing this in terms of days we have:

2^32 / 1000 / 60 / 60 / 24 = 49.710 days

So, the number returned will overflow (go back to zero) after around 49 days (almost 50 days, as the reference page says).


Is this bad?

No. Is it bad your clock overflows (goes back to zero) at midnight? No it isn't.

Let's clear up some misconceptions:

  • The processor does not reset when the timer overflows.
  • The timer does not stop.
  • Nothing "bad" happens.

All that happens, on a timer overflow, is that it goes back to zero and starts counting up again. Just like your clock does.


How do I time an event?

There are two ways of timing something (eg. a minute from now). You can add or subtract. Because of the way that unsigned arithmetic works, subtracting is the correct way.


Example of adding - not recommended!

I want to detect a minute in the future...

unsigned long startTime = millis ();
unsigned long whenToStop = startTime + 60000;

...

if (millis () >= whenToStop)
  {
  // do something
  }

This will work most of the time, but not when the timer overflows. Let's see why:

Say the timer will overflow in 50 seconds, that is, it is currently:

2^32 - 50000 = 4294917296 (0xFFFF3CB0)

Now we add 60000, and get:

2^32 - 50000 + 60000 = 10000 (0x2710)

Our "whenToStop" variable has overflowed and is now quite small. And thus it is immediately less than the current time, and our check for when a minute is up fails, it appears to be up immediately.


Example of subtracting - recommended

I want to detect a minute in the future...

unsigned long startTime = millis ();
unsigned long interval = 60000;

...

if (millis () - startTime >= interval)
  {
  // do something
  }

This will work all the time. Let's see why:

Say the timer will overflow in 50 seconds, that is, it is currently:

2^32 - 50000 = 4294917296 (0xFFFF3CB0)

In 50 seconds the timer wraps around and goes to zero. Subtracting now gives us:

// millis () - startTime   = elapsed interval
    0        - 4294917296  = 50000 (0xC350)

The subtraction "wrapped around" and gave us a small number. If you use a scientific calculator and try the above you may get a result of 0xFF0000C350. The initial 0xFF is the "extended sign bit", in effect, however as the field is unsigned, and only four bytes long, the sign bits are dropped, giving a small positive number, not a large negative one.

Demo using the Mac Calculator app:

millis() rollover

In 60 seconds the timer goes to 10000. Subtracting now gives us:

// millis () - startTime   = elapsed interval
    10000    - 4294917296  = 60000 (0xEA60)

So the elapsed interval (the difference) is now 60000, which is what we wanted.

Directly after we set up startTime and millis we will see a sequence like this:

millis ()   -  startTime    =  difference
                             (modulo 2^32)
4294917296     4294917296               0  <-- we start timing here
4294917297     4294917296               1
4294917298     4294917296               2
...
4294967295     4294917296           49999  <-- largest unsigned long value
         0     4294917296           50000  <-- wraps here back to zero
         1     4294917296           50001  <-- keeps counting up
...
      9999     4294917296           59999
     10000     4294917296           60000  <-- time's up!

Recommended method

  • Use unsigned long (not just long) for "time" variables.
  • Record the start time of some event (eg. a debounce, when you start feeding the fish, etc.)
  • Subtract the start time from the time now, giving a difference.
  • See if the difference exceeds the desired interval.

eg.

startedFeedingFish = millis ();

...

if (millis () - startedFeedingFish >= 20000)  // feed them for 20 seconds
  {
  // stop feeding the fish
  }

Can't I just reset millis?

You can, but you don't need to. Plus it may well throw out any library you are using that does not expect millis to be reset. Plus you have to decide exactly when to do it. It would need to be a time when you aren't using millis. For example if you happened to be feeding the fish in the above example, and your code reset millis, then the fish will get an awful lot of food!


What about micros?

The function micros() returns the number of microseconds since reset. This also wraps around, however since there are 1000 microseconds to a millisecond it will wrap around faster.

 2^32 / 1000 / 1000 / 60  = 71.58 minutes

http://arduino.cc/en/Reference/Micros

However you can use exactly the same technique to keep track of time to the microsecond interval. Be aware that you can't time more than 71.58 minutes this way though.

Also be aware that the resolution of micros() is four microseconds (because of the timer prescaler) so you won't ever be able to time a one or two microsecond interval.


Limitations

If you are using millis() you cannot time more than 49.71 days, and if you are using micros() you cannot time more than 71.58 minutes, without extra code to detect the wrap-around. For long intervals I suggest a real-time clock, since that would be more reliable during power failures, or when you replace batteries, etc.


Reference

This answer taken from my page http://www.gammon.com.au/millis

-2

You could handle the wrapping - for example

long long start = millis();
// do some stuff
long long end = millis();

// If the counter wrapped during that time, artificially fix 'end'
if (end < start) end += 4294967295;

long duration = (long)(end - start);

(Of course this is basically impossible to test, but I think it's correct.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.