Implementing state machines with a table with function pointers is very common. The table should however be read-only and preferably stored in flash. This does however seem to be some PC-like ARM with Linux?
The design with a centralized state transition decision-maker is particularly important and your program is well-written there. Picking next state, based on the result from the previous, should be done at one single place in the program, not in each individual state ("stateghetti programming"). With a centralized decision-maker, you can also easily implement a safe mode for the program where it goes upon certain critical errors.
Regarding the state machine/array:
typedef enum state_transitions
- the enum tagstate_transitions
is useless and can be removed.- At the end of
sbState
enum, include a member for size such assbStates
or what you wish to call it. Then change thestates[9]
tostates[]
andstatic_assert(sizeof states/sizeof *states == sbStates, "states data inconsistent");
to guarantee data integrity. - Assuming
char name[255];
will never change, then 255 bytes per array item is wasteful. Achar*
to a string literal will save loads of read-only memory over this. states
should beconst
.
So you could instead have something like:
static const sbStateDef states[] = { [sbIdle] = {"Idle", sbIdleState, {{sbStartupTrans, sbStartup}, {sbSelfTestTrans, sbSelfTest}}}, [sbSelfTest] = {"Self Test", sbSelfTestState, {{sbStartupTrans, sbStartup}, {sbErr, sbIdle}}}, ...};static_assert(sizeof states/sizeof *states == sbStates, "states data inconsistent"); // C11
You can do similar with transitions
. Overall, designated array initializers like above, that make use of the enum is strongly recommended for readability and data integrity.
General comments:
- Never define variables in header files, ever. If you ever find yourself with the need to define a variable in a header, something has gone fundamentally wrong in your program design. In this case you could probably put both the allocation of the state machine and the calling code in main.c.
- All the
while(1)
loops mean that you can't change states... I'm assuming this is just place-holders? Such loops are unacceptable in a hosted multi-process system such as Linux. You should probably consider porting this to pthreads and put the thread to sleep when it isn't working.