State Machine

Introduction

This document describes the State Machine component of the EzEmbedded Framework.

The State Machine component provides a lightweight, event-driven hierarchical state machine implementation suitable for embedded systems. It enables developers to model complex system behavior using well-defined states and transitions.

What this component does:

  • Provides a structured way to implement finite state machines

  • Supports entry, action, and exit functions for each state

  • Handles event-driven state transitions via an event queue

  • Supports hierarchical state machines (sub-state machines)

  • Allows data sharing between states via a user-defined data pointer

What this component does NOT do:

  • Does not provide automatic code generation from state diagrams

  • Does not support orthogonal regions (parallel states)

  • Does not implement history states

Use cases:

  • Protocol implementations (communication protocols, handshake sequences)

  • Device control logic (power management, mode switching)

  • User interface navigation

  • Workflow and process control in embedded applications

Component’s structure

The State Machine component is composed of two main structures:

ezState_t - Represents a single state with the following elements:

  • name: Human-readable state name for debugging

  • action: Function executed repeatedly while in this state

  • enter: Function executed once when entering this state

  • exit: Function executed once when leaving this state

  • handle_event: Function to process incoming events

  • sub_sm: Pointer to a nested sub-state machine (optional)

ezStateMachine_t - Represents the state machine itself:

  • curr_state: Pointer to the currently active state

  • next_state: Pointer to the next state (used during transitions)

  • events: Ring buffer storing pending events

  • data: User-defined data pointer shared between states

The component also provides helper macros for defining states:

  • INIT_STATE(state_name, sub_state_machine): Declares and initializes a state

  • DEFINE_ACTION_FUNCTION(state_name): Declares an action function

  • DEFINE_ENTRY_FUNCTION(state_name): Declares an entry function

  • DEFINE_EXIT_FUNCTION(state_name): Declares an exit function

  • DEFINE_EVENT_HANDLER_FUNCTION(state_name): Declares an event handler

Component’s behavior

External Behavior

The state machine exposes the following API functions:

  • ezSM_Init(): Initializes the state machine with an initial state and event buffer

  • ezSM_Run(): Gives processing time to the state machine (call periodically)

  • ezSM_SetEvent(): Pushes a new event to the state machine’s event queue

  • ezSM_ClearAllEvents(): Clears all pending events from the queue

  • ezSM_SetState(): Forces a state transition to a specific state

  • ezSM_GetCurrState(): Returns a pointer to the current state

Internal Behavior

The state machine follows this execution model during ezSM_Run():

  1. Event Processing: If the current state has an event handler and events are pending, the handler is called with the oldest event. The handler may return a new state to trigger a transition.

  2. Action Execution: If no state change occurred from event handling:

    • If a sub-state machine exists, ezSM_Run() is called recursively on it

    • Otherwise, the state’s action function is executed

    • The action function may return a new state to trigger a transition

  3. State Transition: If a next state is determined:

    • The current state’s exit function is called (if defined)

    • The exit function can override the next state if an error occurs

    • The new state’s enter function is called (if defined)

    • The enter function can redirect to another state (e.g., for error handling)

State Transition Diagram:

        flowchart TD
   Start([ezSM_Run]) --> EventPending{Event pending?}

   EventPending -->|Yes| HandleEvent[handle_event]
   EventPending -->|No| SubSMExists{Sub-SM exists?}

   SubSMExists -->|Yes| RunSubSM[ezSM_Run sub]
   SubSMExists -->|No| Action[action]

   HandleEvent --> StateChanged{State changed?}
   RunSubSM --> StateChanged
   Action --> StateChanged

   StateChanged -->|Yes| Transition["exit -> enter"]
   StateChanged -->|No| Done([Done])

   Transition --> Done
    

Data Flow

The following diagram illustrates how data flows through the state machine:

        flowchart TD
  subgraph Application
    SetEvent["ezSM_SetEvent()"]
    Run["ezSM_Run()"]
    GetState["ezSM_GetCurrState()"]
  end

  subgraph SM["ezStateMachine_t"]
    EventQueue["Event Queue\n(RingBuffer)"]
    States["curr_state\nnext_state"]
    Data["data (void*)\n(user-defined struct)"]
  end

  subgraph State["ezState_t"]
    HandleEvent["handle_event(event)\n► Returns next state or NULL"]
    Action["action(sm)\n► Returns next state or NULL"]
    Enter["enter(sm)\n► Initializes state, may redirect"]
    Exit["exit(sm)\n► Cleanup, may redirect on error"]
    SubSM["sub_sm\n(optional nested SM)"]
  end

  SetEvent --> EventQueue
  Run --> States
  States --> GetState

  EventQueue --> HandleEvent
  States --> Action
  Data --> Enter
  Data --> Exit

  Action --> SubSM
    

Data exchange between states:

  • States share data through the sm->data pointer

  • This pointer is set during ezSM_Init() and remains constant

  • States can cast it to the appropriate structure type to access shared data

Event flow:

  1. External code calls ezSM_SetEvent() to push events to the queue

  2. Events are stored in a ring buffer (FIFO order)

  3. During ezSM_Run(), events are popped and processed by handle_event()

  4. The event handler returns a new state pointer or NULL (no transition)

Component’s data type

Function Pointer Types

type ezSM_DoFunction

Function executed repeatedly while the state machine is in a state.

Param sm:

Pointer to the state machine

Return:

Pointer to the next state, or NULL if no state change

type ezSM_EntryFunction

Function executed once when entering a new state.

Param sm:

Pointer to the state machine

Return:

Pointer to redirect state, or NULL to stay in current state

type ezSM_ExitFunction

Function executed once when exiting the current state.

Param sm:

Pointer to the state machine

Return:

Pointer to override next state, or NULL to proceed normally

type ezSM_EventHandler

Function to handle incoming events.

Param event:

The event to be handled (uint8_t)

Return:

Pointer to the next state, or NULL if event doesn’t cause transition

Structures

struct ezState

Represents a single state in the state machine.

Member name:

State name string for debugging (can be NULL)

Member action:

Pointer to the action function (can be NULL)

Member enter:

Pointer to the entry function (can be NULL)

Member exit:

Pointer to the exit function (can be NULL)

Member handle_event:

Pointer to the event handler (can be NULL)

Member sub_sm:

Pointer to a sub-state machine (can be NULL)

struct ezStateMachine

Represents the state machine instance.

Member curr_state:

Pointer to the currently active state

Member next_state:

Pointer to the next state during transitions

Member events:

Ring buffer for storing pending events

Member data:

User-defined data pointer for inter-state communication

Constants

MAX_SUPPORTED_STATE

Maximum number of supported states (0xFF = 255)