A state machine is the upgrade from nested if-else spaghetti to clean, readable robot logic. Every mechanism has a set of states it can be in. Define the states, define what triggers transitions between them, and the code almost writes itself.
1
The Concept
2
Basic Example
3
Subsystem FSM
4
Auton FSM
// Section 01
What a State Machine Is
A finite state machine (FSM) is a model of a system that can be in exactly one state at a time. Events trigger transitions between states. Each state defines what the system does while it is in that state.
💡
You already think in state machines. “The intake is either IDLE, INTAKING, or EJECTING.” That is a 3-state FSM described in plain English. Writing it in code is just making that mental model explicit — giving each state a name, defining what the robot does in each state, and specifying what causes transitions.
States vs. Nested If-Else
Without a state machine, mechanism logic grows into nested conditionals that are hard to read and impossible to debug:
// Nested if-else — hard to read, easy to breakif (master.get_digital(DIGITAL_R1)) {
if (!jamDetected) {
if (hasElement) {
if (armIsUp) { intake.move(-127); }
else { intake.move(127); }
} else { intake.move(127); }
} else { intake.move(-80); } // unjam
} else { intake.move(0); }
With a state machine, the same logic becomes:
// State machine — each state is explicit and readableswitch (intakeState) {
caseIDLE: intake.move(0); break;
caseINTAKING: intake.move(127); break;
caseEJECTING: intake.move(-127); break;
caseUNJAMMING: intake.move(-80); break;
}
The transitions (what causes state changes) are handled separately. Each piece of logic has exactly one home.
1 / 4
// Section 02
A Basic Toggle FSM
The simplest real-world FSM: a claw that toggles between OPEN and CLOSED on button press. Two states, one transition event.
OPEN
⇄
Button press (rising edge)
⇄
CLOSED
src/claw.cpp — two-state FSM
#include "main.h"#include "claw.hpp"// Define the states using an enum — readable names, not magic numbersenum classClawState { OPEN, CLOSED };
staticClawState clawState = ClawState::OPEN;
// Called once per opcontrol() loopvoidclawUpdate() {
// ── TRANSITION LOGIC ────────────────────────────────────────────────if (master.get_digital_new_press(DIGITAL_A)) {
// Toggle between states on rising edge only
clawState = (clawState == ClawState::OPEN)
? ClawState::CLOSED
: ClawState::OPEN;
}
// ── STATE ACTIONS ───────────────────────────────────────────────────switch (clawState) {
caseClawState::OPEN:
claw.move_absolute(0, 80); // open positionbreak;
caseClawState::CLOSED:
claw.move_absolute(500, 80); // closed positionbreak;
}
}
✅
Separate transitions from actions. The top half of clawUpdate() handles when to change state. The bottom half handles what to do in each state. Keep these two sections structurally separate in your code — it makes both easier to modify independently.
2 / 4
// Section 03
A Full Intake FSM with Four States
A real intake needs more than two states — it needs to handle idle, intaking, ejecting, and jammed conditions cleanly.
Adding a new behavior is easy. Want a LOADED state that stops the intake when a sensor detects a game element? Add LOADED to the enum, add one transition (stall or optical → LOADED), and add one action (case LOADED: intake.move(0);). No other code changes needed. That is the power of FSM structure.
3 / 4
// Section 04
Using FSM in Autonomous
State machines shine in autonomous — they make complex multi-step sequences readable, and each step can respond to sensor feedback rather than blindly running on a timer.
Autonomous as a State Sequence
Instead of a linear list of drive commands, think of your autonomous as a sequence of states: DRIVE_TO_GOAL, SCORE, RETURN, AWP_TOUCH. Each state runs until a condition exits it.
src/autons.cpp — state-based autonomous
enum classAutonState {
DRIVE_TO_GOAL,
SCORE,
RETURN_HOME,
AWP_TOUCH,
DONE
};
voidautonScoreAWP() {
AutonState state = AutonState::DRIVE_TO_GOAL;
bool initialized = false;
while (state != AutonState::DONE) {
switch (state) {
caseAutonState::DRIVE_TO_GOAL:
if (!initialized) {
chassis.pid_drive_set(30, 110);
initialized = true;
}
if (!chassis.drive_pid_task_running()) {
state = AutonState::SCORE;
initialized = false;
}
break;
caseAutonState::SCORE:
if (!initialized) {
intake.move(-127); // eject into goal
initialized = true;
}
// Sensor confirms score OR timer expiresif (!intakeHasElement()) {
intake.move(0);
state = AutonState::RETURN_HOME;
initialized = false;
}
break;
caseAutonState::RETURN_HOME:
if (!initialized) {
chassis.pid_drive_set(-20, 100);
initialized = true;
}
if (!chassis.drive_pid_task_running()) {
state = AutonState::AWP_TOUCH;
initialized = false;
}
break;
caseAutonState::AWP_TOUCH:
// AWP confirmed — done
state = AutonState::DONE;
break;
default: state = AutonState::DONE; break;
}
pros::delay(10);
}
}
⚠️
For most VRC autonomous routines, EZ Template’s linear API (pid_drive_set + pid_wait) is simpler and adequate. FSM-based autonomous is most valuable when states need to respond to sensor feedback (did the game element actually score? is the robot stuck?) rather than pure dead-reckoning. Use FSM when you need the extra intelligence, not by default.
When to Use FSM
Operator control mechanisms with multiple modes — any mechanism with 3+ states benefits from FSM over nested if-else
Sensor-responsive autonomous sequences — when you need to branch based on what actually happened, not just what was commanded
End-game sequences — complex multi-step actions with timing dependencies are much cleaner as states
Not worth it for: simple toggle (2 states with one button) — a flag variable and get_digital_new_press is simpler
⚙ STEM HighlightComputer Science: Automata Theory & Formal State Models
Finite State Machines (FSMs) are a fundamental concept in theoretical computer science — originating with Alan Turing and formally described by Mealy and Moore in the 1950s. Every digital system that has modes is an FSM: traffic lights, vending machines, video games. In VRC, your robot’s intake (IDLE → COLLECTING → FULL → EJECTING) is a Mealy machine: transitions are triggered by inputs (sensor readings, button presses) and outputs (motor commands) are associated with transitions. The formal definition: FSM = (States, Inputs, Outputs, Transition function, Initial state).
🎤 Interview line: “Our robot’s intake logic is implemented as a Finite State Machine — a formal model from automata theory. We defined five states, the transitions between them, and the outputs in each state. This eliminates nested if-else logic and makes the behavior verifiable — we can prove the intake never enters an invalid state.”
🔬 Check for Understanding
Your FSM has an INTAKE_ON state. The intake button is pressed while already in INTAKE_ON. What should happen?
The FSM should crash because you cannot enter a state you are already in
The FSM should transition to INTAKE_OFF because pressing a toggle button while on should turn it off
It depends on how the transition function is defined — the FSM designer specifies the behavior for every state+input combination