Beginner Arduino programs tend to focus on a single goal, such as blinking an LED or spinning a servo, where the timing is controlled using delay; the famous blink sketch is a prime example:

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}

This works well for single tasks like blinking an LED; however, delay prevents multiple tasks from running concurrently. We can easily add another LED to the blink sketch and have it blink in time with the existing LED...

void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  digitalWrite(12, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  digitalWrite(12, LOW);
  delay(1000);
}

...but if we want the LED's to blink at different rates, where for example the first LED stays on for three seconds and the second stays on for two seconds, we'll find that delay pauses the program, preventing other tasks from running.

Multitasking with millis

millis returns the number of milliseconds since the Arduino board began running the current program. When the LED is switched on, the current time returned by millis should be saved. As the program runs, the difference between the time that the LED was switch on and the current time is compared to a predefined interval; once the interval has passed, the LED can be switched off. Below is the official BlinkWithDelay sketch which demonstrates this.

const int ledPin =  13;
int ledState = LOW;

unsigned long previousMillis = 0;
const long interval = 1000;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    digitalWrite(ledPin, ledState);
  }
}

Whilst this works well for blinking a single LED, keeping track of the timing and states for multiple tasks is convoluted.

Using finite state machines

A finite state machine is a model of computation that encapsulates the states a machine can be in and how the machine transitions from one state to another; the machine can only be in one state at a time.

If we imagine each task in our program as a state machine, we can have the machine manage the transitions for us. In our program to blink two LED's, we'd have two state machines, each with two states: one for when the LED is on, and the other for when it's off.

I recently updated my arduino-fsm library to include support for timed transitions, so you don't need to manage the timing yourself. Below is the code for blinking two LED's at different rates using arduino-fsm and the new add_timed_transition method.

#include <Fsm.h>

#define LED1_PIN 10
#define LED2_PIN 11

void on_led1_on_enter() {
    digitalWrite(LED1_PIN, HIGH);
}

void on_led1_off_enter() {
    digitalWrite(LED1_PIN, LOW);
}

void on_led2_on_enter() {
    digitalWrite(LED2_PIN, HIGH);
}

void on_led2_off_enter() {
    digitalWrite(LED2_PIN, LOW);
}

State state_led1_on(&on_led1_on_enter, NULL);
State state_led1_off(&on_led1_off_enter, NULL);

State state_led2_on(&on_led2_on_enter, NULL);
State state_led2_off(&on_led2_off_enter, NULL);

Fsm fsm_led1(&state_led1_off);
Fsm fsm_led2(&state_led2_off);

void setup() {
    pinMode(LED1_PIN, OUTPUT);
    pinMode(LED2_PIN, OUTPUT);

    fsm_led1.add_timed_transition(&state_led1_off, &state_led1_on, 1000, NULL);
    fsm_led1.add_timed_transition(&state_led1_on, &state_led1_off, 3000, NULL);
    fsm_led2.add_timed_transition(&state_led2_off, &state_led2_on, 1000, NULL);
    fsm_led2.add_timed_transition(&state_led2_on, &state_led2_off, 2000, NULL);
}

void loop() {
    fsm_led1.check_timer();
    fsm_led2.check_timer();
}

In the example, there are two state machines: one for each LED. The following timed transitions are added:

  • A transition from LED1 off to LED1 on after 1 second has passed;
  • A transition from LED1 on to LED1 off after 3 seconds has passed;
  • A transition from LED2 off to LED2 on after 1 second has passed;
  • A transition from LED2 on to LED2 off after 2 seconds has passed;

Both state machines begin in the LED off state. The loop calls the check_timer function on each state machine, which checks all timed transitions. If the interval has been reached, the transition is performed.

double LED state machine animation

For more information about the arduino-fsm library check out the GithHub project page.