About finite state machines
Finite state machines are a convenient means to model and implement the behavior of many physical or logical objects. Good candidates are processes (workflows), communication protocols and more generally, any situation where executing an action is bound to the occurrence of an event, as in the case of embedded systems and devices used in IoT solutions. Resorting to finite state machines to implement these cases can save you a lot of time and also brings more flexibility into your design: you can add new conditions and corresponding actions by simply adding new states and transitions to your “machine”.
Finite state machines are part of the many tools that scriptr.io provides to developers in order to simplify and accelerate the development of their IoT applications.
In this tutorial, we go through a step by step example illustrating how to use this feature by implementing the following simple scenario.
A watering device needs to adjust the flow of the water it is dispensing according to the humidity level of the soil, which in turn is measured by another humidity sensor. For simplicity’s sake, we consider that there are only three possible humidity levels: “normal”, “wet” and “dry”, which should result in the following respective water flows: “normal”, “reduced” and “increased”.
Since the water flow directly depends on the humidity level, the behavior of our system can easily be represented as a state machine:
The above figure indicates that whenever our machine receives the “dry” event, it shifts to the “increased_flow” state. When it receives the “wet” event, it shifts to the “reduced_flow” state and when the event is “normal” it moves to the “normal_flow” state.
Our state machine will be running on scriptr.io and communicating with the aforementioned devices through WebSockets, playing the role of a mediator between them. More specifically:
- The humidity sensor sends messages (humidity levels, i.e. “events”) to the state machine via a web socket connection
- Messages fire transitions to the next states (e.g. “dry” fires the transition the “increased_flow” state)
- Transitions publish events to a given channel (“wateringsystem”), to which the watering device is subscribed, allowing it to receive instructions to regulate the water flow
- In order to run this tutorial, you need an account on scriptr.io
Scriptr.io’s State Machine Editor Overview
Log into your scriptr.io account and launch the State Machine editor by clicking on the arrow at the right of the “New Script” option, at the bottom left corner of the screen.
To create a state, click on the “Add state” icon.
To add a transition, click on the “Add transition” icon.
To rename a state or a transition, just double-click on it, enter the new name in the “Name” field of the dialog box that opens, then save.
To connect two states (including the initial pseudo-state) using a transition, drag each end of the transition and drop it on a targeted state.
- Transitions names are events. This means that whenever you need to fire a transition from one state to another, you need to send the corresponding event to the instance of the state machine (we will see how this is done in a later section). In our example, this means that if the state machine script receives the “dry” event, it shifts to the “increased_flow” state. If it receives the “normal” event, it shifts to the “normal_flow” state, etc.
- Although state names and transitions names (events) are always displayed in capital letters, names are actually case sensitive.
Step 1 – Create the state machine
- In the state machine editor, add three states and rename them respectively to “normal_flow”, “reduced_flow” and “increased_flow”.
- Add three new transitions, starting from the initial pseudo state and targeting the “normal_flow”, “reduced_flow” and “increased_flow” states, renaming the transitions to “normal”, “dry” and “wet” respectively. To change the names, double click on each transition and type a new name in the dialog box, then save.
- From the “normal_flow” state, draw two transitions: one to the “increased_flow” state and the other to the “reduced_flow” state and respectively rename them to “dry” and “wet”.
- From the “reduced_flow” state, draw two transitions: one to the “increased_flow” state and the other to the “normal_flow” state and respectively rename them to “dry” and “normal”.
- Last, from the “increased_flow” state, draw two transitions: one to the “reduced_flow” state and the other to the “normal_flow” state and respectively rename them to “wet” and “normal”.
Step 2 – Create a channel for publish/subscribe communication
As mentioned in our scenario, our state machine communicates with the watering system by publishing messages to a channel to which the watering device is subscribed.
To create a new channel, click on your user name at the top left corner of scriptr.io’s workspace, then click on “Settings” and select “Channels”. Click on “Add Channel” then enter the channel name (we chose “watersystem” in this example). Make sure to specify read/write permissions on this channel. To keep things simple, we grant read/write permissions to “anonymous” scripts, i.e. scripts that can be invoked using your anonymous token. Once done, don’t forget to click on the “check” sign on the right to save and validate your changes before closing the dialog.
Step 3 – Send instructions to the watering device
We now need to inform the watering device of the adjustment to make to the water flow. This is done using our state machine, adding a few lines of code to our transitions. As described in our scenario, the “dry” (humidity) event should result in an “increased” flow, the “wet” event should result in a “reduced” flow and the “normal” event should result in a “normal” flow. Therefore, all we need to do is to publish the flow using the “watersystem” channel.
To add code to a transition, just click on it to open the code editor.
In the editor, invoke the native publish function and pass it the name of the channel and the message to publish. In the above example, we selected a transition fired by the “dry” event, therefore we publish the “increased” instruction to the channel. Proceed similarly with the other transitions but make sure to publish the appropriate flow (“decreased” for “wet” and “normal” for “normal”).
Important: Notice that the default code that is available for any transition is “return true”. This instructs the state machine that it can actually move to the next state, whereas returning false would prevent it. You can use this feature to add guard conditions to your transition, i.e. combine the occurrence of the event that fires the transition to some conditions that need to be verified before moving to the next state. Make sure though to return true whenever moving to that state is possible.
Running the state machine
State machines you define in scriptr.io are actually scripts and thus can be invoked as any other script, through HTTP requests or WebSocket messages. However, state machines also expect to receive some specific parameters.
Starting a state machine
To start a state machine, just invoke the script and pass it the “event” parameter, set to the value of one of the events that can fire a transition stemming from the initial pseudo-state of the state machine. In the case of our tutorial, the value of the event parameter can be one of “dry”, “wet” or “normal”.
Try running the state machine from the workspace: click on the arrow next to “Run”. Type “event” as a parameter name and set “dry” as the value the click “Run”.
Notice that in the returned response you get the identifier of the state machine instance that was created, as well as its state. You should keep this identifier for further calls to that specific instance.
Sending events to a state machine
Sending events to a state machine instance is similar to starting a new instance, except that in the current case, you also need to specify the id of the instance by sending the id parameter along with the event parameter.
Check the source code out from Github.
You can also try it online (note that the simulators display data on the browser’s console not on the HTML page):
- Launch the watering device simulator
- Launch the humidity sensor simulator
- To start and stop the simulators, press the start and stop buttons on the screen.
(output is on the console not the page)