State machines
What you’ll learn
- What a state machine is
- How to draw one and turn it into code
- Why it beats a pile of nested
ifs
States and transitions
A robot is always in one state: SEARCH, ATTACK, ESCAPE. Each state has its own simple behaviour. Transitions are the rules for moving between states: “if I see the opponent, go from SEARCH to ATTACK”.
sees opponent SEARCH ─────────────► ATTACK ▲ ▲ │ │ │ edge clear │ sees edge │ └──── ESCAPE ◄─────┘ │ ▲ └──────────┘ (from any state: sees edge → ESCAPE)In code
let state = 'SEARCH';
function loop() { const edge = !lineLeft() || !lineRight(); if (edge) state = 'ESCAPE'; // top priority transition
if (state === 'SEARCH') { motors(30, -30); if (distance() < 180) state = 'ATTACK'; } else if (state === 'ATTACK') { motors(80, 80); if (distance() >= 180) state = 'SEARCH'; } else if (state === 'ESCAPE') { motors(-60, -20); if (!edge) state = 'SEARCH'; }}Why it helps
- Each state is short and easy to understand.
- You can print the state to see exactly what the robot thinks it’s doing.
- Adding a behaviour means adding a state, not untangling
ifs.
Example: a traffic light
| State | Lights | Next state after |
|---|---|---|
| RED | red | 5 s → GREEN |
| GREEN | green | 4 s → YELLOW |
| YELLOW | yellow | 1 s → RED |
enum State { RED, GREEN, YELLOW };State state = RED;unsigned long since = 0;
void loop() { unsigned long t = millis() - since; if (state == RED && t > 5000) { state = GREEN; since = millis(); } if (state == GREEN && t > 4000) { state = YELLOW; since = millis(); } if (state == YELLOW && t > 1000) { state = RED; since = millis(); } // set the LEDs for the current state here}Example: a delivery robot
FOLLOW_LINE → (junction) → TURN → (line found) → FOLLOW_LINE → (end marker) → DROP → (done) → RETURN.
Try it in the sumo ring
The robot must stay on the black ring and find the grey opponent block.
Robot commands
motors(left, right): wheel power, −100 to 100lineLeft(),lineRight():trueover the black linedistance(): cm to the nearest thing ahead (max 200)distanceLeft(),distanceRight(): cm to the sideslinePosition(): where the line is, −1 (left) to 1 (right), ornullmillis(): milliseconds since Runprint(value): show a message belowsetup()runs once;loop()runs 50 times a second
Watch the statesEasy
Run it and read the printed states. In what order do they appear?
Timed escapeMedium
Make ESCAPE last exactly 0.6 s before going back to SEARCH.
Remember millis() when you enter ESCAPE.
Add let since = 0;, set since = millis(); when entering ESCAPE, and leave with if (millis() - since > 600) state = 'SEARCH';.
Add a stateHard
Add a WANDER state: if SEARCH has spun for 3 s without finding anything, drive forward for 1 s, then go back to SEARCH.
Quick check
Finished this lesson?