Bird
Raised Fist0
Arduinoprogramming~5 mins

Timing-based state machines in Arduino

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction

Timing-based state machines help control actions that change over time without stopping the whole program. They let your Arduino do many things smoothly by switching states after set times.

You want to blink an LED on and off at regular intervals without pausing the program.
You need to control a motor that runs for 5 seconds, then stops for 3 seconds, repeatedly.
You want to read a sensor every 2 seconds while still running other code.
You want to create a simple traffic light sequence with timed changes.
Syntax
Arduino
enum State {STATE1, STATE2, STATE3};
State currentState = STATE1;
unsigned long previousMillis = 0;
const unsigned long interval = 1000; // time in milliseconds

void loop() {
  unsigned long currentMillis = millis();
  switch(currentState) {
    case STATE1:
      // do something
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        currentState = STATE2;
      }
      break;
    case STATE2:
      // do something else
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        currentState = STATE3;
      }
      break;
    case STATE3:
      // do another thing
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        currentState = STATE1;
      }
      break;
  }
}

Use millis() to track time without stopping the program.

Use an enum to name your states clearly.

Examples
This example blinks the built-in LED on and off every 500 milliseconds using a timing-based state machine.
Arduino
enum State {LED_ON, LED_OFF};
State currentState = LED_OFF;
unsigned long previousMillis = 0;
const unsigned long interval = 500;

void loop() {
  unsigned long currentMillis = millis();
  switch(currentState) {
    case LED_OFF:
      digitalWrite(LED_BUILTIN, LOW);
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        currentState = LED_ON;
      }
      break;
    case LED_ON:
      digitalWrite(LED_BUILTIN, HIGH);
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        currentState = LED_OFF;
      }
      break;
  }
}
This example turns a motor on pin 9 on for 5 seconds, then off for 3 seconds repeatedly.
Arduino
enum State {MOTOR_ON, MOTOR_OFF};
State currentState = MOTOR_OFF;
unsigned long previousMillis = 0;
const unsigned long onTime = 5000;
const unsigned long offTime = 3000;

void loop() {
  unsigned long currentMillis = millis();
  switch(currentState) {
    case MOTOR_OFF:
      digitalWrite(9, LOW);
      if (currentMillis - previousMillis >= offTime) {
        previousMillis = currentMillis;
        currentState = MOTOR_ON;
      }
      break;
    case MOTOR_ON:
      digitalWrite(9, HIGH);
      if (currentMillis - previousMillis >= onTime) {
        previousMillis = currentMillis;
        currentState = MOTOR_OFF;
      }
      break;
  }
}
Sample Program

This program simulates a traffic light using three LEDs connected to pins 2, 3, and 4. It changes the light from RED to GREEN to YELLOW with timed intervals. The Serial monitor shows the current light state every second.

Arduino
enum TrafficLightState {RED, GREEN, YELLOW};
TrafficLightState currentState = RED;
unsigned long previousMillis = 0;

const unsigned long redTime = 5000;
const unsigned long greenTime = 4000;
const unsigned long yellowTime = 2000;

void setup() {
  pinMode(2, OUTPUT); // RED LED
  pinMode(3, OUTPUT); // GREEN LED
  pinMode(4, OUTPUT); // YELLOW LED
  Serial.begin(9600);
}

void loop() {
  unsigned long currentMillis = millis();
  switch(currentState) {
    case RED:
      digitalWrite(2, HIGH);
      digitalWrite(3, LOW);
      digitalWrite(4, LOW);
      Serial.println("RED light");
      if (currentMillis - previousMillis >= redTime) {
        previousMillis = currentMillis;
        currentState = GREEN;
      }
      break;
    case GREEN:
      digitalWrite(2, LOW);
      digitalWrite(3, HIGH);
      digitalWrite(4, LOW);
      Serial.println("GREEN light");
      if (currentMillis - previousMillis >= greenTime) {
        previousMillis = currentMillis;
        currentState = YELLOW;
      }
      break;
    case YELLOW:
      digitalWrite(2, LOW);
      digitalWrite(3, LOW);
      digitalWrite(4, HIGH);
      Serial.println("YELLOW light");
      if (currentMillis - previousMillis >= yellowTime) {
        previousMillis = currentMillis;
        currentState = RED;
      }
      break;
  }
  delay(1000); // slow down serial output for readability
}
OutputSuccess
Important Notes

Do not use delay() for timing in state machines because it stops the whole program.

Always update the previousMillis when switching states to keep timing accurate.

Summary

Timing-based state machines let your Arduino do tasks that change over time without stopping other work.

Use millis() to check elapsed time and switch states when needed.

Organize your states with enum and use switch to handle each state clearly.

Practice

(1/5)
1.

What is the main purpose of using millis() in a timing-based state machine on Arduino?

easy
A. To pause the program for a fixed time
B. To reset the Arduino board
C. To track elapsed time without stopping the program
D. To read analog sensor values

Solution

  1. Step 1: Understand what millis() does

    millis() returns the number of milliseconds since the Arduino started running. It keeps counting without stopping the program.
  2. Step 2: Connect millis() to timing-based state machines

    Using millis() lets the program check how much time passed and change states without pausing or blocking other tasks.
  3. Final Answer:

    To track elapsed time without stopping the program -> Option C
  4. Quick Check:

    millis() tracks time without delay [OK]
Hint: Remember: millis() never stops your code [OK]
Common Mistakes:
  • Thinking millis() pauses the program
  • Confusing millis() with delay()
  • Using millis() to reset Arduino
2.

Which of the following is the correct way to check if 1000 milliseconds have passed using millis()?

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

void loop() {
  unsigned long currentMillis = millis();
  // What condition checks if interval passed?
  if (__________) {
    // do something
    previousMillis = currentMillis;
  }
}
easy
A. previousMillis + currentMillis <= interval
B. previousMillis - currentMillis >= interval
C. currentMillis + previousMillis >= interval
D. currentMillis - previousMillis >= interval

Solution

  1. Step 1: Understand elapsed time calculation

    Elapsed time is current time minus previous time: currentMillis - previousMillis.
  2. Step 2: Check if elapsed time reached interval

    We compare if elapsed time is greater or equal to the interval: currentMillis - previousMillis >= interval.
  3. Final Answer:

    currentMillis - previousMillis >= interval -> Option D
  4. Quick Check:

    Elapsed time = current - previous [OK]
Hint: Subtract previous from current time to get elapsed [OK]
Common Mistakes:
  • Reversing subtraction order
  • Adding times instead of subtracting
  • Using <= instead of >=
3.

What will be the output of this Arduino code snippet that uses a timing-based state machine?

unsigned long previousMillis = 0;
const long interval = 2000;
int ledState = LOW;

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    digitalWrite(13, ledState);
    Serial.println(ledState);
  }
}
medium
A. Prints alternating 0 and 1 every 2 seconds
B. Prints 1 continuously every 2 seconds
C. Prints 0 continuously every 2 seconds
D. No output because of syntax error

Solution

  1. Step 1: Understand the timing and state toggle

    Every 2000 ms, the code toggles ledState between LOW (0) and HIGH (1).
  2. Step 2: Check output printed

    Each toggle prints the current ledState (0 or 1) to Serial, alternating every 2 seconds.
  3. Final Answer:

    Prints alternating 0 and 1 every 2 seconds -> Option A
  4. Quick Check:

    State toggles and prints 0,1 alternately [OK]
Hint: Toggle state and print inside timed if-block [OK]
Common Mistakes:
  • Assuming constant output without toggle
  • Confusing HIGH/LOW with 1/0
  • Missing update of previousMillis
4.

Identify the bug in this timing-based state machine code and choose the fix.

unsigned long previousMillis = 0;
const long interval = 1000;
int ledState = LOW;

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

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    ledState = !ledState;
    digitalWrite(13, ledState);
  }
}
medium
A. Add previousMillis = currentMillis; inside the if-block
B. Change int ledState to bool ledState
C. Replace ! with ~ in toggle
D. Remove the if condition to toggle every loop

Solution

  1. Step 1: Check timing update logic

    The code never updates previousMillis, so the condition stays true forever after first pass.
  2. Step 2: Fix by updating previousMillis

    Adding previousMillis = currentMillis; inside the if-block resets the timer for the next interval.
  3. Final Answer:

    Add previousMillis = currentMillis; inside the if-block -> Option A
  4. Quick Check:

    Update previousMillis to reset timer [OK]
Hint: Always update previousMillis after interval check [OK]
Common Mistakes:
  • Forgetting to update previousMillis
  • Using bitwise NOT (~) instead of logical NOT (!)
  • Removing timing check causes fast toggling
5.

You want to create a state machine that cycles through three LED states: OFF, RED, GREEN. Each state lasts 3 seconds. Which code snippet correctly implements this using millis()?

unsigned long previousMillis = 0;
const long interval = 3000;
int state = 0;

void setup() {
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    state = (state + 1) % 3;
    switch(state) {
      case 0:
        digitalWrite(RED_PIN, LOW);
        digitalWrite(GREEN_PIN, LOW);
        break;
      case 1:
        digitalWrite(RED_PIN, HIGH);
        digitalWrite(GREEN_PIN, LOW);
        break;
      case 2:
        digitalWrite(RED_PIN, LOW);
        digitalWrite(GREEN_PIN, HIGH);
        break;
    }
  }
}
hard
A. Does not change states due to missing update
B. Correctly cycles OFF, RED, GREEN every 3 seconds
C. Cycles states every 1 second instead of 3
D. Cycles only RED and GREEN, skipping OFF

Solution

  1. Step 1: Check timing and state update

    The code uses millis() to check 3 seconds passed, then updates state cycling 0,1,2 with modulo 3.
  2. Step 2: Verify LED outputs per state

    State 0 turns both LEDs off, 1 turns RED on, 2 turns GREEN on. This matches the required cycle.
  3. Final Answer:

    Correctly cycles OFF, RED, GREEN every 3 seconds -> Option B
  4. Quick Check:

    State cycles with modulo and timing [OK]
Hint: Use modulo (%) to cycle states smoothly [OK]
Common Mistakes:
  • Forgetting to update previousMillis
  • Incorrect modulo causing wrong cycles
  • Not turning off LEDs in OFF state