Let's find out what are Finite State Machines and how they can help us write better code - more maintainable, future proof and easier to understand - in our IoT projects.
But first, some background information.
What is a Finite State Machine?A finite-state machine (FSM) (...) is a mathematical model of computation used to design both computer programs and sequential logic circuits. It is conceived as an abstract machine that can be in one of a finite number of states. The machine is in only one state at a time; the state it is in at any given time is called the current state. It can change from one state to another when initiated by a triggering event or condition; this is called a transition. A particular FSM is defined by a list of its states, its initial state, and the triggering condition for each transition.Why Developers Should Be Force-Fed State Machines
Because state machines are awesome. At least according to this Shopify post:
The main reason for using state machines is to help the design process. It is much easier to figure out all the possible edge conditions by drawing out the state machine on paper. This will make sure that your application will have less bugs and less undefined behavior. (...)
Moreover, state machines have decades of math and CS research behind them about analyzing them, simplifying them, and much more. Once you realize that in management state machines are called business processes, you'll find a wealth of information and tools at your disposal.We Don't Need One Until We Do
Another interesting post, that led me to the one mentioned above, states this:
The problem is that you almost never create an object fully formed with all the behaviour it is ever going to need, rather you build it up over time. (...) It's a bit of a catch-22. It's overkill and by the time it's not, it's too late. (...)
(By using an FSM) there is less complexity than you would think and more benefits than you would expect as long you don't try to retrofit a state machine after the fact. So next time you have an object that even hints at having a "status" field, just chuck a state machine in there, you'll be glad you did. I guarantee it or your money back :).
Starting to look interesting? Let's take a look at few concepts.
FSM Concepts- A state is a description of the status of a system that is waiting to execute a transition.
- A transition is a set of actions to be executed when a condition is fulfilled or when an event is received.
- A state diagram is one way of representing an FSM:
A state transition table is another way of representing an FSM:
Entry and exit actions: in some FSM representations, it is possible to associate actions with a state:
- Entry action: performed when entering the state
- Exit action: performed when exiting the state
Note: the FSM library I am using for my Particle projects, allows for such actions.
Practical Application: A Garage DoorI will create a state machine for a very popular project in the DIY IoT world: a garage door opener. What do you think are the possible states a garage door can be at any time? Yep, you guessed it right:
- close
- open
So let's draw a state diagram in plantUML with the information we have so far:
In code it looks like this:
State closeState = State(closeEnterFunction, closeUpdateFunction, closeExitFunction);
State openState = State(openEnterFunction, openUpdateFunction, openExitFunction);
//initialize the state machine, start in state: close
FSM garageStateMachine = FSM(closeState);
void loop()
{
garageStateMachine.update();
}
From One State to AnotherRemember that an FSM can change from one state to another when a triggering event or condition happens; this is known as a transition.
Imagine our garage door is closed. We can say it's in the CloseState. An event (for instance: hitting the remote button) will trigger the transition called opening. After a short time, the garage door will be in the OpenState.
Let's add the transitions to our state diagram:
In code, transitions look like this:
void closeEnterFunction()
{ //actions to execute when entering the state
}
void closeUpdateFunction()
{
if (OPEN_BUTTON_WAS_PRESSED)
{
garageStateMachine.transitionTo(openState);
}
}
void closeExitFunction()
{ //actions to execute when leaving the state
}
void openEnterFunction()
{
}
void openUpdateFunction()
{
if (CLOSE_BUTTON_WAS_PRESSED)
{
garageStateMachine.transitionTo(closeState);
}
}
void openExitFunction()
{
}
A More Complete FSMTo make things more realistic, we can now add two more states:
- opening
- closing
Our state diagram looks like this now:
And the code:
State closeState = State(closeEnterFunction, closeUpdateFunction, closeExitFunction);
State openState = State(openEnterFunction, openUpdateFunction, openExitFunction);
State openingState = State(openingEnterFunction, openingUpdateFunction, openingExitFunction);
State closingState = State(closingEnterFunction, closingUpdateFunction, closingExitFunction);
Double garage? No problem!Do you need to control a second garage? Just create another FSM:
FSM garageStateMachine2 = FSM(closeState2);
Triple Garage?I let you do the home work. Hint: it's the same as adding code for a second garage.
Other Practical ApplicationsI did my thermostat project with FSMs, and after tinkering for almost a year now with it I can say that it is code I Iook forward maintaining. For instance, I created the thermostat during winter so I coded only the states required for heating. When summer came along I worked on the cooling states and added them.
In retrospect, the work required to add the cooling states was not too difficult.
One thing I did not do properly in my thermostat is the handling of the fan. Instead of adding a couple of states for it, I added a status variable. This choice made me write some convoluted code that is both hard to maintain and understand. Every time I look at that part of the code, I still can't imagine what I was thinking at the time. Don't believe me? check the first few lines of the function idleUpdateFunction() in the code yourself.
I used FSMs in these other projects:
Switch-Case OptionTechnically you do not need to use an FSM library to write code that uses state machines. Another way of implementing a state machine is using switch-case statements. You can check this program for a practical example.
Have in mind that the FSM library comes with a few extras that you will need to implement yourself if you use switch-case statements instead.
Advantages of Using FSMsHere are some advantages noticed when using FSMs in my code:
- when transitioning from state X to state Y you can publish an event or log a message, so you know exactly when (and perhaps why) a transition is triggered.
- if you use an FSM library, like this one ported from Arduino, you have access to state enter and exit functions that can be useful to set things up (or clean up) while entering/leaving a state. It also tells you how much time the FSM has been in that state.
- when you start coding with FSMs in mind you don't really know what you are doing but you try it and most probably you'll stick with it since you'll like it so much.
- if your code has a variable called state or status then is very probable that you are using an FSM but you did not know.
- when troubleshooting, if you know the state of the machine, you can narrow your search for the problem to a specific piece of code where the problem will be.
- the exercise of creating the state diagram forces you to think about exactly what you wanted the code to do and provided structure that made coding easier. You can simply write the code for each state once the overall flow was set.
- when you want to add more functionality, it can be done cleanly as a new state or a modification of an existing state. This should keep the code from getting too complex over time.
My advice? Start with a small project that has few states and get some practice with it. By the time it is done, you will have built confidence and hands on understanding about FSMs.
Because state machines are awesome. But I already told you that...
HelpIf you require professional help with your projects, don't hesitate to write me a line about your needs at gusgonnet@gmail.com. Thank you!
Comments