IoT

IoT prototyping with Raspberry Pi, Mender OTA and Firebase

This topic might seem a bit far from tower engineering but I could not skip it as IoT is not the future anymore but our present. It is great to have an insight how site-specific design parameters are gathered and analysed which might lead us to make better tower designs.

Balázs Kisfali
Balázs Kisfali
Oct 1, 2020
12
min read
IoT prototyping with Raspberry Pi, Mender OTA and Firebase

Introduction

In my previous post I left open several issues of my small Raspberry Pi weather station project. These are:

  1. Script on the Pi terminates upon closing the SSH connection and no resilience upon network or power failure
  2. data only printed to the terminal, no database connection
  3. not backed with a robust IoT system
  4. no solution for remote software updates
  5. no visualisation of the data

In this post I will address all these issues and solve them each by each. This will be still a prototyping project, so I will still leave few challenges for a later blog post. Anyhow, by the end of this post you will have an overview how to scale up a small IoT system into a more reliable service level.

I will use the numbering of the small issue list above, so you may also jump to certain part of the post.

1. Make the code permanently running

As we saw in the previous post, the Python scripts is terminating if we close the SSH connection. We can solve this issue in several ways. There are some Linux libraries which can run commands "behind the scenes". We could utilise the nohup command to execute other programs as its argument. Another way to install a library, like screen. This small Linux utility app lets us create sessions. In those sessions whatever we do we can return to it later. But my choice (as always) is something which is native and doesn't require additional libraries. That is systemd. Systemd is an init system used in Linux distributions to bootstrap the user space and to manage system processes after booting. It includes a collection of tools for a range of different tasks. Its primary purpose is to initialise, manage and track system services and daemons, both during start-up and while the system is running.

So, let's create a service which can run when the Pi will boot, more precisely after the network connection is established. We can really fin-tune when and how we want our code to be running. In the below service script, you may see that the I have chosen After=network.target, so this will allow my Pi to execute my code only after the network connection is established. The other important piece of the code is ExecStart where we can state which program should be triggered by what programming language. In my case that is Python3 and the main.py program will run.

[Unit]
Description=My service
After=network.target

[Service]
ExecStart=/usr/bin/python3 -u main.py
WorkingDirectory=/home/pi/myscript
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi

[Install]
WantedBy=multi-user.target
Link to Raspberry

Copy this file into /etc/systemd/system as root, for example:

sudo cp myscript.service /etc/systemd/system/myscript.service

Once this has been copied, we can attempt to start the service using the following command:

pi@raspberrypi:~ $ sudo systemctl status myscript.service

Stop it using following command:

sudo systemctl stop myscript.service

When we are happy that this starts and stops our app, we can have it start automatically on reboot by using the following command:

sudo systemctl enable myscript.service

The systemctl command can also be used to restart the service or disable it from boot up! Now our program is running we can test the service for program crash. But before to do so, we can check the status of the program.

pi@raspberrypi:~ $ sudo systemctl status myscript.service
Link to Raspberry

After running the command we got a result something similar to the below one.

● myscript.service - My service
   Loaded: loaded (/etc/systemd/system/myscript.service; enabled; vendor preset:
   Active: active (running) since Sat 2020-08-22 13:28:32 BST; 1 weeks 0 days ag
 Main PID: 1801 (python3)
    Tasks: 3 (limit: 2200)
   Memory: 19.1M
   CGroup: /system.slice/myscript.service
           └─1801 /usr/bin/python3 -u wind.py

Aug 30 09:52:46 raspberrypi python3[1801]: Number of spins:  5
Aug 30 09:52:46 raspberrypi python3[1801]: Wind speed (mph):  3.75
Aug 30 09:52:46 raspberrypi python3[1801]: Global count:  59
Aug 30 09:52:46 raspberrypi python3[1801]: Start 3 second
Aug 30 09:52:47 raspberrypi python3[1801]: Temp=20.0*C  Humidity=69.7%
Aug 30 09:52:50 raspberrypi python3[1801]: Number of spins:  4
Aug 30 09:52:50 raspberrypi python3[1801]: Wind speed (mph):  3.0
Aug 30 09:52:50 raspberrypi python3[1801]: Global count:  64
Aug 30 09:52:50 raspberrypi python3[1801]: Start 3 second
Aug 30 09:52:50 raspberrypi python3[1801]: Temp=20.1*C  Humidity=69.6%

It also prints terminal output as in the script I printed out data to the terminal. This we can delete from the code as we will not need it later once we have the database connection setup. We can simulate a program crash by terminating the Python scrip we are running. First we have to find the process id.

pi@raspberrypi:~ $ ps -ax | grep wind
 1801 ?        Ssl  143:23 /usr/bin/python3 -u wind.py
24454 pts/0    S+     0:00 grep --color=auto wind

The first number is the process ID, so to stop it we do:

pi@raspberrypi:~ $ kill -9 1801

Then we can check if it restarted by running the same script as before but now we look for the process ID change. As you can see below the process ID is renewed, so we the program restarted successfully and systemd is doing a great job.

pi@raspberrypi:~ $ ps -ax | grep wind
25066 ?        Ssl    0:01 /usr/bin/python3 -u wind.py
25201 pts/0    S+     0:00 grep --color=auto wind

2. Database connection

So far the script prints some results only to the terminal. That is not so useful as we want to make some visualisation later, so we need the data in a database. For this prototyping phase I will use a simple but scalable solution with a Firebase realtime database.

I will add some new code to my existing script as below. You can find the full code in my previous post, so I just highlight the new lines for the Firebase connection.

import pyrebase

db_url = 'https://home-iot-wind.firebaseio.com/'
config = {
  "apiKey": "<YOUR_APIKEY>",
  "databaseURL": db_url,
}
firebase = pyrebase.initialize_app(config)
db = firebase.database()
...
while True #loop
    ...
    data_3sec = {
        "bws-3-sec": bws_3_sec,
        "temperature": temperature,
        "humidity": humidity,
        "timestamp": time.time()
    }
    #build some path
    db.child("home").child("wind").child("3_sec").push(data_3sec)

You can see that by utilising a small library pyrebase and few lines of code our data is writing to the realtime database in Firebase.

Firebase realtime database

3. Connecting to a robust IoT system

There are certainly many providers out there for IoT device management but I prefer to choose a system which can scale, has open source library with an active community and flexible enough to attach other systems to it. I have made my research and tested out several options, namely:

My choice is Mender as their published solution works well, the dashboard is not overwhelming, and scales well and promises a real industrial experience for software updates and it's open sourced. The open source library at GitHub got the last commit just 2 days ago (as per the time of writing) and they have a very active community on their Hub page.

We can have two major choices when it comes to Mender. We can go with their hosted solution or we deploy the open source Mender to our server or virtual machine. For simplicity and avoiding extra charges to run a VM I will show you how I used their hosted solution. Anyhow, they offer currently 10 devices free of charge for 12 months, so it is sufficient enough to play around with a small fleet.

We can head to the Raspberry Pi setup section in their documentation. Then can download the Raspberry Pi OS image with Mender integrated. At this time I will use the Raspberry Pi Imager to flash the SD card. Since I am using a custom image from Mender, I need to choose the Custom Image option from the Imager app.

Raspberry Pi Imager app.

Once we have the file on the SD card we have to remember to enable the SSH and add a config file for the network wi-fi setup. If you don't know how to do it, please see in my previous blog post.

Now we can power up the Pi and connect to it through the SSH connection. In the meantime we can login to our Mender account and head over to DEVICES and then CONNECT A DEVICE. From here it will be a breeze to setup the Mender client on the device.

Mender hosted account: CONNECT A DEVICE.

If we did everything well, then the Pi should show up on our Mender dashboard and we have to accept it as a device.

4. Software updates

Mender is popular for their OTA (Over-The-Air) software update capability and robustness. Companies like NOKIA, Uber, Lyft, Piaggio and many more rely on their services for software updates.

A software update is relatively easy with the hosted Mender and basically involves to upload the script and deploy this software release ti our fleet. Which is in my case is composed by only one Pi but the exact same procedure works with thousands of devices.

First of all we need the target directory on the remote device where we want to deploy our software release. In my Pi my wind.py script sits in the following folder:

pi@raspberrypi:~/wind $ pwd
/home/pi/wind

Now we have the target directory on the pi we can head over for the update. Once we are signed in to the service, we go to RELEASES and then click UPLOAD. Then we can drag a new update in our code, if any. To demonstrate that the update eventually be happen, I add a new comment line into my script as I don't want to change anything else in the moment.

Add a comment line to the Python script. @80%

When we click on the UPLOAD button on the Mender dashboard under RELEASE, we will be also prompt to give the path of the target directory, a name for the artifact and the device types compatibility. With the last one, we need to choose our device type we have already set up earlier when we have deployed the Pi.

Software release with Mender.

Now we can go ahead to deploy our release to our group of devices which our Pi is assigned to. In my case I have a group name Home, so I choose this group. You may imagine that you have several groups of devices, maybe also other types, or older version of a Pi, then it is good to create different groups as they likely need different software updates. So, go to RELEASES, choose the release you want use for the deployment (in my case I called it "Blog"), click on CREATE DEPLOYMENT WITH THIS RELEASE and follow the prompts.

Deploy a software release with Mender.

We have several option about how Mender retries the deployment in case of any failure. However, the good thing with Mender is that if a deployment completely fails for any reason then it gracefully returns to the previous working release. You can read more about their retry functionality here. In case we have a successful deployment we should see a small green status mark in our finished deployments.

A successful deployment with Mender.

Now I can login to my Pi and control if the scripts was updated properly. If I list my files in the local directory where Mender deployed the script, then I can see that the file has been changed in the last few minutes.

pi@raspberrypi:~/wind $ ls -hartl
total 12K
drwxr-xr-x 11 pi pi 4.0K Aug 24 10:07 ..
drwxr-xr-x  2 pi pi 4.0K Oct  1 06:34 .
-rw-r--r--  1 pi pi 3.1K Oct  1 07:04 wind.py

And if I cat the file the nI can see the new comment line at the end of my file.

...
    wind_gust = max(store_speeds)
    #wind_speed = statistics.mean(store_speeds)
    #print(wind_speed, wind_gust) """

pause()

# Hello from Mender update :)
pi@raspberrypi:~/wind $ 

By now we have release and deployed a software update successfully. One task is remained though. The newly updated script is not executed so far. The Pi runs the previously deployed code from the memory as it didn't get instruction to restart the script. One solution can be to create a new service script to kill the running process and restart the new script.

5. Visualisation of the wind data

Now we have all the capabilities we need for our robust IoT system, it's time to visualise some data.  I'll not go to the details of the front-end development as that is probably a bit out of the scope of the this article. The main task here is to get out the latest values from the Firebase real-time database. Our script is pushing data in every 3 seconds. With Firebase it is easy to find the latest data with the .limitToLast() method on a database reference. With lines of code we can get out the temperature, humidity and wind speed data. The following lines demonstrates the principal of it.

...
// Create database reference
const tempRef = database.ref('home').child('wind').child('3_sec');
    // Sync objects changes
    tempRef.limitToLast(1).on('value', function(snapshot) {
        snapshot.forEach(function(childSnapshot) {
            var childData = childSnapshot.val().temperature.toFixed(1);
            console.log("temperature: " + childData);
            tempElement.innerText = childData + "°C";
        });
    });
...

I've borrowed some CSS stylings and JS function from here, so I can setup a simple dashboard to visualise my data. I can also utilise the hosting function at Firebase, so After deploying index.html and JS files the dashboard looks like this. For real time data you may visit this url: https://home-iot-wind.web.app/ ...some weeks have passed since I deployed my system and in the meantime the humidity sensor has damaged and shows incorrect values. This period is not so windy here in Norway, in the Drammen fjord at this period of time, so the wind speeds are moderate.

Conclusion

We have seen throughout these two posts that to setup an IoT system right is a complex task. It is something if we want to run some code locally in our Raspberry Pi or other embed device but it something completely different if we seek for some industrial solution and robust, resilient IoT system. The challenge is not yet finished as we have just gathered our data into a database and display the latest 3-second updates from the sensors. Data analysis should kick-off at this stage and analyse the many data we have acquired. This is what we are doing for instance at the TowerUp WindFinder when we gather 25 years of historical wind speed data from different locations in the globe to predict 50-years extreme wind speeds. If you want to know more about the wind speed prediction, please see my other post about the topic.

This topic was maybe a little far from tower engineering but I could not skip this topic as IoT is not the future anymore but our present. I think we, structural engineers, it is very beneficial to have an overview about how site-specific design parameters are gathered and analysed, so let us make better decisions when we design structures.

I hope you liked this post anyway and if so, please share it with your friends and colleagues and/or subscribe to our newsletter to be informed about new posts.

More articles

Go to blog