Skip to content

Robotics

How to program a robot as a state machine

A state machine organises a robot's program into states, like SEARCH, APPROACH and GRAB, with only one active at a time. A variable holds the current state; on every pass of loop(), a switch runs that state's code, and each state checks its own conditions to decide when to move to another. Because nothing ever waits, every sensor is read on every pass.

States and arrows

Draw it before coding it. Each state is a circle: what the robot does while it's there. Each way out is an arrow, labelled with what triggers it. A fetch robot:

StateDoesLeaves whenGoes to
SEARCHspins on the spotrange under 600APPROACH
APPROACHdrives forwardrange under 150GRAB
APPROACHdrives forwardrange over 800SEARCH
GRABstops, closes the gripper500 ms passedCARRY
CARRYdrives homeeye sees the tapeDONE
DONElets go

Arrows can point backwards. If the target slips away during APPROACH, the range jumps over 800 and the robot goes back to searching. Leave that arrow out and it drives on at nothing.

In code

enum State { SEARCH, APPROACH, GRAB, CARRY, DONE };
State state = SEARCH;
unsigned long since = 0;

void loop() {
  int range = analogRead(A0);   // read everything first
  switch (state) {
    case SEARCH:
      spin();
      if (range < 600) state = APPROACH;
      break;
    case APPROACH:
      forward();
      if (range < 150) { state = GRAB; since = millis(); }
      else if (range > 800) state = SEARCH;
      break;
    case GRAB:
      stopAndGrip();
      if (millis() - since >= 500) state = CARRY;
      break;
    // CARRY and DONE follow the same pattern
  }
}

Live experiment

A fetch robot in five states

SEARCH spins until something is ahead, APPROACH drives up to it, GRAB holds on for 500 ms, CARRY drives home to the tape and DONE lets go. In the second scene the target slips away: only an APPROACH with an arrow back to SEARCH recovers.

Try it yourself: free lesson

“A brain in states” is lesson 6 of 6 in Waypoint Wharf, Robotics World 3. Free with an account. Lessons unlock in order, so you’ll do the 19 before it first.

Timers without waiting

GRAB needs half a second. delay(500) would make the robot blind for that half second: no range, no eye, nothing. Instead, note the time once, on the way in, and check it on each pass. Set since inside the GRAB case and it's always "now", so the state never ends.

Why one number

You could track the robot's behaviour with a handful of true or false flags, searching, approaching, gripping. But then two can be true at once, and the code has to untangle them. A single state variable can only ever hold one state, so the robot is always doing exactly one thing. Traffic lights, vending machines, washing machines and games are all built the same way, and so are robots that have to do more than one thing in order.

Free lesson

Now build it yourself

Each lesson comes in three short parts: see the idea, try it on a live board, then build it yourself, with quick checks as you go.

Try it yourself: free lesson

“A brain in states” is lesson 6 of 6 in Waypoint Wharf, Robotics World 3. Free with an account. Lessons unlock in order, so you’ll do the 19 before it first.

Or start from the very first lesson, no account needed

Practice questions

Three quick questions from the lessons. Pick an answer to check it.

  1. Question 1 of 3Why not write the grab as stop, delay(500), then drive home?
    Show the answer

    Answer: For half a second nothing else is read: not RANGE, not the eye, nothing. A state machine keeps loop() spinning. Every pass reads every sensor, even while a state waits.

    • Why not “delay(500) is too short”? The length isn't the problem; a delay of any length stops every sensor reading while it runs.
    • Why not “It's exactly the same”? They behave differently: with delay() the robot is blind for 500 ms, with a timer it keeps reading.
    • Why not “It wouldn't compile”? delay(500) compiles fine. The trouble is what it does: nothing else runs for half a second.
  2. Question 2 of 3A case forgets its break. After its own lines, the program…
    Show the answer

    Answer: Runs straight on into the next case's lines too. Without break, C runs on into the next case. SEARCH would spin, then drive straight on top of it in the same pass.

    • Why not “Stops”? Nothing stops the program at the end of a case; without break it simply keeps going.
    • Why not “Starts loop() again”? loop() only restarts when it reaches its end. A missing break runs on into the next case first.
    • Why not “Carries on correctly”? It doesn't carry on correctly: the next case's lines run in the same pass, as if that state were active too.
  3. Question 3 of 3Why keep the mode as a number instead of separate true/false flags?
    Show the answer

    Answer: One number can only be one mode at a time, so modes can't clash. With flags, two could be true at once. A single mode number is always exactly one mode.

    • Why not “No reason”? There's a good reason: with separate flags, two could be true at the same time and clash.
    • Why not “Numbers are faster”? Speed isn't the reason. A single number makes it impossible to be in two modes at once.
    • Why not “Flags use more pins”? Flags and numbers are both variables; neither uses pins.
Keep practising: free lesson

“A brain in states” is lesson 6 of 6 in Waypoint Wharf, Robotics World 3. Free with an account. Lessons unlock in order, so you’ll do the 19 before it first.

Questions people ask

What is a finite state machine in Arduino?

A way to organise a program as a set of states, only one active at a time. A variable holds the current state, a switch statement runs that state's code on each pass of loop(), and each state checks its own conditions to decide when to move to another.

Why use a state machine instead of delay()?

A state that waits checks a timer on each pass instead of stopping the program, so loop() keeps running and every sensor is read all the time. The robot can react to a bump or a lost target in the middle of any step.

How do I draw a state machine?

Draw a circle for each state and an arrow for each way out, labelled with the condition that takes it: a reading crossing a line, a timer running out, a button press. Then each circle becomes a case, and each arrow an if inside it.

Every topic