C Programming 3 min read

enum, typedef and state machines

An enum gives integer constants names, and typedef gives the type a short name. Together they describe a state machine: a variable holds the current state, and a step function maps state plus event to the next state. Handling every case without a default lets the compiler flag a missed state.

The machine starts in IDLE. EV_START moves it to CONNECTING. EV_ACK moves it from CONNECTING to ONLINE. EV_TIMEOUT moves it from ONLINE back to IDLE. A timeout while CONNECTING also returns to IDLE.
The path from IDLE to ONLINE. A timeout in CONNECTING or ONLINE also returns to IDLE.

enum and typedef

enum { ST_IDLE, ST_CONNECTING, ST_ONLINE } defines named integer constants, numbered 0, 1, 2 unless you give values. typedef names the type, so state_t s; reads better than enum state s;. A final ST_COUNT entry gives the number of states, handy for sizing tables such as names below.

A state machine in a switch

Firmware often waits for things: a button to settle, a reply to arrive, a timeout. A state machine makes that explicit. One variable holds the current state, and a step function takes the state and an event and returns the next state. Each call is short and never blocks, so it fits in a super-loop or an interrupt-driven design. Because every transition is in one place, the behaviour can be tested event by event on a PC.

fsm.cC
#include <stdio.h>typedef enum { ST_IDLE, ST_CONNECTING, ST_ONLINE, ST_COUNT } state_t;typedef enum { EV_START, EV_ACK, EV_TIMEOUT } event_t;static const char *const names[ST_COUNT] = { "IDLE", "CONNECTING", "ONLINE" };static state_t step(state_t s, event_t e) {    switch (s) {                  /* no default: -Wswitch flags a missing state */    case ST_IDLE:        return (e == EV_START) ? ST_CONNECTING : ST_IDLE;    case ST_CONNECTING:        if (e == EV_ACK) {            return ST_ONLINE;        }        return (e == EV_TIMEOUT) ? ST_IDLE : ST_CONNECTING;    case ST_ONLINE:        return (e == EV_TIMEOUT) ? ST_IDLE : ST_ONLINE;    case ST_COUNT:        break;    }    return ST_IDLE;}int main(void) {    const event_t events[] = { EV_START, EV_TIMEOUT, EV_START, EV_ACK };    state_t s = ST_IDLE;    for (unsigned i = 0; i < sizeof events / sizeof events[0]; i++) {        state_t next = step(s, events[i]);        printf("%-10s -> %s\n", names[s], names[next]);        s = next;    }    return 0;}
Output
IDLE       -> CONNECTING
CONNECTING -> IDLE
IDLE       -> CONNECTING
CONNECTING -> ONLINE

Table-driven machines

When there are many states and events, a two-dimensional table, next_state[state][event], can replace the switch, often paired with a table of action functions. The whole machine is then data that can be read, reviewed and placed in Flash as const.