Skip to content

State Machine

Example:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// https://github.com/gbmhunter/FunctionPointerStateMachineExample

// https://github.com/misje/stateMachine
// http://misje.github.io/stateMachine/

#include <stdio.h>
#include <stdbool.h>

typedef enum
{
  ST_IDLE,
  ST_LED_ON,
  ST_LED_OFF
} state_t;

typedef struct
{
  state_t currState;
} stateMachine_t;

/// \brief      All the possible events that can occur for this state machine.
/// \details    Unlike states_t, these do not need to be kept in a special order.
typedef enum
{
  EV_ANY,
  EV_NONE,
  EV_BUTTON_PUSHED,
  EV_TIME_OUT,
} event_t;

typedef struct
{
  const char *name;
  void (*func)(void);
} stateFunctionRow_t;

void Led_Init()
{
  printf("Led_Init() called.\r\n");
}

void Led_On()
{
  printf("LED turned on.\r\n");
}

void Led_Off()
{
  printf("LED turned off.\r\n");
}

void Led_Idle()
{
  printf("LED in idle state.\r\n");
}

/// \brief  Maps a state to it's state transition function, which should be called
///         when the state transitions into this state.
/// \warning    This has to stay in sync with the state_t enum!
static stateFunctionRow_t stateFunctionA[] = {
    // NAME         // FUNC
    {"ST_IDLE", &Led_Idle},   // ST_IDLE
    {"ST_LED_ON", &Led_On},   // ST_LED_ON
    {"ST_LED_OFF", &Led_Off}, // ST_LED_OFF
};

typedef struct
{
  state_t currState;
  event_t event;
  state_t nextState;
} stateTransMatrixRow_t;

static stateTransMatrixRow_t stateTransMatrix[] = {
    // CURR STATE  // EVENT              // NEXT STATE
    {ST_IDLE, EV_BUTTON_PUSHED, ST_LED_ON},
    {ST_LED_ON, EV_TIME_OUT, ST_LED_OFF},
    {ST_LED_ON, EV_BUTTON_PUSHED, ST_IDLE},
    {ST_LED_OFF, EV_TIME_OUT, ST_LED_ON},
    {ST_LED_OFF, EV_BUTTON_PUSHED, ST_IDLE}};

void StateMachine_Init(stateMachine_t *stateMachine)
{
  printf("Initialising state machine.\r\n");
  stateMachine->currState = ST_IDLE;
}

// TIP
void StateMachine_RunIteration(stateMachine_t *stateMachine, event_t event)
{

  // Iterate through the state transition matrix, checking if there is both a match with the current state
  // and the event
  for (int i = 0; i < sizeof(stateTransMatrix) / sizeof(stateTransMatrix[0]); i++)
  {
    if (stateTransMatrix[i].currState == stateMachine->currState)
    {
      if ((stateTransMatrix[i].event == event) || (stateTransMatrix[i].event == EV_ANY))
      {

        // Transition to the next state
        stateMachine->currState = stateTransMatrix[i].nextState;

        // Call the function associated with transition
        (stateFunctionA[stateMachine->currState].func)();
        break;
      }
    }
  }
}

const char *StateMachine_GetStateName(state_t state)
{
  return stateFunctionA[state].name;
}

int main()
{
  printf("main() called.\r\n");

  // Create new state machine object
  stateMachine_t stateMachine;

  StateMachine_Init(&stateMachine);
  printf("State is now %s.\r\n", StateMachine_GetStateName(stateMachine.currState));

  // Push button to start flasher
  printf("Button pushed.\r\n");
  StateMachine_RunIteration(&stateMachine, EV_BUTTON_PUSHED);
  printf("State is now %s.\r\n", StateMachine_GetStateName(stateMachine.currState));

  // Timeout
  printf("Timeout.\r\n");
  StateMachine_RunIteration(&stateMachine, EV_TIME_OUT);
  printf("State is now %s.\r\n", StateMachine_GetStateName(stateMachine.currState));

  // Timeout
  printf("Timeout.\r\n");
  StateMachine_RunIteration(&stateMachine, EV_TIME_OUT);
  printf("State is now %s.\r\n", StateMachine_GetStateName(stateMachine.currState));

  // Timeout
  printf("Timeout.\r\n");
  StateMachine_RunIteration(&stateMachine, EV_TIME_OUT);
  printf("State is now %s.\r\n", StateMachine_GetStateName(stateMachine.currState));

  // Push button again to stop flasher
  printf("Button pushed.\r\n");
  StateMachine_RunIteration(&stateMachine, EV_BUTTON_PUSHED);
  printf("State is now %s.\r\n", StateMachine_GetStateName(stateMachine.currState));

  return 0;
}

Result:

Current Time = Mon Aug  2 16:48:29 2021