Introduction: Arduino Timing Methods With Millis()

About: - everything for the maker and electronics enthusiast, with free delivery worldwide

In this article we introduce the millis(); function and put it to use to create various timing examples.

Millis? Nothing to do with lip-syncers… hopefully you recognised milli as being the numerical prefix for one-thousandths; that is multiplying a unit of measure by 0.001 (or ten to the power of negative 3).

Interestingly our Arduino systems will count the number of milliseconds (thousands of a second) from the start of a sketch running until the count reaches the maximum number capable of being stored in the variable type unsigned long (a 32-bit [four byte] integer – that ranges from zero to (2^32)-1. (2^32)-1, or 4294967295 milliseconds converts to 49.71027-odd days.

The counter resets when the Arduino is reset, it reaches the maximum value or a new sketch is uploaded. To get the value of the counter at a particular juncture, just call the function – for example:

start=millis();

Where start is an unsigned long variable. Here is a very simple example to show you millis() in action:

/*
millis() demonstration */

unsigned long start, finished, elapsed;

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

void loop() { Serial.println("Start..."); start=millis(); delay(1000); finished=millis(); Serial.println("Finished"); elapsed=finished-start; Serial.print(elapsed); Serial.println(" milliseconds elapsed"); Serial.println(); delay(500); }

The sketch stores the current millis count in start, then waits one second, then stores the value of millis again in finished. Finally it calculates the elapsed time of the delay. In the following screen dump of the serial monitor, you can see that the duration was not always exactly 1000 milliseconds, as show in the image.

Step 1:

To put it simply, the millis function makes use of an internal counter within the ATmega microcontroller at the heart of your Arduino. This counter increments every clock cycle – which happens (in standard Arduino and compatibles) at a clock speed of 16 Mhz. This speed is controlled by the crystal on the Arduino board (the silver thing with T16.000 stamped on it).

Step 2:

Crystal accuracy can vary depending on external temperature, and the tolerance of the crystal itself. This in turn will affect the accuracy of your millis result. Anecdotal experience has reported the drift in timing accuracy can be around three or four seconds per twenty-four hour period.

If you are using a board or your own version that is using a ceramic resonator instead of a crystal, note that they are not as accurate and will introduce the possibility of higher drift levels. If you need a much higher level of timing accuracy, consider specific timer ICs such as the Maxim DS3231.

Now we can make use of the millis for various timing functions. As demonstrated in the previous example sketch, we can calculate elapsed time. To take this idea forward, let’s make a simple stopwatch. Doing so can be as simple or as complex as necessary, but for this case we will veer towards simple.

On the hardware perspective, we will have two buttons – Start and Stop – with the 10k ohm pull-down resistors connected to digital pins 2 and 3 respectively. When the user presses start the sketch will note the value for millis – then after stop is pressed, the sketch will again note the value for millis, calculate and display the elapsed time. The user can then press start to repeat the process, or stop for updated data. Here is the sketch:

/*
Super-basic stopwatch using millis(); */

unsigned long start, finished, elapsed;

void setup() { Serial.begin(9600); pinMode(2, INPUT); // start button pinMode(3, INPUT); // stop button Serial.println("Press 1 for Start/reset, 2 for elapsed time"); }

void displayResult() { float h,m,s,ms; unsigned long over; elapsed=finished-start; h=int(elapsed/3600000); over=elapsed%3600000; m=int(over/60000); over=over%60000; s=int(over/1000); ms=over%1000; Serial.print("Raw elapsed time: "); Serial.println(elapsed); Serial.print("Elapsed time: "); Serial.print(h,0); Serial.print("h "); Serial.print(m,0); Serial.print("m "); Serial.print(s,0); Serial.print("s "); Serial.print(ms,0); Serial.println("ms"); Serial.println(); }

void loop() { if (digitalRead(2)==HIGH) { start=millis(); delay(200); // for debounce Serial.println("Started..."); } if (digitalRead(3)==HIGH) { finished=millis(); delay(200); // for debounce displayResult(); } }

The calls to delay() are used to debounce the switches – these are optional and their use will depend on your hardware. The image is an example of the sketch’s serial monitor output – the stopwatch has started, and then button two pressed six times across periods of time.

Step 3: Speedometer...

If you had a sensor at the start and end of a fixed distance, speed could be calculated: speed = distance ÷ time.

You can also make a speedometer for a wheeled form of motion, for example a bicycle. At the present time we do not have a bicycle to mess about with, however we can describe the process to do so – it is quite simple. (Disclaimer – do so at your own risk etc.)

First of all, let’s review the necessary maths. You will need to know the circumference of the wheel. Hardware – you will need a sensor. For example – a reed switch and magnet. Consider the reed switch to be a normally-open button, and connect as usual with a 10k ohm pull-down resistor.

Others may use a hall-effect sensor – each to their own). Remember from maths class, to calculate the circumference – use the formula: circumference = 2πr where r is the radius of the circle.

Now that you have the wheel circumference, this value can be considered as our ‘fixed distance’, and therefore the speed can be calculated by measuring the elapsed time between of a full rotation.

Your sensor – once fitted – should act in the same method as a normally-open button that is pushed every rotation. Our sketch will measure the time elapsed between every pulse from the sensor.

To do this, our example will have the sensor output connected to digital pin 2 – as it will trigger an interrupt to calculate the speed. The sketch will otherwise be displaying the speed on a normal I2C-interface LCD module. The I2C interface is suggested as this requires only 4 wires from the Arduino board to the LCD – the less wires the better.

Here is the sketch for your perusal:

/*
Basic speedometer using millis(); */

#include "Wire.h" // for I2C bus LCD #include "LiquidCrystal_I2C.h" // for I2C bus LCD module - http://bit.ly/m7K5wt LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display

float start, finished; float elapsed, time; float circMetric=1.2; // wheel circumference relative to sensor position (in meters) float circImperial; // using 1 kilometer = 0.621371192 miles float speedk, speedm; // holds calculated speed vales in metric and imperial

void setup() { attachInterrupt(0, speedCalc, RISING); // interrupt called when sensors sends digital 2 high (every wheel rotation) start=millis(); // setup LCD lcd.init(); // initialize the lcd lcd.backlight(); // turn on LCD backlight lcd.clear(); lcd.println(" Wear a helmet! "); delay(3000); lcd.clear(); Serial.begin(115200); circImperial=circMetric*.62137; // convert metric to imperial for MPH calculations }

void speedCalc() { elapsed=millis()-start; start=millis(); speedk=(3600*circMetric)/elapsed; // km/h speedm=(3600*circImperial)/elapsed; // Miles per hour }

void loop() { lcd.setCursor(0,0); lcd.print(int(speedk)); lcd.print(" km/h "); lcd.print(int(speedm)); lcd.print(" MPH "); lcd.setCursor(0,1); lcd.print(int(elapsed)); lcd.print(" ms/rev "); delay(1000); // adjust for personal preference to minimise flicker }

There isn’t that much going on – every time the wheel completes one revolution the signal from the sensor will go from low to high – triggering an interrupt which calls the function speedCalc().


This takes a reading of millis() and then calculates the difference between the current reading and the previous reading – this value becomes the time to cover the distance (which is the circumference of the wheel relative to the sensor – stored in

float circMetric=1.2;

and is measured in metres). It finally calculates the speed in km/h and MPH. Between interrupts the sketch displays the updated speed data on the LCD as well as the raw time value for each revolution for curiosity’s sake. In real life I don’t think anyone would mount an LCD on a bicycle, perhaps an LED display would be more relevant.

In the meanwhile, you can see how this example works in the following short video clip. Instead of a bike wheel and reed switch/magnet combination, I have connected the square-wave output from a function generator to the interrupt pin to simulate the pulses from the sensor, so you can get an idea of how it works.

Step 4:

That just about sums up the use of millis() for the time being. There is also the micros(); function which counts microseconds.

So there you have it – another practical function that can allow more problems to be solved via the world of Arduino. As always, now it is up to you and your imagination to find something to control or get up to other shenanigans.

This post brought to you by pmdway.com - everything for makers and electronics enthusiasts, with free delivery worldwide.