millis()
Returns the number of milliseconds passed since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 50 days.
Syntax
Parameters
Returns
Number of milliseconds passed since the program started. Data type: unsigned long .
Example Code
This example code prints on the serial port the number of milliseconds passed since the Arduino board started running the code itself.
unsigned long myTime; void setup() < Serial.begin(9600); >void loop() < Serial.print("Time: "); myTime = millis(); Serial.println(myTime); // prints time since program started delay(1000); // wait a second so as not to send massive amounts of data >
Notes and Warnings
- The return value for millis() is of type unsigned long , logic errors may occur if a programmer tries to do arithmetic with smaller data types such as int . Even signed long may encounter errors as its maximum value is half that of its unsigned counterpart.
- millis() is incremented (for 16 MHz AVR chips and some others) every 1.024 milliseconds, then incrementing by 2 (rather than 1) every 41 or 42 ticks, to pull it back into synch; thus some millis() values are skipped. For accurate timing over short intervals, consider using micros().
- millis() will wrap around to 0 after about 49 days (micros in about 71 minutes).
- Reconfiguration of the microcontroller’s timers may result in inaccurate millis() readings. The «Arduino AVR Boards» and «Arduino megaAVR Boards» cores use Timer0 to generate millis() . The «Arduino ARM (32-bits) Boards» and «Arduino SAMD (32-bits ARM Cortex-M0+) Boards» cores use the SysTick timer.
See also
- EXAMPLE Blink Without Delay
Arduino.ru
Возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino. Это количество сбрасывается на ноль, в следствие переполнения значения, приблизительно через 50 дней.
Параметры
Возвращаемое значение
Количество миллисекунд с момента начала выполнения программы. (unsigned long)
Пример
unsigned long time; void setup() < Serial.begin(9600); >void loop()< Serial.print("Time: "); time = millis(); //выводит количество миллисекунд с момента начала выполнения программы Serial.println(time); // ждет секунду, перед следующей итерацией цикла. delay(1000); >
Arduino.ru
Здравствуйте, возникла острая необходимость обнулить millis по просшествии какого-то времени, которое эта же функция считает. Прост омне нужно чтобы в течении допустим 5 секунд горел светодиод и ультразвуковой датчик проверял расстояние и распределяя его на правильное и не правильное и вот по просшествии 5 секунд принимал решение: если не правильных сигналов больше, то продлить время работы светодиода, а если нет, то переключится на другой светодиод и делать то же самое. Сейчас у меня все на цикле while и много функции delay, но я решил подключить кнопку, чтоб переключать режимы, а из-за delay программа может просто не увидеть нажатие кнопки
- Войдите на сайт для отправки комментариев
How can I handle the millis() rollover?
I need to read a sensor every five minutes, but since my sketch also has other tasks to do, I cannot just delay() between the readings. There is the Blink without delay tutorial suggesting I code along these lines:
void loop() < unsigned long currentMillis = millis(); // Read the sensor when needed. if (currentMillis - previousMillis >= interval) < previousMillis = currentMillis; readSensor(); >// Do other stuff. >
The problem is that millis() is going to roll over back to zero after roughly 49.7 days. Since my sketch is intended to run for longer than that, I need to make sure the rollover does not make my sketch fail. I can easily detect the rollover condition ( currentMillis < previousMillis ), but I am not sure what to do then. Thus my question: what would be the proper/simplest way to handle the millis() rollover?
Edgar Bonet
asked Jun 12, 2015 at 11:16
Edgar Bonet Edgar Bonet
42k 4 4 gold badges 36 36 silver badges 75 75 bronze badges
Editorial note: This is not exactly a question of mine, rather a tutorial in a question/answer format. I have witnessed a lot of confusion on the Internet (including here) about this topic, and this site seems like the obvious place to search for an answer. This is why I am providing this tutorial here.
Jun 12, 2015 at 11:18
I would do previousMillis += interval instead of previousMillis = currentMillis if I wanted a certain frequency of results.
Feb 7, 2016 at 9:54
@Jasen: That’s right! previousMillis += interval if you want constant frequency and are sure that your processing takes less than interval , but previousMillis = currentMillis for guaranteeing a minimum delay of interval .
Feb 7, 2016 at 10:02
We really need a FAQ for things like this.
Aug 11, 2017 at 10:47
One of the «tricks» I use is to lighten the load on the arduino by using the smallest int that contains the interval. For instance, for max 1 minute intervals, I write uint16_t previousMillis; const uint16_t interval = 45000; . uint16_t currentMillis = (uint16_t) millis(); if ((currentMillis — previousMillis) >= interval) .
Nov 30, 2018 at 14:42
4 Answers 4
Short answer: do not try to “handle” the millis rollover, write rollover-safe code instead. Your example code from the tutorial is fine. If you try to detect the rollover in order to implement corrective measures, chances are you are doing something wrong. Most Arduino programs only have to manage events that span relatively short durations, like debouncing a button for 50 ms, or turning a heater on for 12 hours. Then, and even if the program is meant to run for years at a time, the millis rollover should not be a concern.
The correct way to manage (or rather, avoid having to manage) the rollover problem is to think of the unsigned long number returned by millis() in terms of modular arithmetics. For the mathematically inclined, some familiarity with this concept is very useful when programming. You can see the math in action in Nick Gammon’s article millis() overflow . a bad thing?. For the problem at hand, what’s important to know is that in modular arithmetics the numbers “wrap around” when reaching a certain value – the modulus – so that 1 − modulus is not a negative number but 1 (think of a 12 hour clock where the modulus is 12: here 1 − 12 = 1).
For those who do not want to go through the computational details, I offer here an alternative (hopefully simpler) way of thinking about it. It is based on the simple distinction between instants and durations. As long as your tests only involve comparing durations, you should be fine.
Note on micros(): Everything said here about millis() applies equally to micros() , except for the fact that micros() rolls over every 71.6 minutes, and the setMillis() function provided below does not affect micros() .
Instants, timestamps and durations
When dealing with time, we have to make the distinction between at least two different concepts: instants and durations. An instant is a point on the time axis. A duration is the length of a time interval, i.e. the distance in time between the instants that define the start and the end of the interval. The distinction between these concepts is not always very sharp in everyday language. For example, if I say “I will be back in five minutes”, then “five minutes” is the estimated duration of my absence, whereas “in five minutes” is the instant of my predicted coming back. Keeping the distinction in mind is important, because it is the simplest way to entirely avoid the rollover problem.
The return value of millis() could be interpreted as a duration: the time elapsed from the start of the program until now. This interpretation, however, breaks down as soon as millis overflows. It is generally far more useful to think of millis() as returning a timestamp, i.e. a “label” identifying a particular instant. It could be argued that this interpretation suffers from these labels being ambiguous, as they are reused every 49.7 days. This is, however, seldom a problem: in most embedded applications, anything that happened 49.7 days ago is ancient history we do not care about. Thus, recycling the old labels should not be an issue.
Do not compare timestamps
Trying to find out which among two timestamps is greater than the other does not make sense. Example:
unsigned long t1 = millis(); delay(3000); unsigned long t2 = millis(); if (t2 > t1)
Naively, one would expect the condition of the if () to be always true. But it will actually be false if millis overflows during delay(3000) . Thinking of t1 and t2 as recyclable labels is the simplest way to avoid the error: the label t1 has clearly been assigned to an instant prior to t2, but in 49.7 days it will be reassigned to a future instant. Thus, t1 happens both before and after t2. This should make clear that the expression t2 > t1 makes no sense.
But, if these are mere labels, the obvious question is: how can we do any useful time calculations with them? The answer is: by restricting ourselves to the only two calculations that make sense for timestamps:
- later_timestamp — earlier_timestamp yields a duration, namely the amount of time elapsed between the earlier instant and the later instant. This is the most useful arithmetic operation involving timestamps.
- timestamp ± duration yields a timestamp which is some time after (if using +) or before (if −) the initial timestamp. Not as useful as it sounds, since the resulting timestamp can be used in only two kinds of calculations.
Thanks to modular arithmetics, both of these are guaranteed to work fine across the millis rollover, at least as long as the delays involved are shorter than 49.7 days.
Comparing durations is fine
A duration is just the amount of milliseconds elapsed during some time interval. As long as we do not need to handle durations longer than 49.7 days, any operation that physically makes sense should also make sense computationally. We can, for example, multiply a duration by a frequency to get a number of periods. Or we can compare two durations to know which one is longer. For example, here are two alternative implementations of delay() . First, the buggy one:
void myDelay(unsigned long ms) < // ms: duration unsigned long start = millis(); // start: timestamp unsigned long finished = start + ms; // finished: timestamp for (;;) < unsigned long now = millis(); // now: timestamp if (now >= finished) // comparing timestamps: BUG! return; > >
And here is the correct one:
void myDelay(unsigned long ms) < // ms: duration unsigned long start = millis(); // start: timestamp for (;;) < unsigned long now = millis(); // now: timestamp unsigned long elapsed = now - start; // elapsed: duration if (elapsed >= ms) // comparing durations: OK return; > >
Most C programmers would write the above loops in a terser form, like
while (millis() < start + ms) ; // BUGGY version
while (millis() - start < ms) ; // CORRECT version
Although they look deceptively similar, the timestamp/duration distinction should make clear which one is buggy and which one is correct.
What if I really need to compare timestamps?
Better try to avoid the situation. If it is unavoidable, there is still hope if it is known that the respective instants are close enough: closer than 24.85 days. Yes, our maximum manageable delay of 49.7 days just got cut in half.
The obvious solution is to convert our timestamp comparison problem into a duration comparison problem. Say we need to know whether instant t1 is before or after t2. We choose some reference instant in their common past, and compare the durations from this reference until both t1 and t2. The reference instant is obtained by subtracting a long enough duration from either t1 or t2:
unsigned long reference_instant = t2 - LONG_ENOUGH_DURATION; unsigned long from_reference_until_t1 = t1 - reference_instant; unsigned long from_reference_until_t2 = t2 - reference_instant; if (from_reference_until_t1 < from_reference_until_t2) // t1 is before t2
This can be simplified as:
if (t1 - t2 + LONG_ENOUGH_DURATION < LONG_ENOUGH_DURATION) // t1 is before t2
if ((signed long)(t1 - t2) < 0) // works with gcc // t1 is before t2
The keyword signed above is redundant (a plain long is always signed), but it helps make the intent clear. Converting to a signed long is equivalent to setting LONG_ENOUGH_DURATION equal to 24.85 days. The trick is not portable because, according to the C standard, the result is implementation defined. But since the gcc compiler promises to do the right thing, it works reliably on Arduino. If we wish to avoid implementation defined behavior, the above signed comparison is mathematically equivalent to this:
#include if (t1 - t2 > LONG_MAX) // too big to be believed // t1 is before t2
with the only problem that the comparison looks backwards. It is also equivalent, as long as longs are 32-bits, to this single-bit test:
if ((t1 - t2) & 0x80000000) // test the "sign" bit // t1 is before t2
The last three tests are actually compiled by gcc into the exact same machine code.
How do I test my sketch against the millis rollover
If you follow the precepts above, you should be all good. If you nevertheless want to test, add this function to your sketch:
#include void setMillis(unsigned long ms) < extern unsigned long timer0_millis; ATOMIC_BLOCK (ATOMIC_RESTORESTATE) < timer0_millis = ms; >>
and you can now time-travel your program by calling setMillis(destination) . If you want it to go through the millis overflow over and over again, like Phil Connors reliving Groundhog Day, you can put this inside loop() :
// 6-second time loop starting at rollover - 3 seconds if (millis() - (-3000) >= 6000) setMillis(-3000);
The negative timestamp above (-3000) is implicitly converted by the compiler to an unsigned long corresponding to 3000 milliseconds before the rollover (it is converted to 4294964296).
What if I really need to track very long durations?
If you need to turn a relay on and turn it off three months later, then you really need to track the millis overflows. There are many ways to do so. The most straightforward solution may be to simply extend millis() to 64 bits:
uint64_t millis64()
This is essentially counting the rollover events, and using this count as the 32 most significant bits of a 64 bit millisecond count. For this counting to work properly, the function needs to be called at least once every 49.7 days. However, if it is only called once per 49.7 days, for some cases it is possible that the check (new_low32 < low32) fails and the code misses a count of high32 . Using millis() to decide when to make the only call to this code in a single "wrap" of millis (a specific 49.7 day window) could be very hazardous, depending on how the time frames line up. For safety, if using millis() to determine when to make the only calls to millis64(), there should be at least two calls in every 49.7 day window.
Keep in mind, though, that 64 bit arithmetic is expensive on the Arduino. It may be worth to reduce the time resolution in order to stay at 32 bits.
