Device Updates: Cut Down Time and Data Usage

Is it time to modernize your approach to device updates?
Have you ever suspected that the way updates are handled on your embedded devices wastes electricity, drives up customer cellular data bills, or takes longer than really needed? Let’s look at how device updates have traditionally been done, and some improvements that could be made.
The state of things
Some high-end embedded devices have an operating system, scripting run-time, file system, or package manager that allows updating individual parts of its software. Most embedded devices don’t have these features, and even when they do, they may not be well suited for a use case that places high value on predictability and reliability.
Updates to a device in the field are traditionally handled by writing a new binary image to a fixed memory location. This is a natural extension of how the device memory is initially programmed during manufacturing. If the update interface is fast and cheap to use for device updates (e.g. WiFi), this approach may not be a problem.
However, if the update interface is slow or requires someone to physically connect and disconnect the update interface, sending large images becomes impractical. If updates are done over a cellular connection, replacing the entire image to make a small change on many devices costs end users far more than is necessary. Let’s look at what we can do to minimize the amount of data that needs to be sent to update a device.
A basic robust update scheme
Before we discuss techniques for minimizing the size of the update for any given code change, let’s quickly summarize a couple of basic options for performing a reliable and secure device update.
Option 1: A/B Seamless Updates
One approach is to have enough memory to hold redundant copies of the device image.

Procedure:
- Remote updater informs the device that an update is coming, and provides some meta data (image size, version number, checksum, signature)
- Device receives the image in chunks and writes them to the memory reserved for the inactive image
- Device logs the image transfer progress, in case the update is interrupted and needs to be resumed
- When the image transfer has completed, the device can be rebooted
- Bootloader runs the newest image that passes an integrity check (using the checksum) and authenticates that it was created by a trusted source (using the signature)
Option 2: Intelligent Bootloader Updates
If memory is limited, and taking the application offline while receiving the update is not a problem, a bootloader can also be used to perform the update. In this case, the bootloader must be able to communicate with the remote updater.

The procedure is nearly identical to the A/B Seamless Updates, but the device jumps back into the bootloader to handle the update and will wait for an update until a valid image is available to run.
For both of these options, chunks of the image may be sent to the device with a command like this:

Improve the update scheme
Don’t resend
Major portions of the image may not change from one version to the other, so we can skip sending that data, and simply copy what we already have on the device.
Perform a DIFF to find sufficiently large matching segments of the new binary that exist in the old binary. Any non-matching segments in the new image must still be transferred with the Chunk command from the last section. Any matching segments can be instructed to be copied with a command like this:

Copying from the old image to the new image should be straightforward for A/B Seamless Updates.
If there is only one memory region available for an image, it is possible that some of the old data could be lost during the update, so take extra care to ensure your remote updater does not instruct it to copy something that isn’t there.
Skip the filler
Rather than sending every byte of a repeating sequence of data, use short-hand. Use this command as a crude form of data compression on any repeated data that is smaller when expressed this way:

Avoid Transfer Overhead
To minimize the overall data sent over the data link, try to find a sweet spot for minimizing dropped frames, while also avoiding the overhead of frames that are too small.
The Chunk, DIFF, and Fill commands can be aggregated into larger messages, to avoid sending a lot of small frames.
Avoid unnecessary changes
Have you ever made a small change and an unexpectedly large number of changes appeared in the binary image? Have you ever generated binary images from the same commit, but got different results on different machines, or even different results on the same machine at different times?
If you answered yes, there are some things you can do to get more deterministic builds. We’ll use gcc as our example tool.
File paths: Avoid including file paths in your release build, by excluding macros like __FILE__
. If this isn’t acceptable, you can set up your build to enforce the release build to be run from a certain directory, or modify environment variables to make the present working directory a constant on any machine
Time stamps: Avoid embedding timestamps in your release build, by excluding macros like __TIMESTAMP__
, __TIME__
, and __DATE__.
Random seed values: Avoid randomly changing seed values. For each file that uses a random seed value, you can create another file with a predefined seed value, and compile the file with the frandom-seed=<input-file-name>
option.
Existing Solutions
Whenever possible, try to use a proven, off-the-shelf solution that is efficient, reliable, and secure. Here are a few offerings:
FreeRTOS
Embedded Linux
Note that there are many more options than what is on this list!
If you do decide to roll your own solution, see Memfault’s article on creating a reliable device firmware update solution.
Summary
To save time, energy, or cellular data costs, you can use the tips above to reduce the size of your embedded device updates. DojoFive brings modern tools, techniques, and best practices from the web and mobile development environments, paired with leading-edge innovations in firmware to our customers to help them build successful products and successful clients. We have talented engineers on hand ready to help you with all aspects of your EmbedOps journey. Bring your interesting problems that need solving – we are always happy to help out. You can reach out at any time on LinkedIn or through email!