0
0
Embedded Cprogramming~7 mins

Event-driven state machine in Embedded C

Choose your learning style9 modes available
Introduction

An event-driven state machine helps a program react to different events by changing its behavior step-by-step. It keeps track of what the program is doing and what should happen next.

When you want a device to respond differently to button presses depending on what it was doing before.
When controlling a traffic light that changes colors based on timers and sensors.
When managing a simple game where the player moves between menus and gameplay.
When handling communication protocols that wait for specific messages in order.
When you want to organize your program clearly by separating different modes or states.
Syntax
Embedded C
typedef enum {
    STATE_IDLE,
    STATE_WORKING,
    STATE_ERROR
} State;

typedef enum {
    EVENT_START,
    EVENT_STOP,
    EVENT_FAIL
} Event;

State current_state = STATE_IDLE;

void handle_event(Event event) {
    switch (current_state) {
        case STATE_IDLE:
            if (event == EVENT_START) {
                current_state = STATE_WORKING;
            }
            break;
        case STATE_WORKING:
            if (event == EVENT_STOP) {
                current_state = STATE_IDLE;
            } else if (event == EVENT_FAIL) {
                current_state = STATE_ERROR;
            }
            break;
        case STATE_ERROR:
            if (event == EVENT_STOP) {
                current_state = STATE_IDLE;
            }
            break;
    }
}

States and events are usually defined as enums for clarity.

The event handler changes the current state based on the event and current state.

Examples
This example toggles between ON and OFF states when a toggle event happens.
Embedded C
typedef enum { STATE_OFF, STATE_ON } State;
typedef enum { EVENT_TOGGLE } Event;

State current_state = STATE_OFF;

void handle_event(Event event) {
    if (event == EVENT_TOGGLE) {
        if (current_state == STATE_OFF) {
            current_state = STATE_ON;
        } else {
            current_state = STATE_OFF;
        }
    }
}
This example models a lock that unlocks when a code is entered and locks again after a timeout.
Embedded C
typedef enum { STATE_LOCKED, STATE_UNLOCKED } State;
typedef enum { EVENT_ENTER_CODE, EVENT_TIMEOUT } Event;

State current_state = STATE_LOCKED;

void handle_event(Event event) {
    switch (current_state) {
        case STATE_LOCKED:
            if (event == EVENT_ENTER_CODE) {
                current_state = STATE_UNLOCKED;
            }
            break;
        case STATE_UNLOCKED:
            if (event == EVENT_TIMEOUT) {
                current_state = STATE_LOCKED;
            }
            break;
    }
}
Sample Program

This program starts in IDLE state. When it receives a START event, it moves to WORKING. Then a FAIL event moves it to ERROR. Finally, a STOP event returns it to IDLE. Each transition prints what happened.

Embedded C
#include <stdio.h>

typedef enum {
    STATE_IDLE,
    STATE_WORKING,
    STATE_ERROR
} State;

typedef enum {
    EVENT_START,
    EVENT_STOP,
    EVENT_FAIL
} Event;

State current_state = STATE_IDLE;

void handle_event(Event event) {
    switch (current_state) {
        case STATE_IDLE:
            if (event == EVENT_START) {
                current_state = STATE_WORKING;
                printf("Transition: IDLE -> WORKING\n");
            }
            break;
        case STATE_WORKING:
            if (event == EVENT_STOP) {
                current_state = STATE_IDLE;
                printf("Transition: WORKING -> IDLE\n");
            } else if (event == EVENT_FAIL) {
                current_state = STATE_ERROR;
                printf("Transition: WORKING -> ERROR\n");
            }
            break;
        case STATE_ERROR:
            if (event == EVENT_STOP) {
                current_state = STATE_IDLE;
                printf("Transition: ERROR -> IDLE\n");
            }
            break;
    }
}

int main() {
    printf("Initial state: IDLE\n");
    handle_event(EVENT_START); // Start working
    handle_event(EVENT_FAIL);  // Fail occurs
    handle_event(EVENT_STOP);  // Stop and go to idle
    return 0;
}
OutputSuccess
Important Notes

Always define clear states and events to avoid confusion.

Keep the event handler simple and focused on changing states.

Print statements help debug state changes during development.

Summary

An event-driven state machine changes behavior based on events and current state.

Use enums to name states and events clearly.

Handle events with a switch or if-else to update the current state.