How to Use Arduino millis()


The Arduino millis() function is a very useful function that returns a number representing milliseconds since reset. On this page you can find out how to use it effectively for event timing and delays as well as learning how it works in detail.

Using Arduino millis as a Delay Timer

The millis() function returns the current time in milliseconds (1/1000th of a second) from when you powered up the board (or reset it). It gives you a way of measuring time from within your program, which is quite different to the delay() function that gives no feedback about time at all.

Measuring a time period using millis, is simply a matter of comparing current time to the time value stored in a variable. As you go round a loop you continuously perform a simple bit of maths: 

millis() - stored_time

This gives you the elapsed time in milliseconds.

This action is shown in the pseudo code below:

LOOP
   ...
   // An event happens
   if (event==true) stored_time = millis();
   ...
   elapsed_time = millis() - stored_time;
   ...
END_LOOP

The above pseudo code snippet results in the variable "elapsed_time" measuring time in milliseconds since the event occurred. An event could be a button press or some action from another part of the program.

Perhaps you want to send out a serial message at one second intervals so in this case the event would be message transmission and you would wait until elapsed time was greater than 1000 (1000ms = 1 second) before sending the next serial message (and storing the time again).

The millis() function is driven by a millisecond timer interrupt that increments an unsigned long every time it activates and just returns the value of that variable.

Arduino millis LED Flashing

The following example shows you how to use millis() to give a non blocking delay. A non blocking delay is a type of delay that allows other code to operate even though there is a delay operation elsewhere. This is very different to using the function delay() where your code stops processing (except for interrupts) and does nothing much for the duration of the delay period.

The pesudo code shown below gives a non blocking delay of 500ms and this delay is repeatedly triggered. This shows you how to create an Arduino millis timer that can have a delay time of any length (up to 49.7 days).


    if ( (millis()-oldtime) > 500) {
       oldtime = millis();

      // Do something every after 500ms

    }
For an action every 12 hours you would use a delay interval of (12 * 60 * 60 * 1000) = 43200000ms replacing 500 with 43200000L (The L indicates a long value to the comiler). For days you need more milliseconds e.g. 3 days would require (3 * 24 * 60 * 60 * 1000) =259200000L again replacing 500 with 259200000L. To stop the repetition of the action see here.

Example 1 : Delay code


void setup (void) {
}
#define LED 13 void loop(void){ static uint8_t tog=0; static uint32_t oldtime=millis(); if ( (millis()-oldtime) > 500) { oldtime = millis(); tog = ~tog; // Invert if (tog) digitalWrite(LED,HIGH); else digitalWrite(LED,LOW); } }

Code Operation : Arduino millis as delay operation

The Arduino code above shows the loop function which, in this case, is the only code required; Set the loop function to be empty as you don't need it to initialise anything i.e. you don't need to start the serial port or SPI interfaces:

Loop Code operation : For Non Blocking delays

The value of oldtime is set to a value of millis() at the start. if the value of millis(), which is increasing every millisecond, is greater than 500 counts above oldtime then the conditional expression in the if statement becomes true. This means 500 milliseconds have past since the value of oldtime was set i.e. a delay of 500ms.

Within the body of the if-statement the LED is toggled from its previous state. This means the LED is flashing at a rate of 1Hz (500ms on and 500ms off) or once a second.

Since millis() is only tested by the conditional if statement, you can add other code within the loop to do other useful work i.e. using Arduino millis() in this way does not stop the processor.

TIP: it is vital that you set oldtime within the if-statement to the current millis() value so that the next delay period can be performed otherwise the if-statement expression would always be true after the first time the expression became true.

Note: The type uint32_t is the same type as "unsigned long". uint32_t is used in embedded programming as it directly specifies the number of bits within the type, whereas "unsigned long" may have a different number of bits for different compilers.

Time Conversions for Arduino millis

Arduino millis to Seconds

Since millis is a shortened engineering term for milliseconds and milli stands for 1/1000th there are 1000 milliseconds in one second. Therefore to count seconds divide millis by 1000.

Seconds = millis()/1000;

Often you will want to work with millis() in fractions of a second e.g. for flashing an LED every 400ms you would compare millis() to 200 - using an on/off toggle for every 200ms results in a time period of 400ms.

Arduino millis to Hours

If you want to get to hours when using Arduino millis as a Timer you need to do some more division:

Minutes = ( millis()/1000 ) / 60;

Hours = ( ( millis()/1000 ) / 60 ) / 60;

Arduino millis to Hours Minutes Seconds and Days

The full set of time values is here:

Days = Hours/24;

Hours = Minutes / 60;

Minutes = Seconds / 60;

Seconds = millis()/1000;

As noted previously days can only have a maximum value of 49.7.

Note: Since Hours. Minutes and Seconds and Days can only have a maximum value below 255 you can use type uint8_t for each of the above quantities. uint8_t is the same as "unsigned char".

Arduino millis limit

So how long can you measure (and why would you care?). The function millis() returns a magic number that appears out of the depths of Arduino code but as an engineer you need to know what it is and how it is created.  You need to know because all systems have limits which trip you up and make your system fail.

The maximum time that can be measured depends on the type of variable used to store the millis data which is an unsigned long and using this type allows you to measure just over 49 days. If your project will never be on for longer than 49 days then you don't have a problem.

For the Arduino the max value of millis is :

4,294,967,295 or (0xffffffff)

This is because the Arduino millis data type is :

unsigned long (which can also be written as uint32_t)...

...in which you can more easily see the number of bits in the type.

The count that an unsigned long is capable of holding is: pow(2,32)-1 or 4,294,967,295 or 4 billion 294 million 967 thousand and 295. So if every count is worth a millisecond then how many days is that?

First divide by 1000 for the seconds, then by 60 for the minutes then by 60 for the hours then by 24 for the days = ~49.71 days.

After aprroximately 50 days (or a bit more than 49.71 days) the timer wraps round to zero and this is the Arduino millis overflow problem.

Note: Arduinos may have a resonator (3 pin) instead of a crystal (2 pin) and these are not as accurate as crystals.

If you are designing a project that must time for more than 49 days then this could cause a problem because at the wrap around point, time that was increasing incrementally in milliseconds, suddenly goes to zero. If you recorded a time stamp for a data logging device using millis after 49.71 days the time stamp would return to the start time i.e. wrong.

Another problem is using the timer for delays e.g. to flash an LED, where you wait for the timer to reach the current time plus 500ms say, if the timer wrapped within that 500ms then the time to wait would instead be 50 days which is a bit of a long time to flash an LED off!

TIP: If you want to go beyond 50 days, add another variable that acts as extra bits in front of the unsigned long e.g. an unsigned char would extend time by 256 * 50 days. Increment it every time the millis() time wraps around. The downside is that you would need to include that 8 bit quantity in all time calculations so you would need to make your own "greater than" operation etc.

How to detect the arduino millis overflow

One easy way to detect the overflow is to do what you should not do and use a signed long (BUT ONLY FOR THE DECTION OF OVERFLOW). See here for why you Must Not Use Long (a signed integer) for counting time when using millis().

When the overflow occurs the hex value of a 32 bit integer goes from 0xFFFFFFFF to 0. For a signed long the left most bit is the sign bit. Therefore at the overflow the value (interpreted as a signed long will go from negative to positive).

That is the signed representation of 0xFFFFFFFF is -1 ~ negative, and the signed interpretation of 0 is positive (or at least you can use the less than operator to detect negative values).

Note: The following code has not been tested.
uint32_t time;
int32_t detect;
int8_t halfway;

void setup(void) {
   time=0;
   detect=0;
   halfway=0;
   overflow=0;
}

void loop(void) {

    time=millis();
    detect= (long)time;

    if (detect<0) halfway = 1; // this is the millis value 0x80000000
    if (halfway && detect>0){
        halfway=0;
        overflow=1;
    }

    if (overflow) {
       // Do something about the overflow
       overflow = 0;
    }
)

Arduino millis vs delay

Lets just say at the start of this discussion - "Don't Use delay()". This will save you time. Read on to find out why...or have a look here (a different page on delay() - examples and explanation of why you should not use delay() are in that page.

Arduino milis() is an interrupt driven function meaning that it is always operating in the background while your code is working. Interrupts are used to update the value that millis() outputs so that after every millisecond that value will increase by one. You can access the current value of the millis() at any time simply by calling the function millis() and placing the result into your own unsigned long variable (or you can compare the millis() output to another constant).

The function delay() on the other hand is not interrupt driven but just causes a "Do Nothing" loop to execute wasting processor time - it does not return any value. The function delay() depends on the interrupt driven output from Timer0. Therefore  delay() can not be used within an interupt service routine since within an ISR is interrupts are turned off.

Since it is not interrupt driven no other code can operate - it just effectively stops the processor from doing anything else while it counts down a delay time.

Note: The above discussion is talking about main code operation - interrupts still interrupt main code and operate e.g. timers or I2C interfaces etc. still work.
Note: millis() does not always increment by one due to the way the timing is calculated within the interrupt routine. The link takes you to a detailed simulation of millis() in this page.

Arduino millis not accurate

No it is not! Well, it does quite a good job.

There are two reasons :

  1. The clock source - This is true of all crystal oscillator based systems but some Arduino boards use resonator that performs worse than a crystal.
  2. The implementation - In general standard crystal oscilaltor frequences that are easy for humans to read e.g.16MHz are not divisible by a binary number so you can't get an exact output period. Arduino code adjusts for this problem using a correction algorithm..

Reason One - The Clock Source

Arduino millis is not accurate and one reason for this is that most Arduino boards come with a 16MHz resonator fitted. This is a cheap oscillation component that is OK for most applications. it will drift around with temperature but will always be approximately 16MHz.

If you are looking for accuracy, you will need an Arduino with a crystal on board (or make up your own bare-bones version with a crystal). You can tell if yours only has a resonator as resonators are three pin devices whereas crystals are two pin devices.

The problem is that to retro fit a crystal (it can be done!) to an Arduino board requires two extra components to make the crystal oscillate correctly (capacitive loads are required). These are two capacitors, each connected to one pin of the crystal and then to ground. They range from 12pF to 33pf - check for the correct capacitor specified by the manufacturer - or just use a couple of 15pF. You of course also need to attach the crystal to the clock inputs of the microcontroller (same connections as the resonator - but not the middle ground one).

A better solution is to use an external RTC

The accuracy of the crystal is specified as ppm and is usually around 100ppm (a 32kHz watch crystal can get to 20ppm - not much use for a microcontroller unless you need low power operation).

If you really want an accurate timestamp then use an ovenised timekeeping chip (a cheap 2ppm one is the DS3232). This does not output a clock for direct use by the microcontroller so you could keep the resonator and use the DS3232 as the accurate timestamp device.

Reason Two - Timer 0 implementation

This second reason is not really that Arduino millis is not accurate (it becomes accurate) but more about the implementation of the timer which is clever but will cause a small jitter. The interrupt for the millisecond timer (using Timer 0 in wiring.c) uses prescalers to divide down the main clock 16MHz but the output of the timer is off by a small amount (you can not divide down a 16MHz clock using divide by 2 divider hardware to get an exact millisecond output - -the closest you can get is 1024us). When the error gets bug enough a correction factor is used to adjust the timer value.

So in the long term the millis() timer is accurate but in the short term it could be out by a small amount.

Timer0 interrupt Clock Cycles

Timer 0 is setup so that it has a prescaler of 64. It is an 8 bit timer so overflows every 256 counts. Therefore for a 16MHz clock, the repeat time for timer 0 is (1.0/16e6)*256*64 = 0.001024 of 1024 us which is close to 1ms but not actually there.

How Timer 0 is corrected for 1ms operation

There are two variables used in the correction and a few macro definitions:

clockCyclesPerMicrosecond gives a result of 16 for a 16MHz clock.

Two other macros are:

clockCyclesToMicroseconds(a) = ( (a) / clockCyclesPerMicrosecond )

microsecondsToClockCycles(a) = ( (a) * clockCyclesPerMicrosecond )

Within wiring.c the following definitions are made:

MICROSECONDS_PER_TIMER0_OVERFLOW =
    clockCyclesToMicroseconds( 64 * 256) = 16384 / 16 = 1024

Since clockCyclesPerMicrosecond is 16

and

Timer 0 is prescaled by 64 and overflows after 256 prescaled clocks so it triggers an interrupt after 64 * 256 clocks cycles.

Which just shows it is a long winded way of doing a simple calculation that does have the advantage of being generic so it means for different clocks a different result will occur. However the code comments state:

// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)

So the comments state that the The fractional timings calculation (below) work for 8 and  8Mhz and 16MHz.

The following calculations are for a 16MHz clock.

MILLIS_INC = ( MICROSECONDS_PER_TIMER0_OVERFLOW / 1000 ) = 1.024

The above when used later inserts 1.024 into the code at the point of use which is in fact when an unsigned long is incremented so that value would be incremented by one.

The more interesting definitions are the parts that deal with fractional increments

Comments in wiring.c are

// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)


FRACT_INC = ( MICROSECONDS_PER_TIMER0_OVERFLOW % 1000 >> 3 )
    = 24 >> 3 = 3 (16MHz)

FRACT_MAX = ( 1000 >>3 )
    = 125

The reason stated for doing the above right shifts is so that the numbers fit into a byte.

Three variables are used in the correction and output of the millis value (timer0_millis - below).

unsigned long timer0_overflow_count - only used in microseconds calculation.

unsigned long timer0_millis - the value output by millis().

Byte timer0_fract

Every time in the interrupt:

timer0_millis is increased by MILLIS_INC (or by 1) - this is the millis() output value.

timer0_fract is increased by FRACT_INC (or by 3).

timer0_overflow_count is increased by one - this is the unadjusted Timer0 interrupt count.

If necessary a correction is made to timer0_millis when the accumulated error gets too big.

Interrupt code (ISR) for TIMER0

The folliowng code is contained within the Interrupt Service Routine and performs the millisecond update which updates the last 3 variables that are globals. The variable timer0_millis is the value returned by the millis() function.

Fractional adjustment in the interrupt vector:

// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;

m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
    f -= FRACT_MAX;
    m += 1;
}

timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;

In the above code, m and f are defined as local variables to allow the compiler to use register which are faster (check this actually happens by examining output assembler code).

The fractional action is that if timer0_fract is greater or equal than FRACT_MAX (125) then subtract FRACT_MAX and increase timer0_millis by one (>= is used since 125 is not a multiple of 3).

So every time that the interrupt fires 3 is added until the accumulated error (timer0_fract) is greater or equal to 125 which will be when time0_fract 1st reaches 126 or 42 interrupt calls. Interrupts are called with a time period of 1024 microseconds therefore the time before a millisecond correction is made is 42 *1024 = 43008us.

Therefore you will get a correction of the Arduino millisecond timer every ~43ms.

Since the timer0 interrupt is running slow the millisecond timer is incremented by 1 every ~43ms

The first time round the loop after correction 1 is left in timer0_fract.

The second time 2 is left in timer0_fract.

So small fractional errors eventually get corrected after >125 times ~43ms.

TCL simulation

The following TCL program simulates the action of the interrupt code for Timer 0:

proc sim_arduino_timer0 {} {

console show
set c 0

 set timer0_millis 0
 set timer0_frac 0
 set MILLIS_INC 1
 set FRACT_INC 3
 set FRACT_MAX 125
 set m 0
 set f 0

 set line ""

 for {set i 0} {$i<1100} {incr i} {

    puts "$m $f"

    set m [expr $m + $MILLIS_INC]
    set f [expr $f + $FRACT_INC]
       if {$f >= $FRACT_MAX} {
          set f [expr $f - $FRACT_MAX]
          incr m
       }
    }

  update idletasks

}

sim_arduino_timer0

Some of the output it gererates is shown below (left is the millis increment and the right is the fractional part).:


0 0
1 3
2 6
3 9
4 12
5 15
6 18
7 21
8 24
9 27
10 30
11 33
12 36
13 39
14 42
15 45
16 48
17 51
18 54
19 57
20 60
21 63
22 66
23 69
24 72
25 75
26 78
27 81
28 84
29 87
30 90
31 93
32 96
33 99
34 102
35 105
36 108
37 111
38 114
39 117
40 120
41 123
43 1
44 4
45 7 

The first adjustment is when the millis output is at 42 and the fractional part has just passed 125. Instead of the expected output of 42 an adjusted output of 43 is output instead i.e the correction algorithm jumps past 42 and so millis() does not output the value of 42 at all. At about every 42 ms this correction is made again.

Arduino Program showing millis and micros output

The next program shows the actual output from an Arduino uno with a resonator as the clock. This is the physical result on real hardware showing the millisecond jitter at 42 ms.

void setup() {
  Serial.begin(230400);
}

void loop() {
static unsigned int c=0,stop=0;
unsigned long mil,mic;

  // Note Reduce this parameter the more is done in the loop! to get 3~4 outputs
// delayMicroseconds(50);
  if (c<2100 && stop==0) {

     c++;
     mil = millis(); // Capture as close together as possible
     mic = micros();
     Serial.print( mil );
     Serial.print(" ");
     Serial.println( mic );

  } else stop =1;
}

The following table shows millis() on the left and micros() on the right.
The relevant section is here (the first adjustment at millis()==42 ):

40 41164
40 41604
41 42040
41 42480
41 42920 *** millis skips a beat here
43 43368 ***
43 43808
44 44244
44 44684
45 45132
45 45572
45 46016
46 46460
46 46892
47 47340

The above program takes samples of the current millis() and micros() timer outputs, and outputs more than one result since the interrupt timer is asynchronous to the main loop code i.e. you don't know when it is going to change so to see changes you have to make the loop run faster than the millisecond timer (hence the high baud rate and higher sample number 2100). You can see here millis on the left and micros on the right - micros is 4us inaccurate so is better than millis(). Observe the transition point at 42 ( where 42 is not output by millis() ) i.e. the same as the simulation output which is reassuring!

You can also see that the millis() output is quite a way out just before the transition e.g. reading millis at the 41st change means you should be reading from 42.0 to 42.9 instead you get 41 (the worst value is just before the adjustment where millsi() outputs 41ms and the actual time is 42.9ms.

You get a jump as the correction factor is added so the millis() function never outputs 42ms and instead outputs 43 as the next result - this brings the millis() output back into alignment with the correct time. This correction process repeats all the time millis() is operating.

However the correction factor at the 42nd interrupt corrects the millis output to 43.3 for millis output 43 so in the long term the millisecond timer is accurate. This oscillation around the correct output is called jitter.

Other values of Arduino Crystal Clock

Elsewhere in the code (microsecond delay) it talks about the following frequencies:

'Assumes a 1, 8, 12, 16, 20 or 24 MHz'

(the 24MHz clock is for overclocking - doing something not specified by the data sheet).

The question is: Would millis be accurate for these crystals?

The following results show that for most cases the millis() output would be accurate but not for 12, 20 and 24MHz. You would need to change the operation of the Arduino code to make millis() output an accurate result.

Microseconds per timer0 overflow:

(64 * 256.0) / 1 = 16384  - 1MHz - no error. 

(64 * 256.0) / 2 = 8192  - 2MHz - no error. 

(64 * 256.0) / 4 = 4096  - 4MHz - no error. 

(64 * 256.0) / 8 = 2048  - 8MHz - no error.  

(64 * 256.0) / 12 = 1365.33 - 12Mhz - millis() would have an error.

(64 * 256.0) / 16 = 1024   - 16MHz - no error.

(64 * 256.0) / 20 = 819.2   - 20MHz - millis() would have an error.

(64 * 256.0) / 24 = 682.66 - 24MHz - millis() would have an error.

The calculation below is FRACT_INC (microseconds_per_interrupt % 1000) >> 3

Frequency (MHz)
FRACT_INC increments
1
48
2
24
4
12
8
6
12
45 (Not accurate millis o/p).
16
3
20
102 (Not accurate millis o/p).
24
85 (Not accurate millis o/p).

Note: Observe how FRACT_INC values blow up when the frequency used is not an exact factor of the Timer0 prescaler and overflow result. since prescalers and timers use 8 or 16 bits it follows that for this scheme the clock frequency must be a base2 value i.e. 1,2,4,8,16,32 etc.

Note: At lower crystal frequencies the value of FRACT_INC is larger but the shifted right value of 1000 ms calculations remains the same - it has a value of 125 - to fit into a byte) . Therefore the jitter will be far greater using lower frequences i.e. the millisecond clock will be corrected more often. e.g. for 16MHz 125/3 is 41.6 but 125/48 is 2.6 - so at 16Mhz the clock is corrected every 42 ms while at 1MH it is corrected every 3ms.

What happens if you use Arduino millis long?

If you decide to use a "long" type definition for the comparison storage variable then you are using a signed quantity instead of the unsigned quantity output by the millis function.

If you were to make a timer using "long" and output the value of long using a Serial command (or any command that converts an integer quality to a string) such as:

Serial.println(timer);

...where timer is specified as long and updates, perhaps to seconds as:

timer = millis()/1000;

Then at some point in the operation of your code (when the timer reaches the mid point) the leftmost bit of the unsigned output of millis() will become high and the print function will interpret this a the sign bit. Therefore it will output a negative number and Arduino millis goes negative (or appears to be negative). For a signed calculation the value is interpreted as negative and the print function treats long as a signed quality and therefore outputs a negative number.

Try this of code in Arduino

uint32_t ulong;
int32_t slong,minus1,plus1,s1;
void setup(void) {

    Serial.begin(115200);

    minus1 = -1;
    plus1 = 1;

    Serial.println("\n Observe plus 1");
    s1 = plus1;
    ulong = (unsigned long) s1;
    Serial.print(" signed plus 1 dec :");Serial.println(s1);
    Serial.print(" signed plus 1 hex :");Serial.println(s1,HEX);
    Serial.print(" unsigned plus 1 dec :");Serial.println(ulong);
    Serial.print(" unsigned plus 1 hex :");Serial.println(ulong,HEX);


    Serial.println("\n Observe minus 1");
    s1 = minus1;
    ulong = (unsigned long) s1;
    Serial.print(" signed dec :");Serial.println(s1);
    Serial.print(" signed hex :");Serial.println(s1,HEX);
    Serial.print(" unsigned dec :");Serial.println(ulong);
    Serial.print(" unsigned hex :");Serial.println(ulong,HEX);

    Serial.println("\n suppose millis() reaches unsigned value 0xffffffff-1");
    Serial.println(" Observe the signed value");
    s1 = (long) (0xffffffff-1);
    ulong = (unsigned long) s1;
    Serial.print(" signed minus 1 dec :");Serial.println(s1);
    Serial.print(" signed hex :");Serial.println(s1,HEX);
    Serial.print(" unsigned dec :");Serial.println(ulong);
    Serial.print(" unsigned hex :");Serial.println(ulong,HEX);

    Serial.println("\n Observe signed value of 0x7fffffff - no problem");
    s1 = (long) (0x7fffffff);
    ulong = (unsigned long) s1;
    ulong = (unsigned long) s1;
    Serial.print(" signed dec :");Serial.println(s1);
    Serial.print(" signed hex :");Serial.println(s1,HEX);
    Serial.print(" unsigned dec :");Serial.println(ulong);
    Serial.print(" unsigned hex :");Serial.println(ulong,HEX);

    Serial.println("\n Observe signed value of 0x80000000 (The sign bit)");
    s1 = (long) (0x80000000);
    ulong = (unsigned long) s1;
    ulong = (unsigned long) s1;
    Serial.print(" signed dec :");Serial.println(s1);
    Serial.print(" signed hex :");Serial.println(s1,HEX);
    Serial.print(" unsigned dec :");Serial.println(ulong);
    Serial.print(" unsigned hex :");Serial.println(ulong,HEX);
}

void loop(void) {
}

This is the result you'll get:


 Observe plus 1
 signed dec :1
 signed hex :1
 unsigned dec :1
 unsigned hex :1

 Observe minus 1
 signed dec :-1
 signed hex :FFFFFFFF
 unsigned dec :4294967295
 unsigned hex :FFFFFFFF

 suppose millis() reaches unsigned value 0xffffffff-1
 Observe the signed value
 signed dec :-2
 signed hex :FFFFFFFE
 unsigned dec :4294967294
 unsigned hex :FFFFFFFE

 Observe signed value of 0x7fffffff - no problem
 signed dec :2147483647
 signed hex :7FFFFFFF
 unsigned dec :2147483647
 unsigned hex :7FFFFFFF

 Observe signed value of 0x80000000 (The sign bit)
 signed dec :-2147483648
 signed hex :80000000
 unsigned dec :2147483648
 unsigned hex :80000000

You can see that the sign bit is very important (the left most bit) and if you use signed types you will get negative output numbers displayed, even though the unsigned version is correct i.e. it has the expected bit value - or hex value shown.

Also shown is the flip over point where using signed long is OK until you reach 2147483647 (0x7fffffff) add one to that and you get -2147483648 (0x80000000). In terms of days a timer will appear to work fine for ~25 days and then adding one results in a negative output. The explanation of number of days that millis() covers is here.

The easy way round that is to use unsigned long (uint32_t) when dealing with millis().

More Examples

How to make a simple scheduler using Arduino millis

The aim of this Arduino millis example is to make a simple scheduler algorithm to start different actions at different times. This is only a simple example and you can find multitasking schedulers that transfer operation to a different task saving variables so that tasks can be interrupted stopped and restarted. There will also be the concept of flags that allow communication between tasks. This simple example is definitely not that type but it can be useful nevertheless.

#define LED 13

void setup(void) {
     Serial.begin(115200);
     Serial.println("Simple Scheduler");

     pinMode(LED,OUTPUT);
}

void loop(void){
static uint8_t tog=0;
static uint32_t ledtime= millis(),s1=ledtime,s2=s1,s3=s1,s4=s1;

    // LED Flash
    if ( (millis()-ledtime) > 500) {
       ledtime = millis();

       tog = ~tog; // Invert
       if (tog) digitalWrite(LED,HIGH); else digitalWrite(LED,LOW);
    }

    // 10 second message
    if ( (millis()-s1) > 10000) {
       s1 = millis();

       Serial.println("10 SECONDS");
    }

    // 5 second message
    if ( (millis()-s2) > 5000) {
       s2 = millis();

       Serial.println("five SECONDS");
    }

    // 3 second message
    if ( (millis()-s3) > 3000) {
       s3 = millis();

       Serial.println("-3- SECONDS");
    }

    // 2 second message
    if ( (millis()-s4) > 2000) {
       s4 = millis();

       Serial.println("*2* SECONDS");
    }

} 

Scheduling initial times for offset start using Arduino millis

TIP: TIP: To avoid everything happening at the same time set the initial conditions to be offset from each other

You can offset the start times of each timer so that they are not all a multiple of 1000, because if they are then they will fire more or less at the same time and serial output will be generated, and serial output takes a bit of time thus changing the actual time that subsequent time matches happen.

For example you could write the following initialisation (using random offsets):

static uint32_t ledtime=millis(),s1=ledtime+66,s2=s1+90,s3=s1+187,s4=s1+231;

This would mean that the starting times of each timed output are offset from each other - only the start times - the subsequent repeat times would be at the repeat time specified in the code - they would still be offset so they would not happen at the exact same time. So the processor would not have to do the actions associated with each timeout at the same time. Therefore the code will operate more smoothly and not have to do a big processing burp!

How to make a one-shot timer with Arduino millis

This code only does one serial output action action after a set time and then stops. Although using Arduino millis() allows other operations to continue i.e. the LED keeps flashing but only one message is output.

#define LED 13

void setup(void) {

     Serial.begin(115200);
     Serial.println("Simple Scheduler");

     pinMode(LED,OUTPUT);
}

void loop(void){
static uint8_t tog=0,s1done=0;
static uint32_t ledtime= millis(),s1=ledtime;

    // LED Flash
    if ( (millis()-ledtime) > 500) {
       ledtime = millis();

       tog = ~tog; // Invert
       if (tog) digitalWrite(LED,HIGH); else digitalWrite(LED,LOW);
    }

    // 3 second message - one shot
    if ( !s1done && (millis()-s1) > 3000) {
       s1 = millis();
       s1done = 1;

       Serial.println("-3- SECONDS ONE SHOT ONLY");
    }
} 

New! Comments

Have your say about what you just read! Leave me a comment in the box below.




Privacy Policy | Contact | About Me

Site Map | Terms of Use





Subscribe to the MicroZine Newsletter and collect your free microcontroller Ebooks, download project code and more...


Visit our Facebook Page:

   Click Here

Recent Articles

  1. The Essential I2C Tutorial: All you need to know about I2C...

    I2C tutorial: Learn all about the 2 wire I2C serial protocol. Learn how easy it is to use, how it works and when to use it...

    Read more

  2. [Arduino] : Using the 74HC595 shift register with shiftOut().

    The Essential Guide to the 74HC595; What it is and how you can easily use one in any of your projects.

    Read more

  3. How to use a DS18B20 on the Arduino for easy temperature measurement

    How to Easily Use the DS18B20 Maxim One-Wire thermometer with Arduino example code showing external or parasitic power modes.

    Read more

  4. Secrets of Arduino millis: How it works and how to use it.

    How to use Arduino millis() to make delays, one-shots and simple schedulers plus simple analysis of arduino millis() code; Find out how it works.

    Read more

  5. The TCS230 Color Sensing Chip: How it works and how to use it.

    How to use the TCS230 (/TCS3200) Color detector chip and easily add it to any of your projects.

    Read more

  6. Pic Programmer Types

    Which pic programmer do you need? This page discusses PIC programmers and gives some essential information on choosing or building your own programmer.

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's

SUPERB and FANTASTIC."

- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan

bursach<at>gmail.com<

"Awesome site,
very, very easy and nice
to navigate!"


- Matt
matt_tr<at>
wolf359.cjb.net


Learn Microcontrollers

"Interested in
Microcontrollers?"

Sign up for The
Free 7 day guide:

FREE GUIDE : CLICK HERE


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."


- Dave

de_scott<at>bellsouth.net

"Your site is a great
and perfect work.
congratulations."


- Suresh

integratredinfosys<at>
yahoo.com

"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.
Regards."


- Anon

Back to Top