How To Handle Microsecond Rollover With Arduino’s micros() Function

When timing on the Arduino, it’s common to use the millis() function, which counts the milliseconds the current program has been running. But sometimes you need to do things smaller – enter the microsecond timing function, micros().

However, there’s a big problem: its maximum size ends up being too small for long-term programs!

To see how that works, consider: the largest value that micros() can produce is 2^32-1, or 4,294,967,295. At one million ‘ticks’ per second, we can do

4,294,967,295 / 1,000,000 = 4294.967295 seconds

Before we overflow (about 71 minutes and 35 seconds).

However, what do we do then? For example, what if we need to keep track, and our last count was 4,294,967,000, and our new count is 250. How do we figure out the change?

In a nutshell – the same way we always did!

To see how that works, consider code to check the change in time, or delta:

unsigned long oldTime=micros();
...later on...
unsigned long newTime=micros()-oldTime;

So if oldTime is 500, and newTime is 750, we have (750-500=250), which is fine. But look at our earlier example:

         decimal          hexadecimal

             250                   FA
 - 4,294,967,000           - FFFFFED8
----------------     ----------------
 - 4,294,966,750             00000222 (decimal 546)

What happened? In our world, we have negative numbers, so the result is negative. But we’re working with unsigned longs – there’s only 32 bits, and no negative. So the value rolls over like an old-fashioned odometer, and we get 546.

To see if this is right, let’s do some math:

  • From 4,294,967,000 to 4,294,967,295 (hexadecimal FFFFFFFF) is 295
  • From 4,294,967,295 to 4,294,967,296 is 1
  • But 4,294,967,296 doesn’t exist in an unsigned long, so it rolls over to zero
  • So, from 0 to 250 is 250
  • 295 + 1 + 250 is 546 – the result we expect!

So it would seem we can ignore rollover when we do time calculations, and use the result no matter what.

Not quite:

  • For this to work, we MUST have an ‘oldTime’ that occurred in the last 71.5 minutes. If we cycle over two or more times, the difference is meaningless; since we can’t ‘hold’ multiple 71.5 minutes cycles in an unsigned long, we can’t figure out the whole time interval.
  • The value as mentioned must be an unsigned long, which rolls over after FFFFFFFF.

However, if we keep this in mind, this means that as long as we time frequently enough, code like unsigned long newTime=micros()-oldTime; will work!

Comments are closed.