Arduino C++ library to simply and efficiently model and run a finite state machine.
States represent the different situations in which the machine can be at any time for given inputs.
A Transition connect two states, and are associated with a boolean trigger condition.
The AgileStateMachine library introduces great flexibility for using a finite state machine in the embedded world. The finite state machine can interact with the rest of the firmware:
- using callback functions;
- using "boolean" Actions (similar to what is done with PLCs);
- using a mix between the two methodologies;
To update the machine, call the execute() function, in order to checks transitions of current running state: if one of the transitions is triggered, the machine goes into the next state.
The state machine is quick and efficient since it relies all on pointers (on the little Arduino UNO for example, the RailCrossing example run with a loop() time cycle of about 40/50uS)
Each states can be binded up to three different callback functions that will be executed when the state is activated (on enter), when is left (on exit) and while is running (on run).
For each state it is also possible to define an optional maximum and a minimum duration time.
If a callback function is not needed, simply use value nullptr or NULL instead of the name of function.
/* The Finite State Machine */
StateMachine fsm;
State* stExampleState = fsm.addState("An Example State", onEnter, onExit, onRun);
State* stAnotherStateA = fsm.addState("Another State", onEnter);
State* stAnotherStateB = fsm.addState("Another State", onEnter, nullptr, onRun);
/* Set initial state and start the Machine State */
fsm.setInitialState(stExampleState);
fsm.start();To connect two states, a transition need to be defined. The trigger of transition can be performed with a bool function() or a bool variable.
Also the timeout of state itself can be used for triggering to next state.
stExampleState->addTransition(stAnotherStateA, aCallbackFunction); // A callback function will be used as trigger
stAnotherStateA->addTransition(stAnotherStateB, aBoolVariable); // A bool variable will be used as trigger
stAnotherStateA->addTransition(stExampleState, aTime); // An unsigned long variable will be used as timeout trigger
stAnotherStateB->addTransition(stExampleState, ()[] { // A lambda callback function will be used as trigger
return (digitalRead(input) == LOW);
}); While running the State Machine, a State timeout can be checked with the method bool getTimeout();
if(fsm.getCurrentState()->getTimeout) {....}For each state you can define also a set of qualified Actions, that will be executed when state is active causing effect to the target bool variable
bool targetVar1, targetVar2, targetVar3;
stExampleState->addAction(Action::Type::N, targetVar1); // set targetVar1 to true while state is active (clear on exit)
stAnotherState->addAction(Action::Type::R, targetVar2); // reset targetVar2 to false when state is active
stAnotherState->addAction(Action::Type::D, targetVar3, 2000); // targetVar3 == true 2000 milliseconds after state become activeThe library support this action qualifiers:
| Action qualifier | Description | Short explanation on the effect | Duration |
|---|---|---|---|
| N | Non-stored | Action is active (target = TRUE) as long as the state input is active | NO |
| R | Reset | Reset to FALSE the value of target variable | NO |
| S | Set (or Stored) | Set to TRUE the value of target variable | NO |
| L | time Limited | target = TRUE until the end of the set time or until the state is deactivated | YES |
| D | time Delayed | target = TRUE after the set time has elapsed until the state is deactivated | YES |
| RE | Rising Edge | target = TRUE only once after the state is activated | NO |
| FE | Falling Edge | target = TRUE only once after the state is de-activated*** | NO |
*** Since the state is not active anymore, target must bel cleared manually
Take a look at the examples with some "scholastic" problems solved with a state machine in the examples folder:
// Create new Finite State Machine
StateMachine fsm;// Add a State to State Machine
State* addState(const char *name);
State* addState(const char *name, uint32_t min, uint32_t max);
State* addState(const char *name, state_cb enter = nullptr, state_cb exit = nullptr, state_cb run = nullptr);
State* addState(const char *name, uint32_t min = 0, state_cb enter = nullptr, state_cb exit = nullptr, state_cb run = nullptr);
State* addState(const char *name, uint32_t min, uint32_t max, state_cb enter = nullptr, state_cb exit = nullptr, state_cb run = nullptr);
// Start the State Machine
void start();
// Stop the State Machine
void start();
// Run the State Machine (true on transitions)
bool execute();
// Set first state (after start)
void setInitialState(State* state);
// Force to specific state
void setCurrentState(State *newState, bool callOnEntering = true, bool callOnLeaving = true);
// Get number of states
const int GetStatesNumber();
// Get label of current state
const char *getActiveStateName();
// Get pointer to current state
State* getCurrentState();
// Get active state name
const char* ActiveStateName();
// Get the number of defined finite states
const int GetNumStates()// Add a transition to state
Transition* addTransition(State *out, bool &trigger);
Transition* addTransition(State *out, condition_cb trigger);
Transition* addTransition(State *out, uint32_t timeout);
// Add an action to state
Action* addAction(uint8_t type, bool &target, uint32_t _time = 0);
// Get the state index (the position as added in the linked list of StateMachine class)
uint8_t getIndex();
// True if state is running for a time greater then max time
bool getTimeout();
// Set the max time for current state
void setStateMaxTime(uint32_t _time);
// Set the min time for current state (before exit)
void setStateMinTime(uint32_t _time);
// Reset the enter time (usefull for extend the running time over timeout)
void resetEnterTime();
// Get the time when state has entered
uint32_t getEnterTime();
// Get the state label name
const char* getStateName();
The library works virtually with every boards supported by Arduino framework (no hardware dependency)
- 1.0.4
getStateName()bugfix - 1.0.3 State name passed with template,
F()macro support - 1.0.2 Action Type::S bugfix
- 1.0.1 Callback state function in a more convenient order
- 1.0.0 Initial version


