bygreencn / AgileStateMachine

Arduino/C++ library to simply and efficiently model and run a finite state machine.

Repository from Github https://github.combygreencn/AgileStateMachineRepository from Github https://github.combygreencn/AgileStateMachine

AgileStateMachine

Arduino C++ library to simply and efficiently model and run a finite state machine.


Introduction

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)

SFC_esempio

State definition and callback functions

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();

Transition definition and trigger

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) {....}

Action definition

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 active

The 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

Examples

Take a look at the examples with some "scholastic" problems solved with a state machine in the examples folder:

Constructor

// Create new Finite State Machine
StateMachine fsm;

Public methods of StateMachine class

// 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()

Public methods of State class

// 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();

Supported boards

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

About

Arduino/C++ library to simply and efficiently model and run a finite state machine.

License:Apache License 2.0


Languages

Language:C++ 100.0%