Firmware versioning is a crucial part of ensuring the binary you’re about to load onto your device is the correct arrangement of bits that will control the hardware how you want. It is fundamental to a system like Golioth’s Over-The-Air update service. Today we’re going to look at different ways that you can tell which version you’re running in Zephyr and how your firmware update is correct the first time and every time. We’ll use Golioth Reference Designs as an approximation of what a product design would do for the devices in their fleet.
How we generate firmware versions
Around these parts, we adhere to Semantic Versioning, or SemVer. It’s a useful way to assign incremental changes to a firmware image. It also matches how Zephyr increments versions, and how the Golioth SDK is versioned. So…it’s a good idea to have any binaries we create for our Reference Designs to also have a SemVer attached. As you can see in the video, we also able to detect versions and show interesting elements when the images adhere to SemVer. We expect our users to follow a similar format (especially if using MCUboot), though Golioth is flexible across different version methods.
For Reference Designs, the firmware version is reported through the bootloader; MCUboot in the case of our Zephyr Designs. That means when the image is being loaded by MCUboot, it is reading the version embedded in the image.
Previously, we needed to do this manually. At some point in the generation of a firmware binary, the firmware engineer needs to decide this is the version that will be released as 1.2.3 (or whatever number is next in your internal process). That’s where things can get tricky, because humans are often in the loop. Perhaps we have a commit in Git like 1a2b3c4d
and we want to attach the version 1.2.3
to it. Well, we would do that on the command line… a place I notoriously mess things up! See more in the last section (historical footnote
) for more info.
We changed how we do things in Reference Designs starting in v2.4.0 of the RD Template. We now use a controlled VERSION file, following the guidance of the Zephyr Application Version Management system. This has been working out great. We store the file in the repository and it gets incremented (and tracked!) like any other file.
Where can I see it?
OK, so we have the version number embedded into the binary, but how do I actually see it? We can do this in a couple different places: the log output, the MCUboot shell, all the Golioth console.
Application load and boot
When built on top of nRF Connect SDK and Zephyr, you can see logging output for the version in our main application code. This application also shows the firmware version of the modem and the attached Ostentus display firmware (when present).
The MCUboot shell
This shell is not enabled by default on the Reference Design Template, so I turned it on by adding CONFIG_MCUBOOT_SHELL=y
to prj.conf in my file. This gives me a new shell menu that shows my regions. Super useful when trying to troubleshoot OTA issues.
On Golioth’s Devices -> Firmware Tab
Part of the OTA code in the Golioth SDK is that it reports back the current version and the current state of OTA, so you can see where you are in the midst of an update. This is a great place to see the last reported version. The same information is available on the Summary tab of the same page.
On Golioth’s Firmware Updates -> Cohorts Tab
This shows the history of packages, which will include the one currently running
On Golioth’s Firmware Updates -> Packages Tab
This is where you upload new images to be included in future deployments and where Golioth extracts the MCUboot version and uses it as another check against existing images on the server. We also point out when you’re trying to upload a similar image you have uploaded before.
Keeping your deployments organized
Attaching a SemVer to your binary won’t solve all your problems, but it will help you organize the range of firmware images you’re sending out to your devices. Golioth helps to check your versions on the cloud and seamlessly deliver the package to eligible devices. If you have questions about how to build or deploy your next firmware image, let us know in the forum!
Historical Footnote
When we used to have to call out the image version on the command line, it was a mess. I recall a scenario where I made a change to my firmware and re-used the last build command which also happened to have the version number attached. But that’s because we’re not actually using a controlled document to set the version. Sure, I might have a tag in GitHub that says 1.2.3
, but there’s no mandate for the firmware to adhere to that so I needed to do that manually before. Note that there is a --
which escapes the west build command and then the next DCONFIG
command, which actually sent the version to CMake to include in the build.
west build -p -b nrf9160dk_nrf9160_ns samples/dfu -- -DCONFIG_MCUBOOT_IMAGE_VERSION=\"1.2.3\"
Note: the above code will not work for modern versions of Zephyr
Start the discussion at forum.golioth.io