Over-the-Air (OTA) Firmware Update
The most sought after Golioth feature is Over-the-Air firmware update (OTA). The
ability to update firmware is crucial to building a robust and reliable IoT
fleet. This is built into the Golioth SDK and the golioth_basics
sample we
ran on the previous page includes this feature. Let's try it out!
Overview
On this page we'll cover four steps to completing an OTA update:
- Change the version number in the sourcecode and build the binary
- Upload the binary to Golioth as a new Package
- Assign the device(s) to a Cohort
- Deploy the package to the device(s)
Update version number and build firmware
The golioth_basics
sample uses a common file located at
golioth-firmware-sdk/examples/common/golioth_basics.c
. Edit this file,
updating the version number to 1.2.6
. For fun, also change the tag name to
golioth_basics_new
just so it's easy to see from the logging messages that a
new firmware version is running.
LOG_TAG_DEFINE(golioth_basics_new);
// Current firmware version
static const char* _current_version = "1.2.6";
Now return to the golioth-firmware-sdk/examples/esp_idf/golioth_basics
folder
and rebuild the example.
idf.py build
Remember, do not use the idf.py flash
command this time. We will be uploading
the new binary to the device over WiFi.
After running the build command, your new binary will be located at
build/golioth_basics.bin
.
Upload the binary to Golioth and deploy it to your device
1. Create a Package
The Golioth Cloud represents upgradeable software components as "Packages". In the OTA example code, the package for the application firmware is called "main", so we'll need to create a corresponding package in the Golioth Console.
In the Golioth Console, go to Firmware Updates→Packages on the left
sidebar and click the Create button. Set the name of the package to main
, and
optionally add a short description.
2. Upload the firmware binary
Now that we have created our package, we can upload the firmware binary as the
first version of main
. Open the main
package in the list of packages,
and click New Version
.
- Set the Artifact Version to 1.2.6 (to match what was compiled into the firmware)
- Click the upload button in the middle of the window and choose your
golioth_basics.bin
file. - Click the Upload Artifact button to finish creating an artifact.
3. Assign the device to a Cohort
To enroll your device into Golioth's OTA update system, you need to assign it to a Cohort. Cohorts are groups of devices that have the same firmware and receive the same OTA updates.
First, we need to create a new Cohort for this device type. In the Golioth
Console, go to Firmware Updates→Cohorts in the left sidebar and click
the Create button. Select a name for your Cohort and click Create
.
Next, we need to assign our device to this new Cohort. In the Cohort page, click
Add Devices
, and find your device in the list. Click the Add button on the
right hand side of the device to assign it to the Cohort.
4. Create a deployment
OTA updates are rolled out to a Cohort as Deployments. To start a new update for
our device, go to the Cohort's page in the Golioth Console and click Deploy
.
Add the main
package to the deployment, and make sure it's set to version
1.2.6. You can optionally pick a name for your deployment.
Click Next
to review your deployment, then Start Deployment
to start the
update.
Observe the OTA firmware update
In the terminal window you see the ESP32 almost immediately recognizes that new firmware is available:
I (175827) golioth_basics: Sending hello! 17
I (185007) golioth_fw_update: Received OTA manifest
I (185007) golioth_fw_update: Current version = 1.2.5, Target version = 1.2.6
I (185017) golioth_fw_update: State = Downloading
I (185317) golioth_fw_update: Image size = 1211744
I (185327) golioth_fw_update: Getting block index 0 (1/1184)
I (185827) golioth_basics: Sending hello! 18
W (187867) golioth_coap_client: CoAP message retransmitted
I (187947) fw_update_esp_idf: Writing to partition subtype 17 at offset 0x1a0000
I (187947) fw_update_esp_idf: Erasing flash
I (191627) golioth_fw_update: Getting block index 1 (2/1184)
I (191837) golioth_fw_update: Getting block index 2 (3/1184)
I (192037) golioth_fw_update: Getting block index 3 (4/1184)
I (192187) golioth_fw_update: Getting block index 4 (5/1184)
I (192447) golioth_fw_update: Getting block index 5 (6/1184)
I (192597) golioth_fw_update: Getting block index 6 (7/1184)
... snip ...
I (279837) golioth_fw_update: Getting block index 1181 (1182/1184)
I (280107) golioth_fw_update: Getting block index 1182 (1183/1184)
I (280317) golioth_fw_update: Getting block index 1183 (1184/1184)
I (280457) golioth_fw_update: Total bytes written: 1211760
I (280467) esp_image: segment 0: paddr=001a0020 vaddr=3f400020 size=29df0h (171504) map
I (280527) esp_image: segment 1: paddr=001c9e18 vaddr=3ffbdb60 size=05868h ( 22632)
I (280537) esp_image: segment 2: paddr=001cf688 vaddr=40080000 size=00990h ( 2448)
I (280547) esp_image: segment 3: paddr=001d0020 vaddr=400d0020 size=d9a64h (891492) map
I (280847) esp_image: segment 4: paddr=002a9a8c vaddr=40080990 size=1e2a0h (123552)
I (280887) esp_image: segment 5: paddr=002c7d34 vaddr=50000000 size=00010h ( 16)
I (280887) golioth_fw_update: State = Downloaded
I (281127) golioth_fw_update: State = Updating
I (281327) fw_update_esp_idf: Setting boot partition
I (281337) esp_image: segment 0: paddr=001a0020 vaddr=3f400020 size=29df0h (171504) map
I (281397) esp_image: segment 1: paddr=001c9e18 vaddr=3ffbdb60 size=05868h ( 22632)
I (281417) esp_image: segment 2: paddr=001cf688 vaddr=40080000 size=00990h ( 2448)
I (281417) esp_image: segment 3: paddr=001d0020 vaddr=400d0020 size=d9a64h (891492) map
I (281717) esp_image: segment 4: paddr=002a9a8c vaddr=40080990 size=1e2a0h (123552)
I (281757) esp_image: segment 5: paddr=002c7d34 vaddr=50000000 size=00010h ( 16)
I (281827) golioth_fw_update: Rebooting into new image in 5 seconds
I (282827) golioth_fw_update: Rebooting into new image in 4 seconds
I (283827) golioth_fw_update: Rebooting into new image in 3 seconds
I (284827) golioth_fw_update: Rebooting into new image in 2 seconds
I (285827) golioth_fw_update: Rebooting into new image in 1 seconds
... snip ...
I (4267) esp_netif_handlers: sta ip: 192.168.1.159, mask: 255.255.255.0, gw: 192.168.1.1
I (4267) example_wifi: WiFi Connected. Got IP:192.168.1.159
I (4277) example_wifi: Connected to AP SSID: TheNewPeachRepublic
I (4287) golioth_mbox: Mbox created, bufsize: 2184, num_items: 20, item_size: 104
I (4287) golioth_basics_new: Waiting for connection to Golioth...
W (4297) wifi:<ba-add>idx:0 (ifx:0, c6:ff:d4:a8:fa:10), tid:0, ssn:1, winSize:64
I (4307) golioth_coap_client: Start CoAP session with host: coaps://coap.golioth.io
I (4307) libcoap: Setting PSK key
I (4317) golioth_coap_client: Entering CoAP I/O loop
I (4637) golioth_basics_new: Golioth client connected
I (4647) golioth_coap_client: Golioth CoAP client connected
I (4657) golioth_basics_new: Hello, Golioth!
I (4657) golioth_fw_update: Current firmware version: 1.2.6
I (4657) golioth_fw_update: Waiting for golioth client to connect before cancelling rollback
I (4677) golioth_fw_update: Firmware updated successfully!
I (4727) golioth_fw_update: State = Idle
I (5937) golioth_basics_new: Synchronously got my_int = 42
I (5937) golioth_basics_new: Entering endless loop
I (5937) golioth_basics_new: Sending hello! 0
- The firmware will be downloaded in blocks
- When the download is complete the ESP32 will reset automatically
- After reset, the ESP32 will connect to Golioth and confirm it is running the most recent firmware version
Look closely at the terminal output and you'll see the tag name in the log
messages have changed to golioth_basics_new
. This is because of the change we
made in the sourcecode before compiling the new firmware.
Confirm the new firmware version on the Golioth Console
After an OTA update, devices report their firmware version to Golioth.
Here you can see that the ESP32 is running the new version 1.2.6 firmware!