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_transitionsis useless and can be removed.- At the end of
sbStateenum, include a member for size such assbStatesor 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. statesshould 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"); // C11You 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.