Home | pfodApps/pfodDevices | WebStringTemplates | Java/J2EE | Unix | Torches | Superannuation | | About Us
 

Forward Logo (image)      

How to write Timings and Delays
in Arduino

by Matthew Ford 8th Nov 2013 (original - 21st March 2013)
© Forward Computing and Control Pty. Ltd. NSW Australia
All rights reserved.

How to write Timings and Delays in Arduino
Why your program might fail after 50 days.

This page has been re-written to use ElapsedMillis repeating timers
This page also shows how to use elapsedMillis for a one-off timers.

Introduction

Using delay() causes your system to be stuck while waiting for the delay to expire. However replacing delays requires some care. This page explains in a step by step way how to replace delays with repeating timers in a reliable way.

Warning: Do not run the standard Arduino BLINK example code, or any other code using pin 13 (LED) as an output, on the Fio V3 board when a battery is connected. You may damage the board.
See this page for more warnings about the FioV3 board

Timers Using elapsedMillis
A Repeating Timer using elapsedMillis
A Once Off Timer using elapsedMillis

Code Examples to be Avoided
Simple Blink Example using Delay
Second Attempt to Blink without Delay
Almost Final solution to Blink without Delay
Word of Warning – Add a loop monitor
Final Solution for Reliable Timers (Superseded by above elapsedMillis examples, 8
th Nov 2013)

Timers Using elapsedMillis

Lets start at the end with the two working examples that don't have problems, based on elapsedMillis by Paul Stoffregen, one for a repeating timer and one for a once off timer.

First install the elapsedMillis library. Downloaded the elapsedMillis.zip file.
Unzip this file to your
Arduino/libraries directory (open the IDE File->preferences window to see where your local Arduino directory is).
Some times the instructions for How to Install a Library - Automatic installation work, but not always. Unzipping the file manually is safest.

A Repeating Timer using elapsedMillis

This is a simple example of a repeating timer

#include <elapsedMillis.h>
// see warning above about FioV3
int led = 13;  
// Pin 13 has an LED connected on most Arduino boards.
// if using Arduino IDE 1.5 or above you can use pre-defined
// LED_BUILTIN  instead of 'led'
// 
elapsedMillis timer0;
#define interval 1000
// the interval in mS 

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  timer0 = 0; // clear the timer at the end of startup
}

void loop() {
  if (timer0 > interval) {
    timer0 -= interval; //reset the timer
    int ledPin = digitalRead(led);
    // read the current state and write the opposite
    digitalWrite(led, !ledPin);
  }
}

The code above loop() continues to run without being stuck waiting for the delay to expire.
During each pass of the loop(), a timeout interval is compared to a free running timer.
When the timer exceeds the value of the interval the desired action is taken (in this example change the state of the LED) and the timer is reset.

The reason for using
timer0 -= interval; //reset the timer
is that it allows for the possibility that the timer just happened to be incremented between testing it
if (timer0 > interval) {
and resetting it
timer0 -= interval; //reset the timer
or if there is some other delay the prevents the main loop from running every milli-second. (See the Adding a Loop Montor below)

Using timer0 -= interval; also gives you the option of varying the timing interval.

Another point is to clear timer0 at the end of startup(), using
timer0 = 0;
This ensures the timer is accurate at the start of the loop(), even if startup() takes some time to execute.

A Once Off Timer using elapsedMillis

If you only want the timer to fire once and never again you need to add a guard boolean to prevent code being executed again after the timer has fired

#include <elapsedMillis.h>
// see warning above about FioV3
int led = 13;
// Pin 13 has an LED connected on most Arduino boards.
// if using Arduino IDE 1.5 or above you can use pre-defined
// LED_BUILTIN  instead of 'led'
// 
elapsedMillis timer0;

#define interval 5000
// the interval in mS 
boolean timer0Fired;

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  digitalWrite(led, HIGH);
  timer0Fired = false;
  timer0 = 0; // clear the timer at the end of startup
}

void loop() {
  if ((!timer0Fired) && (timer0 > interval)) {
    timer0Fired = true; // don't execute this again
    digitalWrite(led, LOW); // turn led off after 5 sec
  }
}

Also see http://playground.arduino.cc//Code/ElapsedMillis

Code Examples to be Avoided

Simple Blink Example using Delay

Many of the sample Arduino programs are seductively simple. Take the Blink example

// Initial code attempt
int led = 13; // don't use on FioV3 when battery connected
// Pin 13 has an LED connected on most Arduino boards.
// if using Arduino IDE 1.5 or above you can use pre-defined
// LED_BUILTIN  instead of 'led'
// 
// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
  delay(1000); // wait for a second
  digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
  delay(1000); // wait for a second
}

The problem with this example is that the CPU is stuck waiting in the delay function.
delay(1000);

If you try and add more code to the loop() you will find it is very slow to execute. So the first point is:-
Don't use delay( )

Second Attempt to Blink, without Delay

But if you don't use delay what are the alternatives. Well Arduino provides a millis() method that returns the number of milli Seconds since the last uC reset. Using this method we can code this second attempt at a delay.

// Second attempt
int led = 13; // don't use on FioV3 when battery connected
// Pin 13 has an LED connected on most Arduino boards.
// if using Arduino IDE 1.5 or above you can use pre-defined
// LED_BUILTIN  instead of 'led'
// 
unsigned long timeout;
// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  timeout = millis();
}

// the loop routine runs over and over again forever:
void loop() {
  if (millis() > timeout) { // WARNING – this has problems as discussed below
    // time to toggle the Led
    timeout += 1000;
    if (digitalRead(led)) {
      digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
    } else {
      digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
    }
  }
}

Now there are no delays in the main loop() and your other code will be run promptly.

However all it not good with this solution.

The second attempt above has a failing. About 50 days after the Arduino board is reset, millis() goes back to counting from 0. This is because the 32bits used to store the value overflow and start counting from zero again. See the Arduino reference description for millis(). The timeout variable is also 32bits and so eventually timeout + 1000 will overflow and become a small number. What this means is that for about 1 second every 50 days
if (millis() > timeout) {
will be true
every loop and the led will flash at a very high rate for that second until millis() over flows back to a small number as well.

For a blinking led this may not be a problem but in a real application it could cause damage every 50 days when the timers stop working as expected.

If you are just interested in a repeating timer then see http://arduino.cc/en/Tutorial/BlinkWithoutDelay for a simple reliable repeating timer which does not count down. The code below works because even if currentMillis overflows back to a small number, currentMillis – previousMillis still gives the correct result.

void loop() {
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis> interval) {
    // save the last time 
    previousMillis = currentMillis;
    // do stuff here each interval (interval -- an unsigned long)
    ...
  }



Almost Final solution to Blink without Delay

The almost final solution involves a very small but significant change to the code.
Replace
if (millis() > timeout) { // WARNING – this has problems as discussed below
with
if (millis() == timeout) { // this works if loop() takes <1mS to execute

So the code becomes

// Final solution code
int led = 13; // don't use on FioV3 when battery connected
// Pin 13 has an LED connected on most Arduino boards.
// if using Arduino IDE 1.5 or above you can use pre-defined
// LED_BUILTIN  instead of 'led'
// 
unsigned long timeout;
// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  timeout = millis();
}
// the loop routine runs over and over again forever:
void loop() {
  if (millis() == timeout) { // NOTE: the == only toggle led when millis() equals timeout
    // time to toggle the Led
    timeout += 1000;
    if (digitalRead(led)) {
      digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
    } else {
      digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
    }
  }
}

This code works because when timeout + 1000 overflows, millis() will not equal it until millis() also overflows.

Word of Warning – Add a loop monitor

The almost final solution presented above does have one dependency. That is the main loop() must execute at lease once every 1mS, otherwise you may miss the exact milliSecond when millis() == timeout.

This means you must not have any other delay()'s in you program what would prevent the loop() from running at least once every 1mS. Unfortunately many of the standard Arduino libraries use delay() or introduce pauses, such as AnalogRead and SoftwareSerial. Usually the delay these introduce are small but they can add up so I suggest you add a monitor at the top of your loop() to check how quickly it runs.

The loop monitor is very similar to the blink example. A small piece of code at the top of the loop() method just toggles the Led each time loop() is executed. You can then use a digital multimeter with at Hz scale to measure the frequency of the output on the LED pin (pin 13 in this case)

The code is:-

// Loop Monitor – this checks that the loop() is executed at least once every 1mS
// (c)2013 Forward Computing and Control Pty. Ltd. 
// www.forward.com.au
//
// This example code is in the public domain. 
int led = 13; // don't use on FioV3 when battery connected
// Pin 13 has an LED connected on most Arduino boards.
// if using Arduino IDE 1.5 or above you can use pre-defined
// LED_BUILTIN  instead of 'led'
// 

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  // add your other setup code here
}

// the loop routine runs over and over again forever:
void loop() {
  // toggle the led output each loop The led frequency must measure >500Hz (i.e. <1mS off and <1mS on)
  if (digitalRead(led)) {
    digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
  } else {
    digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
  }

// add the rest of your loop code here
}



You can download the monitor code here. When I run this code on my Uno board the multiMeter on the Hz range connected between pin 13 and GND reads 57.6Khz. i.e. about 100 times >500hz. As you add your code to loop() the Hz reading will reduce. Just check it stays well above 500Hz in all situations.

Final Solution for Reliable Count Down Timers
(Note: this code has been superseded by the elapsedMillis code at the top of this page)

This last example shows how to code count down timers, both repeating and one off, that will reliably execute even if loop() takes more the 1mS to execute.

The idea is to capture the number of millis() that have gone by since the last loop() and use that number to decrement your timers. This code can be downloaded from here. As noted above see http://arduino.cc/en/Tutorial/BlinkWithoutDelay for an alternative simple reliable repeating timer which does not count down.

/*
Robust Timer
(c)Forward Computing and Control Pty. Ltd.
Works even if sometimes the loop() takes longer the 1mS to execute.
This example code is in the public domain.
*/

static unsigned long lastMillis = 0; // holds the last read millis()
static int timer_1 = 0; // a repeating timer max time 32768 mS = 32sec use a long if you need a longer timer
// NOTE timer MUST be a signed number int or long as the code relies on timer_1 being able to be negative
// NOTE timer_1 is a signed int
#define TIMER_INTERVAL_1 2000
// 2S interval

static int timer_2 = 0; // a one off timer max time 32768 mS = 32sec use a long if you need a longer timer
// NOTE timer MUST be a signed number int or long as the code relies on timer_2 being able to be negative
// NOTE timer_2 is a signed int
#define TIMER_INTERVAL_2 5000
// 5 sec interval

// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  timer_1 = TIMER_INTERVAL_1; // timeout in first loop 
  timer_2 = TIMER_INTERVAL_2; // set up one off timer times out in 10 sec
  Serial.begin(9600);
  while (Serial.available()) {
  }
  Serial.println("Timer Begin");
  lastMillis = millis(); // do this last in setup
}

// the loop routine runs over and over again forever:
void loop() {
  // set millisTick at the top of each loop if and only if millis() has changed
  unsigned long deltaMillis = 0; // clear last result
  unsigned long thisMillis = millis(); 
  // do this just once to prevent getting different answers from multiple calls to   millis()
  if (thisMillis != lastMillis) {
  // we have ticked over
  // calculate how many millis have gone past
  deltaMillis = thisMillis-lastMillis; // note this works even if millis() has rolled over back to 0
  lastMillis = thisMillis;
}

  // handle one off timer
  // repeat this code for each timer you need to handle
  if (timer_2 > 0) { // still counting down
    timer_2 -= deltaMillis;
    if (timer_2 <= 0) {
      // timer timed out
      // do timeout stuff here
      // in this set the led HIGH 
      Serial.println("Timer 2 timed out");
    }
  }

  // handle repeating timer
  // repeat this code for each timer you need to handle
  timer_1 -= deltaMillis;
  if (timer_1 <= 0) {
    // reset timer since this is a repeating timer
    timer_1 += TIMER_INTERVAL_1; // note this prevents the delay accumulating if we miss a mS or two 
    // if we want exactly 1000 delay to next time even if this one was late then just use timeOut = 1000;

    // do time out stuff here
    Serial.println("Repeating Timer 1 timed out");

  }
}

Note that for the one off timer, timer_2, we first check if the timer is >0 before subtracting the deltaMillis so that we only process the timeout once.

For the repeating timer, timer_1, we just subtract the deltaMillis every time, but most of the time deltaMillis will be 0.

So for both types of timers, you can also add a check for deltaMillis > 0 and skip all the timer code except for those times when the mS ticks over. i.e.

if (deltaMillis> 0) {
  // handle one off timer
  // repeat this code for each timer you need to handle
  .. etc

  // handle repeating timer
  // repeat this code for each timer you need to handle
  .. etc
}

For use of the Arduino name see http://arduino.cc/en/Main/FAQ


pfodDevice™ and pfodApp™ are trade marks of Forward Computing and Control Pty. Ltd.


Forward home page link (image)

Contact Forward Computing and Control by
©Copyright 1996-2012 Forward Computing and Control Pty. Ltd. ACN 003 669 994