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.
Timing-based state machines in Arduino
Start learning this pattern below
Jump into concepts and practice - no test required
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.
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;
}
}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;
}
}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.
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
}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.
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
What is the main purpose of using millis() in a timing-based state machine on Arduino?
Solution
Step 1: Understand what
millis()doesmillis()returns the number of milliseconds since the Arduino started running. It keeps counting without stopping the program.Step 2: Connect
Usingmillis()to timing-based state machinesmillis()lets the program check how much time passed and change states without pausing or blocking other tasks.Final Answer:
To track elapsed time without stopping the program -> Option CQuick Check:
millis()tracks time without delay [OK]
millis() never stops your code [OK]- Thinking
millis()pauses the program - Confusing
millis()with delay() - Using
millis()to reset Arduino
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;
}
}Solution
Step 1: Understand elapsed time calculation
Elapsed time is current time minus previous time:currentMillis - previousMillis.Step 2: Check if elapsed time reached interval
We compare if elapsed time is greater or equal to the interval:currentMillis - previousMillis >= interval.Final Answer:
currentMillis - previousMillis >= interval -> Option DQuick Check:
Elapsed time = current - previous [OK]
- Reversing subtraction order
- Adding times instead of subtracting
- Using <= instead of >=
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);
}
}Solution
Step 1: Understand the timing and state toggle
Every 2000 ms, the code togglesledStatebetween LOW (0) and HIGH (1).Step 2: Check output printed
Each toggle prints the currentledState(0 or 1) to Serial, alternating every 2 seconds.Final Answer:
Prints alternating 0 and 1 every 2 seconds -> Option AQuick Check:
State toggles and prints 0,1 alternately [OK]
- Assuming constant output without toggle
- Confusing HIGH/LOW with 1/0
- Missing update of previousMillis
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);
}
}Solution
Step 1: Check timing update logic
The code never updatespreviousMillis, so the condition stays true forever after first pass.Step 2: Fix by updating
AddingpreviousMillispreviousMillis = currentMillis;inside the if-block resets the timer for the next interval.Final Answer:
AddpreviousMillis = currentMillis;inside the if-block -> Option AQuick Check:
Update previousMillis to reset timer [OK]
- Forgetting to update previousMillis
- Using bitwise NOT (~) instead of logical NOT (!)
- Removing timing check causes fast toggling
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;
}
}
}Solution
Step 1: Check timing and state update
The code usesmillis()to check 3 seconds passed, then updatesstatecycling 0,1,2 with modulo 3.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.Final Answer:
Correctly cycles OFF, RED, GREEN every 3 seconds -> Option BQuick Check:
State cycles with modulo and timing [OK]
- Forgetting to update previousMillis
- Incorrect modulo causing wrong cycles
- Not turning off LEDs in OFF state
