If you’ve been building Arduino projects, you’ve hit a wall: you want to blink an LED and read a button at the same time. But when you use the delay() function, the Arduino is frozen. It ignores everything. It is practically dead.
The solution to this single-minded stubbornness is The Finite State Machine (FSM). It sounds scary, but it’s just a way to force your microcontroller to finally multitask.
A Finite State Machine is a way of organizing your code based on “States”. At any given moment, your system is in exactly one state. It only changes to a different state when a specific event or condition happens (a “Transition”).
Think of a microwave oven:
The microwave can’t be COOKING and IDLE at the same time. It moves between these states based on triggers (like pressing “Start” or opening the door).
To build a state machine in C++ (the language Arduino uses), we use two main tools: enum and switch...case.
enumAn enum (enumeration) is just a way to give names to numbers. It makes our code incredibly easy to read.
enum SystemState {
STATE_IDLE,
STATE_BLINKING,
STATE_ERROR
};
// Create a variable to hold our current state, starting at IDLE
SystemState currentState = STATE_IDLE;
switch...case StatementIn our loop(), instead of running a long list of commands, we use a switch statement to check our currentState and only run the code for that specific state.
void loop() {
switch (currentState) {
case STATE_IDLE:
// Code for IDLE state
// If button pressed -> currentState = STATE_BLINKING;
break;
case STATE_BLINKING:
// Code for BLINKING state (using millis() instead of delay!)
// If error occurs -> currentState = STATE_ERROR;
break;
case STATE_ERROR:
// Code for ERROR state
// Flash red LED
break;
}
}
By using a State Machine, your loop() runs thousands of times per second. It quickly jumps into the switch, checks the state, does a tiny piece of work, and immediately loops back around.
This means your Arduino can constantly check for button presses while simultaneously managing timers with millis(). You achieve true multitasking without ever needing to use delay().
Take a previous project that uses delay()—like a traffic light sequence or a buzzer melody—and try rewriting it as a State Machine. Define your states, set up your switch...case, and watch how much more responsive your buttons become!