0
0
Embedded Cprogramming~7 mins

State machine for protocol handling in Embedded C

Choose your learning style9 modes available
Introduction

A state machine helps manage different steps or stages in a communication protocol clearly and safely.

When you need to handle different messages or commands in a fixed order.
When your device talks to another device and must follow rules step-by-step.
When you want to keep your code organized and easy to understand.
When you want to avoid mistakes by controlling what happens next based on current state.
Syntax
Embedded C
typedef enum {
    STATE_IDLE,
    STATE_WAIT_ACK,
    STATE_PROCESS_DATA,
    STATE_ERROR
} State;

typedef enum {
    EVENT_START,
    EVENT_ACK_RECEIVED,
    EVENT_TIMEOUT
} Event;

State current_state = STATE_IDLE;

void handle_event(Event event) {
    switch (current_state) {
        case STATE_IDLE:
            if (event == EVENT_START) {
                current_state = STATE_WAIT_ACK;
            }
            break;
        case STATE_WAIT_ACK:
            if (event == EVENT_ACK_RECEIVED) {
                current_state = STATE_PROCESS_DATA;
            } else if (event == EVENT_TIMEOUT) {
                current_state = STATE_ERROR;
            }
            break;
        case STATE_PROCESS_DATA:
            // process data here
            current_state = STATE_IDLE;
            break;
        case STATE_ERROR:
            // handle error
            current_state = STATE_IDLE;
            break;
    }
}

Use enum to define all possible states clearly.

Use a switch statement to decide what to do based on the current state.

Examples
This example shows a simple 3-step state machine reacting to events 1 and 2.
Embedded C
typedef enum {
    STATE_INIT,
    STATE_WAIT,
    STATE_DONE
} State;

State state = STATE_INIT;

void update_state(int event) {
    switch(state) {
        case STATE_INIT:
            if (event == 1) state = STATE_WAIT;
            break;
        case STATE_WAIT:
            if (event == 2) state = STATE_DONE;
            break;
        case STATE_DONE:
            // finished
            break;
    }
}
This example uses characters as events to move between states.
Embedded C
enum State {IDLE, RECEIVING, SENDING};
enum State current = IDLE;

void process_event(char event) {
    switch(current) {
        case IDLE:
            if (event == 'r') current = RECEIVING;
            break;
        case RECEIVING:
            if (event == 's') current = SENDING;
            break;
        case SENDING:
            if (event == 'i') current = IDLE;
            break;
    }
}
Sample Program

This program simulates a simple protocol using a state machine. It prints messages as it moves through states based on events.

Embedded C
#include <stdio.h>

typedef enum {
    STATE_IDLE,
    STATE_WAIT_ACK,
    STATE_PROCESS_DATA,
    STATE_ERROR
} State;

typedef enum {
    EVENT_START,
    EVENT_ACK_RECEIVED,
    EVENT_TIMEOUT
} Event;

State current_state = STATE_IDLE;

void handle_event(Event event) {
    switch (current_state) {
        case STATE_IDLE:
            if (event == EVENT_START) {
                printf("Starting protocol, waiting for ACK...\n");
                current_state = STATE_WAIT_ACK;
            }
            break;
        case STATE_WAIT_ACK:
            if (event == EVENT_ACK_RECEIVED) {
                printf("ACK received, processing data...\n");
                current_state = STATE_PROCESS_DATA;
            } else if (event == EVENT_TIMEOUT) {
                printf("Timeout! Error occurred.\n");
                current_state = STATE_ERROR;
            }
            break;
        case STATE_PROCESS_DATA:
            printf("Data processed successfully. Returning to idle.\n");
            current_state = STATE_IDLE;
            break;
        case STATE_ERROR:
            printf("Handling error, resetting to idle.\n");
            current_state = STATE_IDLE;
            break;
    }
}

int main() {
    handle_event(EVENT_START);
    handle_event(EVENT_ACK_RECEIVED);
    handle_event(EVENT_START);
    handle_event(EVENT_TIMEOUT);
    handle_event(EVENT_START);
    handle_event(EVENT_ACK_RECEIVED);
    handle_event(EVENT_START);
    handle_event(EVENT_ACK_RECEIVED);
    handle_event(EVENT_START);
    handle_event(EVENT_TIMEOUT);
    return 0;
}
OutputSuccess
Important Notes

Always define all possible states to avoid unexpected behavior.

Keep state transitions clear and simple to follow.

Use print statements or logging to debug state changes during development.

Summary

A state machine helps control protocol steps clearly and safely.

Use enums and switch-case to manage states and events.

Testing with different events helps ensure your protocol works as expected.