Library: function-fsm [C++]

Finite state machines form the framework of many of the systems, automated machines, and robots I program at work. They are used to define the behaviour of processes on individual machines and of a system as a whole.

I also use state machines ranging from small to complex in my own projects. I’ve even used them to control the detailed behaviour of weapons in a game I’m currently working on. (It’s quite nice to be able to think in terms of the various states of a ‘sword’ object while it’s being used, especially when designing methods of attacking involving multi-button timed combos and cooldown systems.)

When using anything Arduino-compatible in projects, I’ve tended to use a library called arduino-fsm to create my state machines. I like it a lot: it makes turning a state diagram into code very intuitive and I think it leads to code that makes it easy to reconstruct a state diagram without getting bogged down with what actually happens inside any specific state.

I started making my own fork of arduino-fsm because I wanted to add in a few utility functions (I sorely missed having a simple way to determine what state the machine was in – to facilitate multiple state machines working together). But I quickly realised that it was an opportunity to rebuild the library to work in a slightly different way – allowing me to use the library in any C++ environment (removing dependencies on Arduino’s millis()) and get around an issue that I’d wanted to deal with for a while (I’ll get to that later). Importantly, I wanted to maintain the sensibility and simplicity of use that I enjoyed so much from arduino-fsm.

The Library

And so, I created function-fsm – a library that simplifies the creation of FSMs and encourages objects to have ownership of their state by allowing finite state machines to be held by other objects. It works in any C++11 environment, and its GitHub repo is set up so that it’s easy to use with the Arduino environment too. It’ll work with anything with implementations of std::chrono, std::function, and std::vector (I love working with the ESP32 recently!)

With function-fsm you can:

  • Create state machines with any number of states and transitions.
  • Define states with functions to call on entering and leaving a state, and to call repeatedly while in a state.
  • Define transitions with functions to define behaviour on transition and triggers to cause them.
  • Define transitions that will occur after a certain amount of time in a state.
  • Create state machines within other objects.
  • Check current state, and if in a particular state.

Have a look (or, you know, even try using it) and let me know what you think. I’d love constructive critiques of my code and any suggestions for features!

You can see examples of how to use function-fsm for standalone FSMs and within objects in the repo here (the examples compile in the Arduino environment specifically, but make the library’s use clear in any environment).

A side-by-side comparison of a state machine diagram and its corresponding code using the function-fsm library. A text based example can be found in function-fsm's GitHub repo.
A simple example showing how easily a state machine diagram can be turned into code with function-fsm. Functions are defined for the behaviour of each State. States are defined with the functions they call. An FSM is defined with its initial State. Transitions between States are added to the FSM. The FSM is run repeatedly, and State Transitions are triggered when events occur.

Motivation

The technical challenge I was really trying to solve in making my own fsm library was that of creating state machines to be held within an object. It’s easy enough to make a state machine for a traffic light, but if I wanted a set of four independent traffic lights I’d be forced to define four separate yet identical state machines. A nice way around this, I thought, would be creating a traffic light class that holds a traffic light state machine and few other bits and bobs that a traffic light would need (lights, buttons, sensors, whatever). Then if I need four traffic lights I’ve only defined the state machine once, but can create four instances of traffic light which will be completely independent.

The clearest explanation of the problem faced when trying to do this, with an example, can be found in my Stack Exchange question. I think this is a pretty good example of how you can get really useful help (even on niche problems) when you take the time to carefully explain an issue and provide a complete example that demonstrates what you’re trying to achieve and the problem you’re facing. (Thank you, Stack Overflow!)

I’ll try to explain it briefly below, but I do recommend reading the question and its top answer for the best understanding here!

In arduino-fsm, states are defined by providing pointers to the functions that define their behaviour. However, C++ “forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function” – as my compiler so helpfully reminded me when I tried it. So if you want to create a member state using member functions they have to be static. It’s not useful to make all those member functions static either, because you want them to be able to read and change things about the particular object the state machine is a part of.

The best way around this was to replace void(*)()s used to define states with std::function<void>s. They get around the limitation explained previously as they are constructible from anything callable that takes no parameters and returns void – they effectively hide the difference between a void(*)() and a void(TrafficLight::*)(). This, wrapped in a handy lambda that captures the this pointer to allow references to stuff inside the TrafficLight object (kindly suggested by someone on Stack Overflow), gives a full solution to the problem without making any sacrifices.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s