If you like to KISS it, you’re like me. Counting time with millis() is pretty decent, short of an actual real time clock module. Accuracy is not very good if your ATMega MCU is using a resonator (internal or external) but in cases like the GarageMote or the MailboxNotifier where you need a rough idea when something last happened, millis() is nonetheless very handy and sufficient. What I mean is things like “garage closed X seconds ago” or “mailbox opened Y seconds ago“.
All is fine until millis() falls off the edge of the unsigned long universe. That universe is only about 49.71 days long or a total of 4294967295 milliseconds. Because it is unsigned it then wraps back to 0. The Arduino site attempts to explain why this happens.
Let’s simplify and assume unsigned long max = 1000. Now let’s look at the two cases:
- Normal: If the event you care about is recorded by millis() at 500, and time passes until millis() returns 600, the difference between the two millis() readings is 100. Nice and simple, no sleep lost.
- Overflowed: If the event you care about is recorded by millis() at 950, and time passes until millis() overflows and wraps around to 70, the time difference between the two millis() readings is 120.
To solve this second case you have to add the two chunks of millis() together, but maybe that’s not totally obvious. That’s why I put together this diagram to illustrate what I mean. The red parts are the total time we care about. This is the time that passed since the event we care about.
unsigned long timeSince(unsigned long timestamp)
unsigned long now = millis();
if (now >= timestamp)
return now - timestamp;
else return ((unsigned long)(-1)) - timestamp + now;
To get the unsigned long max you can just convert -1 (negative 1) to unsigned long. Because there is no such thing as negative anything in unsigned types, -1 simply wraps around backwards to the last value of the long spectrum, giving you the maximum.
I will have to admit that some of the sample sketches I released so far are ignoring this issue and will cause Moteinos to appear as non responsive when checking when an event last happened. Moving forward I will make an effort to push upgrades to fix these logical bugs. In the meantime you are now empowered to know how to get around it yourself.