Writing Code to Handle Arduino’s millis() Roll Over

The Arduino’s millis() function returns the number of milliseconds since the arduino started running. It returns an unsigned long, 32 bit, value.

A 32 bit unsigned word can contain 0 – 4,294,967,285 (2^32-1). When millis() gets to 4,294,967,285 milliseconds and adds 1, it ‘rolls over’ to zero. It will take 49.7 days (4294967286 / 1000 / 60 / 60 / 24) for this to occur.

If you want your Arduino project to run continuously for more than 49 days you will want to take this roll over into account.

Unsigned Subtraction

Getting around the roll over issue is handled by using the subtraction property of unsigned words.

I’m going most of my examples using 8 bit unsigned words (or bytes). These are declared as uint8_t and can have the values 0 – 255 (2^8 – 1).

You cannot have a negative number stored in an unsigned word. If you have an uint8_t and subtract a large enough number such that the result is negative what happens?

uint8_t      x;

x = 0 - 1;

In binary, we start with zero


and subtracting 1 gives


or 255 in decimal. S0 0 – 1 is 255 when using uint8_t.

Here is the general algorithm the CPU is going to use to subtract two unsigned words, x – y:

if (x - y) < 0 then
  z = x - y + 2^ws
  z = x - y;

If (x-y) is positive, nothing special happens. If it is negative, we must add 2^ws where ws is the word size (we are currently using 8 bits).

Using this general rule, if you want to subtract 0 – 1, the result is going to be negative, so calculate z as

z = 0 - 1 + 256
z = 255

That’s exactly what was expected.

w = 255;

To be clear, YOU do not have do the above algorithm. It is done for you when you subtract two unsigned words:

uint8_t        a,c,x;
a = 0;
b = 1;
x = a - b;

and x now contains 255 because were are using uint8_t

Examples of Roll Overs

Let’s watch how subtraction works as roll overs occur. Assume x is increasing and rolls over. Let’s subtract 10 from each value of x and see what happens before and after the roll over (again, this is using an 8 bit unsigned word):

  x       x-10      x-10+2^8     z
254        244           n/a   244
255        245           n/a   245
  0        -10           246   246
  1         -9           247   247

as x approaches roll over (254 & 255)  x-10 returns a positive value and nothing more needs to be done; however, once x rolls over, a negative value is calculated and so we must add 2^8 which gives us the correct answer thru the entire sequence: 244, 245, 246, 247.

Now let’s consider the same thing with a 32 bit unsigned word:

x            x-10         x-10+2^32    z
4294967294   4294967284   n/a          4294967284
4294967295   4294967285   n/a          4294967285
0            -10          4294967286   4294967286
1            -9           4294967287   4294967287

Now imagine x is the current value of millis() and the constant 10 was the initial value of millis().  millis() – oldMillis returns the next proper value when millis() rolls over.

You may be looking at the x-10 result which is also nearing 2^32-1, and thinking that’s about to roll over anyway – this didn’t buy anything. This is only because I selected such a low value of 10 for the subtraction to keep the example simple. In real life this most likely will not be an issue (see The Catch).

Using Unsigned Subtraction to Our Advantage

Now that you’ve seen how unsigned subtraction works during roll over, let’s take advantage of it in a program.

We need to track the initial value of millis() when we start the timer and we need to know how long the timer should last. I typically use the names xxxTimer and xxxTimeout for these variables and define them something like this:

const unsigned long keyTimeout = 1000UL; 

unsigned long keyTimer;

You start the timer by assigning the current value of millis() to it:

keyTimer = millis();

This is the important part: you must subtract the initial timer from the current millis() value and compare that to your timeout. As long as you do that, your program will handle a roll over just fine:

while (test) {
  if (millis() - keyTimer > keyTimeout){ 
    << code to handle the timeout >>
  << rest of the code >>

The Catch

Well, this isn’t really a catch as much as a usage consideration. The word size of  the return value of millis() limits the length of the timeout. You cannot have a timeout larger than 2^32-1 milliseconds (49.7 days) even if roll over does work properly.

In the 32 bit example above, I set the initial value of millis() of 10. When millis() rolls over, it will be just 10 msecs short of exceeding the maximum allowed timeout. So while the roll over works fine, the I’m about to exceed the maximum possible timeout.

Note on UL Constants

You may have noticed that I used UL on my constants to force their typing such as 1000UL. You may wonder why.

For simple assignments, this really buys nothing:

xxxTimeout = 1000UL;

However, I tend to use constant expressions so I can read the code a little easier. For example, if I want a timeout of an hour I will code it as:

xxxTimeout = 1UL * 60UL * 60UL * 1000UL;

(1 hour * 60 minutes * 60 seconds * 1000 msecs)

rather than

xxxTimeout = 3600000UL;

If I need to make a change I’ll see that expression and realize it is for 1 hour and if I need 2, I just change the first constant (since the expression is evaluated at compile time I loose nothing do this).

The problem I’ve had is the C++ compiler periodically will NOT compute the constant expression properly if I don’t typecast each constant. This has bit me enough times that I simply never leave UL off any constant (or really any other non-integer constant for that matter).

I cannot say what the exact circumstances are to cause this issue because in every case, by the time I figured out what was going wrong I was too irritated to research the root of the problem.

This entry was posted in c-arduino and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.