Firmware Updates

Firmware updates are an important aspect to all parts of the device lifecycle, not just production. In development, they can save you time not burning SD cards by using mechanisms like pushing firmware bundles over the network. Network updates can also pave the way to automate executing canary tests using real hardware. Firmware updates are important to these stages because they are the only way to modify the contents of the read only filesystem.

What is a Firmware Bundle?

Firmware bundles are created using fwup. In Nerves, Firmware bundles are .fw archive files that are constructed as the output of calls to mix firmware. You can think of these bundles as instructions attached to a payload. In modern Nerves-based Mix projects, you’ll find these bundles at _build/#{Mix.target}/#{Mix.Env}/nerves/images. For example, if we were in a project called my_app for Raspberry Pi Zero and running in dev, _build/rpi0/dev/nerves/images/my_app.fw.

The instructions contained in the bundle are declared in the fwup.conf included in the Nerves system. These instructions contain tasks like complete and upgrade.

When the complete task is applied, the destination device is written fresh, like an initial install, producing a layout of 2 slots for firmware on the device. This A/B layout provides some safeguards by retaining a known-good working firmware. For example, when an upgrade task is applied, the new firmware is written to the inactive slot on the device and made active on next the next boot.

How Do I Apply Them?

To apply a firmware bundle, you need fwup installed, the destination device (ex: /dev/mmcblk0), and a task to execute from the fwup.conf (ex: upgrade). Fwup is already included in all Nerves systems. You can interact with it from a console session running on the device.

iex> :os.cmd('fwup --version')
'0.13.0\n'

Applying a firmware bundle is a matter of passing the arguments into the fwup command. Here is an example of how to apply a firmware bundle to /dev/mmcblk0 using the upgrade task.

fwup -aU -i /path/to/my_app.fw -d /dev/mmcblk0 -t upgrade

Using this technique, we can perform these tasks in a number of ways.

SD Card

As a matter of fact, this is the way that nerves firmware.burn writes firmware to your SD card, by interacting with fwup running on your host. The firmware.burn mix task will use the complete task by default, but you could insert an SD card with a prior firmware and pass --task upgrade to upgrade the firmware instead.

Over the Network

You can stream the .fw files over the network to a device by using the nerves_firmware_http package. Simply add this package to your target dependencies, then use mix firmware.push to push to a device. You can specify a .fw file directly, or let mix figure it our for you based on your target.

mix firmware.push 192.168.1.100 --target rpi0

Whats New?

The v0.4.0 release of nerves_firmware_http and nerves_firmware contain several bug fixes and improvements for the network update process.

One of the big improvements with this version is that we can now stream the firmware bundle to the inactive firmware slot in chunks while it’s coming across the network. This saves space and resources instead of writing it to a temporary file, or storing it in large chunks in memory. This means stable support for devices with more limited resources like the Lego EV3 and LinkIt Smart. It also means slightly faster upgrades.

What about Nerves Reactor?

Nerves Reactor and Bootloader are still on their way, and this update to network based firmware updates is a step towards their release. Nerves Reactor will give you fast iteration in the development of your Elixir code and priv files, but it won’t directly handle changes to the Nerves system configuration such as programs in /usr/bin. Those are handled by firmware upgrades.

comments powered by Disqus