How to Use Subsystem Updates
Introduction
This article aims to bring information on how to use Subsystem Updates. This Torizon Cloud feature enables secure firmware updates to secondary systems (subsystems) connected to the primary system (Toradex System on Modules - SoMs).
Prerequisites
- Basic understanding of subsystem updates. Refer to Overview of Subsystem Updates article.
- Follow the steps described in First Steps With Subsystem Updates article.
- Basic understanding of Torizon Cloud features.
- Secondary device (subsystem) of your choice.
- A Toradex System on Module with Torizon OS installed.
Use Case: Subsystem Update in an Ai-Thinker ESP8266 12-E NodeMCU
This is just one example out of all possible use cases enabled by Subsystem Updates. Visit the Action Handler repository to learn about others.
This section brings the use case of updating the firmware of a subsystem (Ai-Thinker ESP8266 12-E NodeMCU) connected via serial to a Toradex Verdin iMX8M Plus module. The project has the objective of simply controlling an LED blinking. The following image shows the setup of a Dahlia Carrier Board with the ESP8266 12-E NodeMCU and the Verdin iMX8M Plus.

Enable the ESP8266 12-E NodeMCU
After you configure Aktualizr as described in First Steps With Subsystem Updates, the secondaries.json file declares the ESP8266 as a subsystem:
{
"docker-compose": [
{
"partial_verifying": false,
"ecu_hardware_id": "docker-compose",
"full_client_dir": "/var/sota/storage/docker-compose",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/docker-compose/docker-compose.yml",
"target_name_path": "/var/sota/storage/docker-compose/target_name",
"metadata_path": "/var/sota/storage/docker-compose/metadata"
}
],
"torizon-generic": [
{
"partial_verifying": false,
"ecu_hardware_id": "verdin-imx8mp-bootloader",
"full_client_dir": "/var/sota/storage/bootloader",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/bootloader/u-boot.img",
"target_name_path": "/var/sota/storage/bootloader/target_name",
"metadata_path": "/var/sota/storage/bootloader/metadata",
"action_handler_path": "/usr/bin/bl_actions.sh"
},
{
"partial_verifying": false,
"ecu_hardware_id": "esp_firmware",
"full_client_dir": "/var/sota/storage/esp_firmware",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/esp_firmware/firmware.bin",
"target_name_path": "/var/sota/storage/esp_firmware/target_name",
"metadata_path": "/var/sota/storage/esp_firmware/metadata",
"action_handler_path": "/home/torizon/esp_firmware.sh"
}
]
}
For the meaning of each key, see Configuring Aktualizr in the Subsystem Updates - Detailed Manual article.
Configure the Firmware Update Instructions
The action handler is where the update happens. Its install action holds the commands that deliver the new firmware to the subsystem, and those commands can run a container, as this use case does. See Writing the Action Handler for the generic version of this file, and Understanding the Action Handler in the Subsystem Updates - Detailed Manual article for the interface details.
Before creating the action handler file (esp_firmware.sh), create a script (update_script.sh) in the host machine with the mechanisms that provide the firmware to be updated.
#!/bin/sh
echo "Updating external firmware ..."
sleep 1
# Set the boot configuration, ensure that the ESP8266 is in firmware flashing mode (flash)
gpioset gpiochip0 0=1
gpioset gpiochip0 0=0
sleep 0.2
# Reset the ESP8266
gpioset gpiochip0 1=0
sleep 0.2
gpioset gpiochip0 1=1
sleep 0.2
# Load new firmware (flash)
python3 ./esptool.py --port /dev/ttymxc0 write_flash -fm dio 0x00000 /esptool/firmware.bin
sleep 1
# Set the normal boot configuration again and restart
gpioset gpiochip0 0=1
sleep 0.2
gpioset gpiochip0 1=0
sleep 0.2
gpioset gpiochip0 1=1
sleep 1
echo "Sucess!"
This file is responsible for setting the ESP8266 into firmware flash mode, loading it through the ESPtool (recommended by manufacturers for this purpose), and restarting the device with the new firmware.
Then, in the host terminal, the execution permission is added to the script:
$ chmod +x update_script.sh
To enable this update to occur automatically, configure a container to perform the steps of the update_script.sh file. Thus, the following Dockerfile should be created in the host machine in the same directory as update_script.sh, configuring the container environment and including all the necessary dependencies for the ESPtool:
FROM torizon/debian:4
RUN usermod -a -G gpio torizon
#Install applications to be used within the container
RUN apt update && apt install -y --no-install-recommends \
git python3 python3-pip python3-serial gpiod libgpiod2 && \
apt-get clean && apt-get autoremove && \
rm -rf /var/lib/apt/lists/*
#Clone the ESPTool tool
RUN git clone https://github.com/espressif/esptool.git /esptool
#Set our working directory
WORKDIR /esptool
#Copy our update file into the container and allow it to be executed
COPY update_script.sh /esptool
RUN chmod +x /esptool/update_script.sh
#Run our update script
CMD [ "/esptool/update_script.sh" ]
To build the container in the host machine, enable the Arm emulation and use the flag --platform linux/arm64/v8 in the docker build command.
Finally, after building the container and pushing it to the DockerHub, the esp_firmware.sh is created:
#!/usr/bin/bash
# ---
# Main program
# ---
case "$1" in
get-firmware-info)
# Perform normal processing for this action.
echo "ACTION HANDLER: get-firmware-info"
exit 64
;;
install)
# Perform normal processing for this action.
docker run --rm -v /dev:/dev -v $SECONDARY_FIRMWARE_PATH:/esptool/firmware.bin \
--device-cgroup-rule='c 207:16 rmw' --device-cgroup-rule='c 254:* rmw' \
<docker_user_name>/esp_flasher:arm64 > /var/logs
echo '{"status": "ok", "message": "Container executed"}'
exit 0
;;
complete-install)
# Perform normal processing for this action.
echo "ACTION HANDLER: complete-install"
exit 64
;;
*)
# Perform normal processing for this action.
echo "ACTION HANDLER: ERROR event $1 not supported"
exit 64
;;
esac
Be aware to change <docker_user_name> with the proper DockerHub user name. Also, check if the name and flag (esp_flasher:arm64) are the same as defined during the build.
After all these processes, the firmware update mechanisms are ready to be used in the Torizon Cloud.
Update in Torizon Cloud
After logging in to the Torizon Cloud, one should follow the steps to proceed with the subsystem update. In the case of not having an account, sign up for free, or one of the plans. Check the plans for more details.
Provision the SoM
The first step is to provision the System on Module on Torizon Cloud by clicking on Provision Device in the left column of the dashboard. A new tab opens on the screen. On this tab, the Docker command provided intends to be pasted in the terminal logged in the SoM. This command carries out the provisioning of the device and prepares it to show its metrics on the Torizon Cloud platform. After these steps, the device is listed as a provisioned device. The video following brings the steps.
Add the Firmware Package
To add the new firmware, use the middle column on the platform, select Add new package, and then choose the Other option. The firmware used may vary depending on the secondary device. Some changes can be made according to preference, such as the name, and version of the files.
Update the Firmware
To initiate an update for the device, click the start arrow in the Dashboard Recent Devices column. Then, select the secondary device to be updated, and choose the package to be installed on it. The previously loaded packages in the Torizon Cloud system appear after selecting the My Uploads box. Choose the package version, then confirm the update.
For more information about the update status, select the More info option in the Update Status section. All the steps from the download to the installation of the new package are displayed.