How to build your firmware continuously in the cloud and deploy it to your devices automatically.
Adding Over The Air (OTA) updates is an important factor for IoT applications to succeed long term. OTA is a mechanism to ensure that devices are always up to date with new settings and security fixes. It's also useful for adding new features to the hardware, making the customer who bought the device feel happier and more confident with the hardware improvements.
There are two important parts on an OTA architecture:
Here I’ll show how to setup an initial OTA mechanism using Google Cloud tools. Then, I will guide you through the process of deploying those updates to ESP8266 and ESP32 boards using the Arduino platform.
PlatformIO will be used for building the images. It has a set of command line tools that enable us to automate the process of generating binary images for the devices. On Google Cloud, we're going to useGoogle Cloud Build—a managed Continuous Integration environment—Google Cloud Storage for storing the binary images on the cloud, and Cloud Functions to handle HTTP request querying for current firmware versions and also for managing them.
PlatformIO is a set of cross-platform tools for developing solutions for embedded devices. It supports a lot of different platforms and frameworks for IoT development. It also has a huge set of libraries made by the community that can be easily used in your project. I recommend installing the Visual Studio Code (VSCode) IDE and the PlatformIO plugin to get started. Follow the steps below below:
The code for this project is available here on Github. Clone or download the project and open it in your IDE.
The platformio.ini file contains all the configuration to build the project on the ESP32 and ESP8266 boards. Also, the project dependencies are listed here. An important configuration is the build flag VERSION, which is compiled on the project code to mark which version the device is currently running. So, every time we create a new firmware version, this code should be bumped so the device will be able to check whether it needs to update to a new version.
The device code makes an HTTP query to the backend, sends the current version, and checks whether it should download a new one. Additionally, there's a device-internal HTTP handler to display the current version. To handle WiFi connectivity, the project uses the WiFiManager library, which creates an access point to setup WiFi on the device.
To deploy to the board, you can use the “Build” and “Upload” buttons on the PlatformIO Toolbar:
To get started with Google Cloud, you can do everything on the Cloud Console web interface. The command line tools are, however, a more powerful toolset. Later, we’ll need to deploy our cloud function.
——————————————————————————
# Authenticate with Google Cloud:
gcloud auth login
# Create cloud project — choose your unique project name:
gcloud projects create YOUR_PROJECT_NAME
# Set current project
gcloud config set project YOUR_PROJECT_NAME
——————————————————————————
Now let’s create the Cloud Build configuration and also the Bucket in which we can store the binaries. Follow these steps:
Cloud Build Setup
Cloud Storage Setup
Our repository contains a cloudbuild.yaml file, which contains all the configuration to build the firmware and push it to Cloud Storage. Cloud Build uses Docker for building artifacts, so I used an image that contains all the PlatformIO tools for building embedded projects using our platformio.ini file.
Now, every time that you push a new tag to your repository, it'll trigger a build on Cloud Build. You can create the tag on the UI of your git provider or you can do so using the following git commands:
——————————————
git tag -a v1.0.0 -m "First build"
git push -u origin --tags
——————————————
And if everything is working correctly up to this point, you should start seeing some builds on the History tag of your Cloud Build page when you push a new tag. We’ll revisit this at the end of the post to see how to push new versions.
To control the OTA process, we need two things:
I've built two cloud functions that help us achieve these two requirements:
Now, you'll need the gcloudtool that we installed in the beginning to deploy the functions. Alter the project ID on the file deploy-prod.sh and run it to deploy both functions. The first time you run the functions, you'll probably be asked to enable the cloud functions API. Just confirm and continue with the process.
————————
./deploy-prod.sh
————————
With this command, all the functions are deploying and reacting to events in our architecture.
To push and build a new firmware version that can be download by the device is simple. With just a couple of git commands, we can trigger a new Continuous Integration build. Let’s say that you add a new feature to the device, e.g. a blink on our loop function.
Now to create a new release, change the version on platformio.ini file (from v1.1.0 to v1.2.0 for example), commit the changed files, tag the new commit, and push everything to the repository. Here are the commands:
————————————————
# Commit the files
git add src/main.cpp platformio.ini
git commit -m "[feat] Add blink feature"
# Tag this commit
git tag -a v1.2.0 -m "Add blink feature"
# Send to the repository with the tags
git push -u origin master --tags
————————————————
I hope that this tutorial gave you an overview of what can be done to automate the firmware update process in order to achieve better IoT deployments. We went through many of the components involved in an OTA deployment process, but our architecture is still too simple. We can improve it by adding many more features, such as: