Integrating SWUpdate for OTA Updates on Siemens IOT2000 via Yocto

David Krause10 min read
Other TopicSiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

Over-the-air (OTA) update capability is a core requirement for any industrial IoT gateway that is deployed in the field, where physical access is restricted, expensive, or impossible. The Siemens SIMATIC IOT2000 family (IOT2020, IOT2040) is shipped with a Yocto-based Linux image, and integrating an OTA update mechanism into that image requires choosing a client-side update agent and a compatible backend server.

Two open-source OTA stacks dominate the embedded Linux landscape: SWUpdate (a neutral, format-driven update agent maintained under the SWUpdate project) and Mender.io (an end-to-end client/server solution with a strong focus on atomic, dual-A/B partition updates). For the IOT2000, the path of least resistance is SWUpdate integrated through the siemens/meta-iot2000 Yocto BSP layer, because the layer already aligns the kernel, U-Boot, and Intel Quark/x86 SoC quirks to the SWUpdate delivery model.

This reference walks through the architecture, the meta-layer changes required, the SWUpdate image ("swu") format, backend interop (Eclipse HawkBit), and the verification steps you need to commission an OTA-capable IOT2000 image built from Yocto.

Why SWUpdate on the IOT2000

SWUpdate is a device-side update agent; it is intentionally agnostic to whatever backend issues the update payload. That decoupling is the reason it has become a de-facto standard in the embedded Yocto ecosystem:

  • It consumes a single signed artifact, the .swu file, regardless of whether the artifact is fetched from a local filesystem, an HTTP(S) server, an MQTT broker, or an OTA director.
  • It is not coupled to a specific storage layout: it can update raw MTD, UBI volumes, block devices, or a combination (a "compound" image).
  • It supports hardware watchdog feeding so a failed upgrade resets the device instead of leaving it half-upgraded.
  • It plugs cleanly into the Yocto build system as meta-swupdate, keeping the IOT2000 image reproducible.

Mender.io was considered as an alternative during the IOT2000 evaluation. The decision went to SWUpdate because of the existing meta-layer integration, the smaller footprint, and the willingness of the meta-iot2000 maintainers to host the device-side glue inside the official Siemens repository rather than as a third-party overlay.

Architecture and Data Flow

The OTA flow on an IOT2000 node has four logical actors:

  1. Backend (OTA director). Eclipse HawkBit, a simple HTTP server, or Mender's tenant server. The backend does not need to know about the IOT2000 specifically; it must only be able to hand a .swu payload to the device when the device polls.
  2. Update client (SWUpdate). Runs on the IOT2000, periodically polls the backend (or is triggered by a webhook), downloads the .swu, verifies its signature, and applies it.
  3. Bootloader (U-Boot).
  4. Storage target. The eMMC, SD card, or a combination. SWUpdate decides which MTD/block devices receive which stream based on the image manifest.

Backend (HawkBit / Mender) SWUpdate client U-Boot eMMC / SD target .swu fetch boot attempt write streams

Prerequisites

Build environment and host tooling:

  • A 64-bit Linux build host (Ubuntu 20.04 LTS or 22.04 LTS are the safest choices for the IOT2000 BSP).
  • git, repo is not required - git alone is enough.
  • At least 50 GB of free disk space for the Yocto sstate-cache and tmp directories.
  • At least 8 GB of RAM; 16 GB is recommended when building Chromium or Node.js components.
  • Yocto baseline compatible with the IOT2000 BSP: the meta-iot2000 release branches track a specific Yocto release. The PR that landed SWUpdate on the device rebases onto the almost-finished upgrade to Yocto 2.6 (Morty-ish / Pyro era kernels); pin your local environment to the matching release tag.

Source trees (clone at the same path level):

mkdir iot2000-ota && cd iot2000-ota
git clone -b <release-tag> git://git.yoctoproject.org/poky.git
git clone -b <release-tag> git://git.openembedded.org/meta-openembedded.git
git clone -b <release-tag> git://git.openembedded.org/meta-intel.git
git clone -b <release-tag> git://github.com/siemens/meta-iot2000.git
git clone -b <release-tag> git://github.com/sbabic/meta-swupdate.git

Inside meta-iot2000, the relevant integration is tracked in pull request #75 ("Iot2000 ebg swu"). That PR is the device-side foundation that lets SWUpdate work against any backend that exposes an SWUpdate-compatible plug-in, Eclipse HawkBit being the canonical example.

Note: The PR was at one point waiting on review while discussions around certain layout details (bootloader fallback, redundant environment blocks) were being closed out. Treat the PR as the canonical patch set, but check the master branch of meta-iot2000 - the SWUpdate hooks eventually merged into master.

Adding SWUpdate to the IOT2000 Image

Edit conf/bblayers.conf and append the two layers:

BBLAYERS ?= " \
  /path/to/poky/meta \
  /path/to/poky/meta-poky \
  /path/to/meta-openembedded/meta-oe \
  /path/to/meta-openembedded/meta-python \
  /path/to/meta-intel \
  /path/to/meta-iot2000 \
  /path/to/meta-swupdate \
"

Edit conf/local.conf and select the IOT2000 machine plus the SWUpdate image features:

MACHINE = "iot2040"

# SWUpdate core and helpers
IMAGE_INSTALL_append = " swupdate swupdate-www swupdate-tools lua"
IMAGE_INSTALL_append = " swupdate-handler-progress"

# HawkBit client is the default backend in meta-swupdate
IMAGE_INSTALL_append = " swupdate-hawkbit"

# mtd/ubifs helpers used by the IOT2000 eMMC layout
IMAGE_INSTALL_append = " mtd-utils"

The PR also adds a swu-capable wic image recipe. Reference it from your custom image or build it directly:

bitbake core-image-minimal-dev
bitbake iot2040-image-swupdate   # produced by meta-iot2000 PR #75

SWU Image Format and the IOT2000 Manifest

A .swu file is a CPIO archive of the form:

sw-description
<handler-script>
<image-stream-1>
<image-stream-2>
...

The sw-description is a JSON or libconfig file that SWUpdate parses to know what to install. A minimal example for an IOT2000 updating the rootfs and a data partition on eMMC looks like:

software =
{
    version = "1.2.3";
    hardware-compatibility = [ "1.0" ];

    iot2000 = {
        stable = {
            copy1 = {
                images = (
                    {
                        filename = "rootfs.ext4.gz";
                        type = "raw";
                        device = "/dev/mmcblk0p3";
                        compressed = "zlib";
                    },
                    {
                        filename = "data.ext4.gz";
                        type = "raw";
                        device = "/dev/mmcblk0p4";
                        compressed = "zlib";
                    }
                );

                scripts = (
                    { filename = "update.sh"; type = "shellscript"; }
                );

                bootenv = (
                    { name = "upgrade_available"; value = "1"; },
                    { name = "bootcount"; value = "0"; }
                );
            };
        };
    };
};

The hardware-compatibility field is the safety net: a node will refuse to apply an artifact whose compatibility set does not contain the board's own revision, so a payload built for an IOT2020 will not accidentally flash an IOT2040.

update.sh is executed before the streams are written; typical use is to stop services, unmount filesystems, and (for dual-scheme updates) flip the active partition variable in U-Boot's environment:

#!/bin/sh
systemctl stop node-red 2>/dev/null
systemctl stop example-app 2>/dev/null
umount /mnt/data 2>/dev/null
exit 0

Backend Selection: HawkBit vs. Mender

Criterion Eclipse HawkBit + SWUpdate Mender.io
Client on IOT2000 swupdate-hawkbit (Lua module) mender-client (Go binary)
Server Spring Boot, Docker image, or sandbox Mender Open Source / Enterprise
Update atom Arbitrary streams in one .swu Single rootfs image, dual A/B
Storage layout Flexible; raw, UBI, MTD, compound Requires A/B or persistent data partition
Footprint ~1.5 MB static binary + Lua ~10 MB Go binary
Artifact signing RSA / CMS / Verity Mender-signed rootfs
meta-iot2000 status PR #75 / merged in master Not part of the BSP; third-party layer required
Best fit Custom images, heterogeneous partitions, signed updates Rootfs-only rollouts, A/B safety

If you only need to roll a new rootfs and you want a polished UI for the rollout fleet, Mender is a strong default. If you need to update a U-Boot image, a configuration partition, and the rootfs atomically in one transaction, SWUpdate is the more flexible choice - and it is the choice that ships with meta-iot2000.

Configuring SWUpdate at Runtime

SWUpdate reads /etc/swupdate/swupdate.cfg. The minimal HawkBit block looks like:

[socket]
enable = true
path = /var/run/swupdate.sock

[hawkbit]
url = https://hawkbit.example.com:8443
tenant = default
deviceId = iot2040-${HOSTNAME}
auth = TOKEN

[log]
level = 3
output = /var/log/swupdate.log

For a manual update - useful during commissioning and for reproducing field issues - trigger SWUpdate over the local socket:

swupdate -i /tmp/iot2040-1.2.3.swu -e stable,copy1

For unattended updates, the suricatta daemon polls the backend:

[suricatta]
hawkbit = true

Verification

After a successful build, before commissioning a node, validate the image and the update path on the bench.

Build-time checks

  1. Confirm the IOT2000 machine matched the artifact:
    cat tmp/deploy/images/iot2040/sw-description | grep hardware-compatibility
  2. Confirm signing keys are embedded:
    swupdate -K /etc/swupdate/public.pem -i image.swu
  3. Confirm the .swu lists every declared stream:
    cpio -idv < image.swu

Boot-time checks

  1. Boot the freshly built image, log in over serial, and confirm swupdate -h runs.
  2. Check the watchdog hook is active:
    systemctl status swupdate-watchdog
  3. Verify the HawkBit connection from the device:
    curl -k https://hawkbit.example.com:8443/DEFAULT/controller/v1/<deviceId>

End-to-end OTA rehearsal

  1. Upload a known-good .swu to the HawkBit rollout view.
  2. Assign it to the test IOT2040.
  3. Watch /var/log/swupdate.log; a successful run ends with SWUPDATE successful and a reboot triggered by U-Boot's upgrade_available=1 env.
  4. After the reboot, confirm /etc/os-release reflects the new version.

Troubleshooting Matrix

Symptom Likely root cause Action
SWUpdate refuses to start: cannot open socket Conflicting /var/run/swupdate.sock from a previous instance Remove the stale socket and restart swupdate.service
Device polls HawkBit but no rollout is offered hardware-compatibility mismatch Re-tag the rollout with the correct hardware-compatibility value
Update applies, but the device does not boot the new image U-Boot environment not written; upgrade_available not set Check the bootenv block in sw-description; verify the U-Boot env partition is mounted RW
Reboot loop after update bootcount never decremented; U-Boot is rolling back Confirm update.sh resets bootcount=0 and that upgrade_available is cleared on a successful boot
signature verification failed Public key on device does not match the key that signed the artifact Re-bake the image with the matching SWUPDATE_SIGNING_KEY
OTA stalls at 50% on large rootfs Insufficient free space in the staging area Use streaming handlers (raw + zlib) and ensure the target partition is at least 1.2x the compressed image size
No log output after the first reboot /var/log is on a tmpfs that was not preserved Persist /var/log to the data partition or stream logs to a remote syslog

Field-Proven Caveats

  • Pull-request lag. PR #75 on meta-iot2000 was awaiting review and rebase work for a noticeable period while Yocto 2.6 alignment was finalized. If you are pinning to a tagged release, check whether the SWUpdate hooks are present in that tag or only in master.
  • Re-spin cadence. A new official IOT2000 image with the SWUpdate build wired in was expected to follow the prior image release, but cadence was tied to maintainer availability. Treat the next official image as a milestone to track, not a hard deadline.
  • Backend choice is not final. The PR intentionally does not bind the device side to a single backend. The same swupdate binary can be re-pointed at a plain HTTP server, MQTT, or HawkBit by swapping the suricatta module - useful when you outgrow a PoC.
  • Compiler and Python toolchain. Building SWUpdate on Yocto 2.6 era layers needs python3-pyyaml and a recent openssl; meta-swupdate's conf/layer.conf declares both, but you will hit cryptic failures if meta-openembedded is missing or pinned to a branch that does not match the meta-swupdate release.

Next Steps

Once a single IOT2040 successfully pulls and applies an artifact, scale the verification across a small fleet before promoting the image. Recommended commissioning order:

  1. One lab IOT2040 with a controlled HawkBit tenant.
  2. Three to five field nodes with a canary rollout policy in HawkBit.
  3. Full fleet, signed artifacts only, hardware-compatibility tagging enforced at upload time.

After that, the meta-iot2000 image is OTA-capable end to end, with no need for a custom update framework per device class.

FAQ

Does meta-iot2000 ship with SWUpdate enabled by default?

No. The SWUpdate hooks were contributed through pull request #75 and are expected to land in the next official image release. Until then, the layer must be added manually and the SWUpdate image recipe enabled.

Can I use Mender.io instead of SWUpdate on the IOT2000?

Yes, but Mender is not part of the official meta-iot2000 layer. You would add a third-party Mender layer, build mender-client into the image, and reserve an A/B partition layout. For a single-artifact, multi-stream update model the meta-iot2000 path with SWUpdate is leaner.

What is the difference between a HawkBit backend and a plain HTTP server for SWUpdate?

A plain HTTP server is enough for a one-shot, manually triggered update. HawkBit adds polling, rollouts, target filters, action history, and statistics. For anything beyond a handful of devices, deploy HawkBit.

How do I prevent an .swu built for an IOT2020 from being installed on an IOT2040?

Use the hardware-compatibility field in sw-description. Set it to the board revision (e.g. 1.0 for IOT2040). The device will refuse any artifact whose compatibility set does not include its own revision.

Why did the integration target Yocto 2.6 specifically?

Yocto 2.6 was the active branch when PR #75 was being rebased. The maintainer waited to rebase the SWUpdate patches onto the finished 2.6 update so the integration would not have to be redone on the next Yocto jump.

Back to blog