What if your program could keep running smoothly while still keeping perfect track of time?
Why Timing-based state machines in Arduino? - Purpose & Use Cases
Start learning this pattern below
Jump into concepts and practice - no test required
Imagine you want to control a traffic light with red, yellow, and green lights turning on and off at specific times. You try to write code that waits for each light to finish before moving to the next, using delay commands everywhere.
Using delays stops your whole program from doing anything else while waiting. This means your traffic light can't respond to button presses or sensors during those pauses. It's slow, clunky, and hard to add new features.
Timing-based state machines let you track time without stopping the program. You check how much time has passed and switch states only when needed. This keeps your program running smoothly and lets you handle many tasks at once.
delay(1000); turnOnRed(); delay(1000); turnOnGreen();
if (millis() - lastChange >= interval) {
changeState();
lastChange = millis();
}You can build responsive, multitasking programs that control devices precisely over time without freezing or missing important events.
Controlling a robot's movements where motors run for set times but sensors must be checked constantly to avoid obstacles.
Manual delays pause everything and block other actions.
Timing-based state machines use time checks to switch states smoothly.
This approach keeps programs responsive and easy to expand.
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
