Mender MCU client is an open source over-the-air (OTA) library updater for MCU devices. This demonstration project runs on STM32L4 hardware using Zephyr RTOS.
This project is used with a NUCLEO-L4A6ZG evaluation board and an Ethernet W5500 module available low cost at several places. The device tree overlay nucleo_l4a6zg_firmware.overlay
specifies the wiring of the module, as illustrated on the following image, and it is possible to adapt it to your own hardware.
The project is built using Zephyr RTOS v3.7.x (v3.7-branch) and Zephyr SDK >= v0.16.0. It depends on cJSON. There is no other dependencies.
To start using Mender, we recommend that you begin with the Getting started section in the Mender documentation.
To start using Zephyr, we recommend that you begin with the Getting started section in the Zephyr documentation. It is highly recommended to be familiar with Zephyr environment and tools to use this example.
Clone the project and retrieve submodules using git submodule update --init --recursive
.
Then open a new Zephyr virtual environment terminal.
The example application should first be configured to set at least:
CONFIG_MENDER_SERVER_TENANT_TOKEN
to set the Tenant Token of your account on "https://hosted.mender.io" server.
You may want to customize few interesting settings:
CONFIG_MENDER_SERVER_HOST
if using your own Mender server instance. Tenant Token is not required in this case.CONFIG_MENDER_CLIENT_AUTHENTICATION_POLL_INTERVAL
is the interval to retry authentication on the mender server.CONFIG_MENDER_CLIENT_UPDATE_POLL_INTERVAL
is the interval to check for new deployments.CONFIG_MENDER_CLIENT_INVENTORY_REFRESH_INTERVAL
is the interval to publish inventory data.CONFIG_MENDER_CLIENT_CONFIGURE_REFRESH_INTERVAL
is the interval to refresh device configuration.
Other settings are available in the Kconfig. You can also refer to the mender-mcu-client API and configuration keys.
Particularly, it is possible to activate the Device Troubleshoot add-on that will permit to display the Zephyr console of the device directly on the Mender interface as shown on the following screenshot. File Transfer feature can be activated too. A littlefs partition is used to upload/download files to/from the Mender server.
In order to get the Device Troubleshoot add-on working, the following configuration keys must be defined in the prj.conf
file:
CONFIG_HEAP_MEM_POOL_SIZE=1500
CONFIG_ZVFS_OPEN_MAX=16
CONFIG_SHELL_BACKEND_SERIAL=n
CONFIG_SHELL_AUTOSTART=n
CONFIG_SHELL_STACK_SIZE=3072
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
The application relies on mcuboot and requires to build a signed binary file to be flashed on the evaluation board.
Use the following commands to build and flash mcuboot (please adapt the paths to your own installation):
west build -s $HOME/zephyrproject/bootloader/mcuboot/boot/zephyr -d build-mcuboot -b nucleo_l4a6zg -- -DDTC_OVERLAY_FILE=path/to/mender-stm32l4a6-zephyr-example/nucleo_l4a6zg_mcuboot.overlay -DCONFIG_BOOT_SWAP_USING_SCRATCH=y -DCONFIG_BOOT_MAX_IMG_SECTORS=256
west flash -d build-mcuboot
Use the following command to build, sign and flash the application (please adapt the paths to your own installation):
west build -b nucleo_l4a6zg path/to/mender-stm32l4a6-zephyr-example
west flash --hex-file build/zephyr/zephyr.signed.hex
After flashing the application on the NUCLEO-L4A6ZG evaluation board and displaying logs, you should be able to see the following:
*** Booting MCUboot v2.1.0-rc1-36-gfb2cf0ec3da3 ***
*** Using Zephyr OS build v3.7.0 ***
I: Starting bootloader
I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Boot source: primary slot
I: Image index: 0, Swap type: none
I: Bootloader chainload address offset: 0xe000
I: Jumping to the first image slot
[00:00:02.016,000] <inf> eth_w5500: eth_w5500@0: Link up
[00:00:02.033,000] <inf> mender_stm32l4a6_zephyr_example: IPv4 address: 192.168.1.216
[00:00:02.044,000] <inf> mender_stm32l4a6_zephyr_example: Lease time: 43200 seconds
[00:00:02.054,000] <inf> mender_stm32l4a6_zephyr_example: Subnet: 255.255.255.0
[00:00:02.064,000] <inf> mender_stm32l4a6_zephyr_example: Router: 192.168.1.1
[00:00:02.074,000] <inf> mender_stm32l4a6_zephyr_example: MAC address of the device '00:08:dc:01:02:03'
[00:00:02.085,000] <inf> mender_stm32l4a6_zephyr_example: Running project 'mender-stm32l4a6-zephyr-example' version '0.1.0'
[00:00:02.104,000] <inf> mender_stm32l4a6_zephyr_example: Mender client initialized
[00:00:02.114,000] <inf> mender_stm32l4a6_zephyr_example: Mender client registered hello-world module
[00:00:02.126,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/platform/storage/zephyr/nvs/src/mender-storage.c (242): Device configuration not available
[00:00:02.144,000] <inf> mender_stm32l4a6_zephyr_example: Mender configure add-on registered
[00:00:02.155,000] <inf> mender_stm32l4a6_zephyr_example: Mender inventory add-on registered
[00:00:02.166,000] <inf> mender_stm32l4a6_zephyr_example: Device configuration retrieved
[00:00:02.177,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/platform/storage/zephyr/nvs/src/mender-storage.c (110): Authentication keys are not available
[00:00:02.195,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/platform/tls/generic/mbedtls/src/mender-tls.c (126): Generating authentication keys...
[00:04:54.037,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/platform/storage/zephyr/nvs/src/mender-storage.c (182): Deployment data not available
[00:04:54.054,000] <inf> mender_stm32l4a6_zephyr_example: Mender client connect network
[00:05:04.953,000] <err> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-api.c (900): [401] Unauthorized: Unauthorized
[00:05:04.969,000] <inf> mender_stm32l4a6_zephyr_example: Mender client authentication failed
[00:05:04.980,000] <inf> mender_stm32l4a6_zephyr_example: Mender client released network
Which means you now have generated authentication keys on the device. Generating is a bit long but authentication keys are stored in storage_partition
of the MCU so it's done only the first time the device is flashed. You now have to accept your device on the mender interface. Once it is accepted on the mender interface the following will be displayed:
[00:10:02.176,000] <inf> mender_stm32l4a6_zephyr_example: Mender client connect network
[00:10:13.205,000] <inf> mender_stm32l4a6_zephyr_example: Mender client authenticated
[00:10:13.215,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (884): Checking for deployment...
[00:10:17.791,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (892): No deployment available
[00:10:17.805,000] <inf> mender_stm32l4a6_zephyr_example: Mender client released network
Congratulation! Your device is connected to the mender server. Device type is mender-stm32l4a6-zephyr-example
and the current software version is displayed.
First retrieve mender-artifact tool.
Change VERSION
file to VERSION_MAJOR=0
and VERSION_MINOR=2
, rebuild and sign the firmware using the following commands. We previously used hex
file because it is required to flash the device, but we now use bin
file that is required for the mender-mcu-client:
west build -b nucleo_l4a6zg path/to/mender-stm32l4a6-zephyr-example
Then create a new artifact using the following command line:
path/to/mender-artifact write rootfs-image --compression none --device-type mender-stm32l4a6-zephyr-example --artifact-name mender-stm32l4a6-zephyr-example-v0.2.0 --output-path build/zephyr/mender-stm32l4a6-zephyr-example-v0.2.0.mender --file build/zephyr/zephyr.signed.bin
Upload the artifact mender-stm32l4a6-zephyr-example-v0.2.0.mender
to the mender server and create a new deployment.
The device checks for the new deployment, downloads the artifact and installs it on the slot1_partition
. Then it reboots to apply the update:
[00:40:02.176,000] <inf> mender_stm32l4a6_zephyr_example: Mender client connect network
[00:40:02.186,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (884): Checking for deployment...
[00:40:07.045,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (918): Downloading deployment artifact with id 'eac3b245-5bdd-427f-b763-?
[00:40:11.614,000] <inf> mender_stm32l4a6_zephyr_example: Deployment status is 'downloading'
[00:40:17.565,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-artifact.c (382): Artifact has valid version
[00:40:17.585,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/platform/flash/zephyr/src/mender-flash.c (42): Start flashing artifact 'zephyr.signed.bin' with s6
[00:40:45.410,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (930): Download done, installing artifact
[00:40:50.039,000] <inf> mender_stm32l4a6_zephyr_example: Deployment status is 'installing'
[00:40:54.588,000] <inf> mender_stm32l4a6_zephyr_example: Deployment status is 'rebooting'
[00:40:54.599,000] <inf> mender_stm32l4a6_zephyr_example: Mender client released network
[00:40:54.610,000] <inf> mender_stm32l4a6_zephyr_example: Restarting system
uart:~$ *** Booting MCUboot v2.1.0-rc1-36-gfb2cf0ec3da3 ***
*** Using Zephyr OS build v3.7.0 ***
I: Starting bootloader
I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Boot source: primary slot
I: Image index: 0, Swap type: test
I: Starting swap using scratch algorithm.
I: Bootloader chainload address offset: 0xe000
I: Jumping to the first image slot
[00:00:02.016,000] <inf> eth_w5500: eth_w5500@0: Link up
[00:00:02.033,000] <inf> mender_stm32l4a6_zephyr_example: IPv4 address: 192.168.1.216
[00:00:02.044,000] <inf> mender_stm32l4a6_zephyr_example: Lease time: 43200 seconds
[00:00:02.054,000] <inf> mender_stm32l4a6_zephyr_example: Subnet: 255.255.255.0
[00:00:02.064,000] <inf> mender_stm32l4a6_zephyr_example: Router: 192.168.1.1
[00:00:02.074,000] <inf> mender_stm32l4a6_zephyr_example: MAC address of the device '00:08:dc:01:02:03'
[00:00:02.086,000] <inf> mender_stm32l4a6_zephyr_example: Running project 'mender-stm32l4a6-zephyr-example' version '0.2.0'
[00:00:02.103,000] <inf> mender_stm32l4a6_zephyr_example: Mender client initialized
[00:00:02.113,000] <inf> mender_stm32l4a6_zephyr_example: Mender client registered hello-world module
[00:00:02.125,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/platform/storage/zephyr/nvs/src/mender-storage.c (242): Device configuration not available
[00:00:02.143,000] <inf> mender_stm32l4a6_zephyr_example: Mender configure add-on registered
[00:00:02.154,000] <inf> mender_stm32l4a6_zephyr_example: Mender inventory add-on registered
[00:00:02.165,000] <inf> mender_stm32l4a6_zephyr_example: Device configuration retrieved
[00:00:02.177,000] <inf> mender_stm32l4a6_zephyr_example: Mender client connect network
[00:00:12.840,000] <inf> mender_stm32l4a6_zephyr_example: Mender client authenticated
[00:00:12.851,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/platform/flash/zephyr/src/mender-flash.c (146): Application has been mark valid and rollback cancd
[00:00:17.383,000] <inf> mender_stm32l4a6_zephyr_example: Deployment status is 'success'
[00:00:17.394,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (884): Checking for deployment...
[00:00:21.935,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (892): No deployment available
[00:00:21.949,000] <inf> mender_stm32l4a6_zephyr_example: Mender client released network
Congratulation! You have updated the device. Mender server displays the success of the deployment.
In case of failure to connect and authenticate to the server the current example application performs a rollback to the previous release. You can customize the behavior of the example application to add your own checks and perform the rollback in case the tests fail.
This example application supports LLEXT feature to permit loading and executing LLEXT modules using mender-mcu-client.
Compile the hello-world LLEXT sample from Zephyr as below:
west build -d build-llext -b nucleo_l4a6zg samples/subsys/llext/modules
Then create a new artifact using the following command line:
path/to/mender-artifact write module-image --compression none --device-type mender-stm32l4a6-zephyr-example --artifact-name hello-world-v1 --type hello-world --output-path build-llext/zephyr/mender-module-hello-world-v1.mender --file build-llext/zephyr/hello_world.llext
Upload the artifact mender-module-hello-world-v1.mender
to the mender server and create a new deployment.
The device checks for the new deployment, downloads the artifact and call the hellow_world
function of the module, which is simply displyaing Hello, world, from an llext!
log in the console:
[00:00:12.987,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (884): Checking for deployment...
[00:00:17.583,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (918): Downloading deployment artifact with id '360e14aa-33d2-4f99-af7d-A
[00:00:22.177,000] <inf> mender_stm32l4a6_zephyr_example: Deployment status is 'downloading'
[00:00:27.725,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-artifact.c (382): Artifact has valid version
[00:00:27.751,000] <inf> mender: CMAKE_SOURCE_DIR/components/mender-mcu-client/core/src/mender-client.c (930): Download done, installing artifact
[00:00:32.275,000] <inf> mender_stm32l4a6_zephyr_example: Deployment status is 'installing'
Hello, world, from an llext!
[00:00:36.817,000] <inf> mender_stm32l4a6_zephyr_example: Deployment status is 'success'
The Device Troubleshoot add-on permits to display the Zephyr Shell on the Mender interface. Autocompletion and colors are available.
The Device Troubleshoot add-on also permits to upload/download files to/from the Mender server. The littlefs partition mounted at /littlefs
is used to demonstrate this feature. To send a file to the device, destination path must start with /littlefs
. To download a file from the device the full path is expected, starting with /littlefs
.
The zephyr integration into the mender-mcu-client is generic and it is not limited to STM32 MCUs. Several points discussed below should be taken into consideration to use an other hardware, including evaluation boards with other MCU families.
The flash sector size is an important criteria to select an evaluation board. The STM32L4A6ZG MCU has 2KB sectors, which is very convenient to define a custom layout. Other MCUs have variable sectors size and it can be very difficult to choose the partitions.
What remains important is that partitions are aligned on sectors. Moreover slot0_partition
and slot1_partition
must have the same size.
The storage partition should be at least 4KB and must contains at least 3 sectors. In the current example the size of the storage_partition
is 8KB and the configuration is set with CONFIG_MENDER_STORAGE_SECTOR_COUNT=4
(4 sectors of 2KB). You should adapt this if you have a different flash layout.
The example is currently using a W5500 module connected to the NUCLEO-L4A6ZG evaluation board according to the device tree overlay. It is possible to use an other module depending of your own hardware. The mender-mcu-client expect to have a TCP-IP interface but it is not constraint by the physical hardware.
The communication with the server is done using HTTPS. To get it working, the Root CA that is providing the server certificate should be integrated and registered in the application (see tls_credential_add
in the src/main.c
file). Format of the expected Root CA certificate is DER.
In this example we are using the https://hosted.mender.io
server. While checking the details of the server certificate in your browser, you will see that is is provided by Amazon Root CA 1. Thus the Amazon Root CA 1 certificate AmazonRootCA1.cer
retrieved at https://www.amazontrust.com/repository
is integrated in the application.
Copyright joelguittet and mender-mcu-client contributors
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.