INTRODUCTION
At Ubidots we continue to see a growing crossover between maker markets and conventional industrial systems. In this article, we will continue to this growing trend and discuss Finite State Machines (FSM) and how hardware engineers can incorporate industrial operations and logic into mainstream builds. Then we will develop a simple script sample to demonstrate FSM logic for sending data to Ubidots through MQTT. As the present trend of Single Board Computers (SBCs) permeating the shop floor continues to grow, we hope this article will bring clarity to your local projects and allow you to compliment industrial gateway staples like Dell, Siemens, etc., with your local solutions resulting in $10,000s in savings for hardware and operational costs.
Before we begin, it is important to understand the concept of MQTT before jumping into this FSM article. If you are not familiar with MQTT, check out Ubidots MQTT API to better understand the basics. Below, are some standard definitions worth knowing when working with IoT, MQTT, FSM, and Ubidots.
Client: A client is the device which sends data to Ubidots for updating or creating variables. Clients are also able to retrieve data.
Broker: Brokers are the counterparts of a client. Brokers process all the data and have a main responsibility to receive messages, update variables, and notify the clients about changes in the platform and any hold sessions. (The url of the Ubidots broker is industrial.api.ubidots.com with communication port 1883.)
Publish: Publish is similar to POST at HTTP. When a client publishes a message, the broker will either update or create a new device depending on the command code.
Subscribe: Similar to the GET function in HTTP, subscribe is the method to obtain values with a huge difference in the GET request. The difference being you do not have to be continuously ask the server for each value in your custom script. For example, if a variable's value changes, Ubidots will automatically update you (the user) of any changes. If no variables change, no data will be sent to/from Ubidots thus saving data requests and processing time for your device and the overall project functionality and expense.
Pro Tip: We strongly advise using our MQTT library and run its examples as shown in this article before of continuing with this tutorial. Best to walk with MQTT before trying to sprint with MQTT. But once you know the basics, sprinting is what you get with MQTT programming.
FINITE STATE MACHINE
Finite State Machines are simply a mathematical computation of a series of cause and events. To better understand what a Finite State Machine (FSM) is, we first need to define the concept of 'state.' A state is a unique piece of information inside a larger computational program. The FSM computation changes or transitions from one state to another state in response to external inputs. A FSM is defined by a listing or logical order of its states; its initial state and the conditions for each transition. Simply, the FSM is an operation which a state is a set of instructions to be executed inside a firmware routine to complete the operation, i.e state READ_SENSORS will perform the task of reading a fixed number of analog inputs while SEND_DATA will perform an output state to send newly sensed values to an end location. Finally, the complete FSM is a new unique state called READ_SEND that implements both of the these tasks into a single command.
States are the FSM's DNA dictating internal behavior or interactions with an environment, such as accepting input or producing output which may or may not cause the system to change or transition its state. The state is to be executed specifically depending on the different conditionals defined in your FSM. This concept is very important for hardware and electrical engineers, as many practical problems like washing machines programming (when to add water or soap, when to spin or rest) are solved easily using FSM instead of the classic sequential programming paradigms; in other words, a FSM is a more 'electrical and electronic' solution for solving a hardware problem vs sequential programming.
Below are two examples of FSMs commonly found in IoT:
Mealy Machine: In the Mealy machine computation, the outputs of every state depend on the actual state and the previous input values. Typically with each Mealy computation one state's input results in a single output to a transition or to a completion state.
Moore Machine: In the Moore machine, the outputs of every state depends on the actual state and is normally based on clocked sequential systems.
STATE DIAGRAM
Any FSM must be described before being coded by a state diagram. The example below shows the FSM behavior and its transitions which are (typically) drawn using bubbles to describe states and arrows for transitions. Also, one common note when executing a FSM properly is to have an unique present state where the next (future) state that will be executed can be easily to identify.
In the diagram above we illustrate a completed Finite State Machine process. Let us assume the operation begins in State 1 then transitions to State 2 once the programming credentials have been met. After transitioning to Stage 2, the FSM computes the current state until a trigger is met to proceed to State 3 or State 4. Note that the In this diagram, State 3 and Stage 4 are end or final states which result in computed data for your project’s end result.
UBIDOTS FSM
Now, let's begin to code an FSM to send data to Ubidots and give you a real-life experience working with this programming method. For our FSM - we look to identify and react to the initial requirement: Send sensor data from our microcontroller (Espressif ESP8266) every minute to Ubidots.
Based on this initial requirement, we have chosen to implement two states using a Moore Machine FSM computation model:
WAIT: Do nothing until one minute has passed (sit in idle state for ~59 seconds).
READ_SEND: Read the analog input of the microcontroller where the sensor is attached and send the result to Ubidots using MQTT at the 60 second mark.
The state diagram illustrates the programming logic of our FSM:
From the diagram above it is clear that the transition from WAIT to READ_SEND depends exclusively on whether the independent time value is greater or less than 60 seconds. Beginning in the the next WAIT state, the program will run continually in WAIT until the independent time hits or exceeds 60 seconds. Once the 60 second mark is achieved, the FSM will transition from WAIT to READ_SEND. After the value is sent, the FSM will transition back to WAIT for an additional counting of ~59 seconds before computing transition again.
CODING
To make this example a little simpler to understand, let's look at a practical FSM code that has been divided into five separate sections to details each of the State and transition programmings. The complete code can be found in its entirety here.
Part 1 - Define Constraints
/****************************************
*Include Libraries
****************************************/
#include "UbidotsESPMQTT.h"
/****************************************
* Define Constants
****************************************/
#define TOKEN "...." // Your Ubidots TOKEN
#define WIFINAME "...." //Your SSID
#define WIFIPASS "...." // Your Wifi Pass
#define WAIT 0
#define READ_SEND 1
uint8_t fsm_state = WAIT; // Initial state
int msCount = 0; // time counter
float value; // memory space for the value to be read Ubidots client(TOKEN);
This first part of the code is not very interesting as we simply import the MQTT library for sending data to Ubidots and complete some required definitions. It is important to note that here we define the two states, WAIT and READ_SEND as the constants inside the whole code and we define the present state using the fsm_state variable.
The next part of the code reserves memory space for the independent timer and the value to be read and the MQTT client to be initialized.
It is important that you do not forget to set the proper values for your TOKEN and your WiFi network name and password. If you do not know where to find your token, please refer to Ubidots Help Center for more tips and tricks.
Part 2 - Callback
/****************************************
* Auxiliar Functions
****************************************/ void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i=0;i < length;i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
In this part of the code we provision a callback that handles data from the server when/if needed. For this simple FSM, this step is required to properly compile your code. As described in our previous published MQTT article, the callback function handles the changes of your variables in Ubidots and it is necessary to compile the code and have it defined.
Part 3a - Main Functions - Setup()
/****************************************
* Main Functions
****************************************/ void setup() { // initialize digital pin LED_BUILTIN as an output.
pinMode(A0, INPUT);
client.wifiConnection(WIFINAME, WIFIPASS);
client.begin(callback);
}
Now let's begin with the main functions. In our setup() we will set the analog pin zero as an input (you should edit the number of the PIN depending of the physical connection of your sensor) to be able to use the ADC that allows the sensor to read the environments data and represent a float number as a value. Also, we initialize the WiFi client and pass the callback function based on the previously defined programming parameters.
Part 3b - Main Functions - Loop()
void loop() {
switch(fsm_state) {
case WAIT:
if (msCount >= 60000){
msCount = 0;
fsm_state = READ_SEND;
}
break;
case READ_SEND:
value = analogRead(A0); if(!client.connected()){
client.reconnect();
}
/* Routine for sending data */
client.add("stuff", value);
client.ubidotsPublish("source1");
client.loop();
fsm_state = WAIT;
break;
default:
break;
}
// Increments the counter
msCount += 1;
delay(1);
}
A popular way to implement FSM in microcontrollers is using the switch-case control structure. For our example the cases will be our states and the switches will be the fsm_state
variable. Let's see in more detail how each state is designed:
A popular way to implement FSM in microcontrollers is using the switch-case control structure. For our example, the switch-cases will be our States and the programming that causes a transition represented by the fsm_state variable. Here we will determine READ_SEND vs WAIT where values of 1 or 0 will be sent respectfully to identify each stage of the FSM and transition between operations based on the independent 60 second timer.
Let's see in more detail how each state is designed:
WAIT: From the code of this state, we can infer that it will not do anything if the independent timer result stored at
msCount
is less than 60000 milliseconds; once this condition is reached the value offsm_state
changes and we transition to the next state, the READ_SEND state.READ_SEND: Here we read the value of our sensor and then simply add it to a variable called "stuff" and publish it's data to a device called "source1". In running this program, we will always transition back to the WAIT state before issuing a second output.
Finally, out of our switch-case structure we increment the value of our timer and have a very small delay of 1 millisecond to make time consistent with our counter.
At this point you would be asking yourself why program all of this if we can use the usual sequential programming? Imagine that you have three additional tasks to perform inside your routine:
Control a servo motor using PWM
Show values in an LCD screen
To close or open a gate
When operating multiple tasks the FSM allows for minimal data storage at a device's local memory plus the state functions can perform immediate tasks based on the input values and not only a single output. Using FSM you can do more logical decision making with less time and energy needed to deploy a tested-logical program. The FSM is your first step to Edge computing at the single device level.
Testing
Our scripts works as expected, a new device called "source1" is created with a variable called "stuff" that both receives and saves the sensor's value every 60 seconds.
Improvements
An FSM can be implemented in many ways, and sometimes the switch-case statement can be tedious to maintain if you need to manage a very large number of states. In Part 2 of this tutorial, we will show you how to implement a more efficient FSM computation using C language.
An additional improvement to the code explained here in Part 1 would be to avoid the wait 1 millisecond between every case analyzed. This can be performed using the millis()
.
Results
With this completed sample code, our script works great! Now it is your turn to give it a try and adopt your own programming and states according to your specific application's needs. Once you've mastered sending data to and from Ubidots using FSM, you can begin developing your very own amazing apps!
Do not forget to leave us your comments in our community forums or you can connect with us vis social media at Facebook, Twitter or Hackster. Don't forget to share your awesome projects with Ubidots to receive free credits for your brilliance!