This is an example to show Azure IoT Device Update on Nuvoton's Mbed CE enabled boards. It relies on the following modules:
- Mbed OS Community Edition
- Azure IoT Device SDK port for Mbed OS:
- Azure IoT C SDKs and Libraries
- Adapters for Mbed OS
- Azure IoT Device Update SDK
- Custom step handler (a.k.a. update content handler)
mcubupdate, integrating with MCUboot for platform layer firmware upgrade - Other dependency libraries
- NTP client library
This example is:
-
Port of Azure IoT Device Update Model sample, which implements the model Device Update Model to enable Device Update.
NOTE: See Azure IoT Device Update and Plug and Play for details.
-
Integrating with MCUboot for platform layer firmware upgrade.
NOTE: See MCUboot for Nuvoton's Mbed CE enabled boards for details.
| Platform | Connectivity | Bootloader | Firmware candidate storage |
|---|---|---|---|
| NuMaker-IoT-M467 | Wi-Fi ESP8266 | MCUboot | SPI flash |
Use cmake-based build system. Check out hello world example for getting started.
NOTE: Legacy development tools below are not supported anymore.
For VS Code development or OpenOCD as upload method, install below additionally:
- NuEclipse: Nuvoton's fork of Eclipse
- Nuvoton forked OpenOCD: Shipped with NuEclipse installation package above.
Checking openocd version
openocd --version, it should fix to0.10.022.
This chapter is intended for developers to get started, import the example application, build, and get it running as Azure IoT Device Update enabled device.
NOTE: The following command lines are verified on Windows git-bash environment. For other shell environments, check Inline JSON help on how different shells use line continuation, quotation marks, and escapes characters differently.
-
NOTE: Used for signing application binary for MCUboot
-
NOTE: Used for concatenating MCUboot bootloader binary and application binary
-
NOTE: You need to also install
azure-iotextension:$ az extension add --name azure-iotNOTE: Used for creating Device Update import manifest.
- Firmware candidate storage
- NuMaker-IoT-M467: On-board SPI flash
- Switch target board
- NuMaker-IoT-M467's Nu-Link2: TX/RX/VCOM to ON, MSG to non-ON
- Connect target board to host through USB
- NuMaker-IoT-M467: Mbed USB drive shows up in File Browser
-
Sign in to Azure portal.
-
Create one IoT Hub and Device using symmetric key authentication.
-
Create one Device Update account and instance.
-
Create one Storage account and container.
NOTE: Make sure you have completed configuring access control roles.
In the following, we take NuMaker-IoT-M467 as example board to show this example.
-
Clone the example and navigate into it
$ git clone https://github.com/mbed-nuvoton/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example $ cd NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example $ git checkout -f master -
Deploy necessary libraries
$ git submodule update --initOr for fast install:
$ git submodule update --init --filter=blob:noneDeploy further for
mbed-ce-client-for-azurelibrary:$ cd mbed-ce-client-for-azure; \ git submodule update --init; \ cd ..Or for fast install:
$ cd mbed-ce-client-for-azure; \ git submodule update --init --filter=blob:none; \ cd .. -
Configure network interface
-
Ethernet: Need no further configuration.
mbed_app.json5:
"target.network-default-interface-type" : "Ethernet",
-
WiFi: Configure WiFi
SSID/PASSWORD.mbed_app.json5:
"target.network-default-interface-type" : "WIFI", "nsapi.default-wifi-security" : "WPA_WPA2", "nsapi.default-wifi-ssid" : "\"SSID\"", "nsapi.default-wifi-password" : "\"PASSWORD\"",
-
-
In
configs/aduc_user_config.h, provide relevant information.NOTE: To meet the following build command lines, just provide your device connection string acquired above to
ADUC_DEVICE_CONNECTION_STRING, and make no other modifications. -
Build application of first version
1.0.0as base.NOTE: We will see application related versions appear in several places. Even though they are managed separately and can be different for the same application binary, make them the same for consistency.
- Software version in Device information interface
- Application version in imgtool sign for MCUboot
- Update ID version in import manifest
- Installed criteria for the custom step handler
mcubupdate
-
In
configs/aduc_user_config.h, setADUC_DEVICEINFO_SW_VERSIONto1.0.0. -
Compile with cmake/ninja
$ mkdir build; cd build $ cmake .. -GNinja -DCMAKE_BUILD_TYPE=Develop -DMBED_TARGET=NUMAKER_IOT_M467 $ ninja $ cd .. -
Append version suffix
_V1.0.0to the built image file name for distinct from below.$ mv \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example.bin \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.0.bin -
Sign application binary of first version
1.0.0$ imgtool sign \ -k bootloader/MCUboot/signing-keys.pem \ --align 4 \ -v 1.0.0+0 \ --header-size 4096 \ --pad-header \ -S 0xE6000 \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.0.bin \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.0_signed.binNOTE: This file will be combined with MCUboot bootloader later.
NOTE: For consistency, set application version for MCUboot to
1.0.0+0.NOTE:
-S 0xE6000is to specify MCUboot primary/secondary slot size. -
Combine MCUboot bootloader binary and signed application binary of first version
1.0.0$ srec_cat \ bootloader/MCUboot/mbed-ce-mcuboot-bootloader_m467-iot_spif.bin -Binary -offset 0x0 \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.0_signed.bin -Binary -offset 0x10000 \ -o build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_merged.hex -IntelNOTE: The combined file
NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_merged.hexis for flash later.NOTE:
-offset 0x0is to specify start address of MCUboot bootloader.NOTE:
-offset 0x10000is to specify start address of primary slot where active application binary is located.
-
Rebuild application of second version
1.0.1for update.-
In
configs/aduc_user_config.h, setADUC_DEVICEINFO_SW_VERSIONto1.0.1. -
Re-compile with cmake/ninja
$ cd build $ ninja $ cd .. -
Append version suffix
_V1.0.1to the built image file name for distinct from above.$ mv \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example.bin \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1.bin -
Sign application binary of second version
1.0.1$ imgtool sign \ -k bootloader/MCUboot/signing-keys.pem \ --align 4 \ -v 1.0.1+0 \ --header-size 4096 \ --pad-header \ -S 0xE6000 \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1.bin \ build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1_signed.binNOTE: For consistency, set application version for MCUboot to
1.0.1+0.NOTE: This file is for upload to Azure Device Update later.
-
Generate Azure Device Update import manifest
$ az iot du update init v5 \ --update-provider Nuvoton \ --update-name NuMaker-ADU-Sample-Update \ --update-version 1.0.1 \ --compat manufacturer=Nuvoton model=NuMaker-ADU-Sample-Device \ --step handler=nuvoton/mcubupdate:1 properties='{"installedCriteria": "1.0.1"}' \ --file path="./build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1_signed.bin" \ > Nuvoton_NuMaker-ADU-Sample-Update_1.0.1.importmanifest.jsonNOTE: For consistency, set
--update-versionto1.0.1.NOTE: If changed,
--compatmust matchADUC_COMPAT_PROPERTY_NAMES,ADUC_DEVICEPROPERTIES_MANUFACTURER, andADUC_DEVICEPROPERTIES_MODELin aboveconfigs/aduc_user_config.h.NOTE: Continuing above,
manufacturer/modelare the common compatibility properties.NOTE:
nuvoton/mcubupdate:1is the custom step handler, integrating with MCUboot for platform layer firmware upgrade.NOTE: For consistency, set
installedCriteriato1.0.1.NOTE: An import manifest JSON file name must end with
.importmanifest.jsonwhen imported through Azure portal. See Create an import manifest.NOTE: This file is for upload to Azure Device Update later.
-
Flash the first version 1.0.0 by drag-n-drop'ing the built image file generated above onto NuMaker-IoT-M467 board
build/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.0_merged.hex
-
Import an update to Device Update.
The files generated above are for import:
Nuvoton_NuMaker-ADU-Sample-Update_1.0.1.importmanifest.jsonbuild/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1_signed.bin
NOTE: The import process can take a few minutes to complete.
-
NOTE: The device must have had connected with IoT Hub so that default or created device group can automatically generate.
Configure host terminal program with 115200/8-N-1, and you should see log similar to below:
On first boot, MCUboot does no firmware upgrade before update deployment:
[INFO][BL]: Starting MCUboot
[INFO][MCUb]: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[INFO][MCUb]: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[INFO][MCUb]: Boot source: primary slot
[INFO][MCUb]: Swap type: none
[INFO][BL]: Booting firmware image at 0x11000
Application version before update deployment should be 1.0.0:
Info: Application version (deviceInformation.swVersion): 1.0.0
Connect to Azure IoT Hub:
Info: Attempting to create connection to IotHub using type: ADUC_ConnType_Device
Info: IotHub Protocol: MQTT
Info: IoTHub Device Twin callback registered.
Info: Refreshing the handle for the PnP channels.
Info: Successfully re-authenticated the IoT Hub connection.
-> 1:36:11 CONNECT | VER: 4 | KEEPALIVE: 240 | FLAGS: 192 | USERNAME: hub-002.azure-devices.net/device-symm-002/?api-version=2020-09-30&DeviceClientType=iothubclient%2f1.9.1%20(native%3b%20mbedOS5%3b%20undefined)&model-id=dtmi%3aazure%3aiot%3adeviceUpdateModel%3b2 | PWD: XXXX | CLEAN: 0
Info: The connection is currently broken. Will try to authenticate in 17 seconds.
<- 1:36:12 CONNACK | SESSION_PRESENT: true | RETURN_CODE: 0x0
Info: IotHub connection status: 0, reason: 6
To print heap usage, press h:
** MBED HEAP STATS **
**** current_size : 44019
**** max_size : 55584
**** reserved_size : 472880
To print stack usage, press s:
** MBED THREAD STACK STATS **
Thread: 0x20008fd4, Stack size: 16384, Max stack: 10240
Thread: 0x20008f4c, Stack size: 512, Max stack: 72
Thread: 0x20008f90, Stack size: 768, Max stack: 96
Thread: 0x20003ce8, Stack size: 2048, Max stack: 608
*****************************
Device will receive update action applyDeployment after you start update deployment on Azure portal:
Info: Update Action info string ({"workflow":{"action":3.00000000000000000,"id":"9b410934-b4e2-49d9-aa71-b40bf2265557"},"updateManifest":"{\"manifestVersion\":\"5\",\"updateId\":{\"provider\":\"Nuvoton\",\"name\":\"NuMaker-ADU-Sample-Update\",\"version\":\"1.0.1\"},\"compatibility\":[{\"manufacturer\":\"Nuvoton\",\"model\":\"NuMaker-ADU-Sample-Device\"}],\"instructions\":{\"steps\":[{\"handler\":\"nuvoton\/mcubupdate:1\",\"files\":[\"f7c99108ce676353f\"],\"handlerProperties\":{\"installedCriteria\":\"1.0.1\"}}]},\"files\":{\"f7c99108ce676353f\":{\"fileName\":\"NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1_signed.bin\",\"sizeInBytes\":629688,\"hashes\":{\"sha256\":\"CWPiC5Z8plEOecGF+tASNnqOXWD71A+oJJIBhSt8j8I=\"}}},\"createdDateTime\":\"2023-03-15T09:55:47Z\"}","updateManifestSignature":null,"fileUrls":null}), property version (21)
......
Info: Processing current step:
{
"handler": "nuvoton\/mcubupdate:1",
"files": [
"f7c99108ce676353f"
],
"handlerProperties": {
"installedCriteria": "1.0.1"
}
}
Download started:
Info: workflow is not completed. AutoTransition to step: Download
Info: Processing 'Download' step
......
Info: Loading handler for step #0 (handler: 'nuvoton/mcubupdate:1')
Info: Loading Update Content Handler for 'nuvoton/mcubupdate:1'.
Info: Determining contract version for 'nuvoton/mcubupdate:1'.
Info: Caching new content handler for 'nuvoton/mcubupdate:1'.
Info: No installed criteria settled down. Maybe it is the first time for ADU.
Info: Upgrade firmware: FileId f7c99108ce676353f
Info: Upgrade firmware: DownloadUri http://hub-002--hub-002.b.nlu.dl.adu.microsoft.com/eastus/hub-002--hub-002/0b8713a8590744e09b74e74d479828ef/NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1_signed.bin
Info: Upgrade firmware: TargetFilename NuMaker-mbed-ce-Azure-IoT-CSDK-OTA-example_V1.0.1_signed.bin
Info: Upgrade firmware: SizeInBytes 629688
Info: Secondary BlockDevice size: 942080 (bytes)
......
Info: Active image version: 1.0.0+0
Info: HTTP download: 0/629688
Info: Image header: padded header size=4096, image size=625256, protected TLV size=0
Info: Stage image version: 1.0.1+0
Info: HTTP download: 1209/629688
Info: HTTP download: 3257/629688
Info: HTTP download: 5305/629688
Download completed:
Info: HTTP download: 624981/629688
Info: HTTP download: 627029/629688
Info: HTTP download: 629077/629688
Info: HTTP download: Completed 629688/629688 bytes
Info: Steps_Handler Download end (level 0).
Info: Action 'Download' complete. Result: 500 (succeeded), 0 (0x0)
After Apply completed, device will reboot:
Info: Action 'Apply' complete. Result: 700 (succeeded), 0 (0x0)
Info: Apply indicated success with RebootRequired - rebooting system now
Info: Calling ADUC_RebootSystem
Info: ADUC_RebootSystem called. Rebooting system.
On second boot, MCUboot does firmware upgrade after update deployment:
[INFO][BL]: Starting MCUboot
[INFO][MCUb]: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[INFO][MCUb]: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
[INFO][MCUb]: Boot source: primary slot
[INFO][MCUb]: Swap type: test
[INFO][MCUb]: Starting swap using scratch algorithm.
[INFO][BL]: Booting firmware image at 0x11000
Application version after update deployment should be 1.0.1:
Info: Application version (deviceInformation.swVersion): 1.0.1
Re-connect to Azure IoT Hub:
Info: Attempting to create connection to IotHub using type: ADUC_ConnType_Device
Info: IotHub Protocol: MQTT
Info: IoTHub Device Twin callback registered.
Info: Refreshing the handle for the PnP channels.
Info: Successfully re-authenticated the IoT Hub connection.
-> 1:43:28 CONNECT | VER: 4 | KEEPALIVE: 240 | FLAGS: 192 | USERNAME: hub-002.azure-devices.net/device-symm-002/?api-version=2020-09-30&DeviceClientType=iothubclient%2f1.9.1%20(native%3b%20mbedOS5%3b%20undefined)&model-id=dtmi%3aazure%3aiot%3adeviceUpdateModel%3b2 | PWD: XXXX | CLEAN: 0
Info: The connection is currently broken. Will try to authenticate in 17 seconds.
<- 1:43:28 CONNACK | SESSION_PRESENT: true | RETURN_CODE: 0x0
Info: IotHub connection status: 0, reason: 6
The chapter lists advanced topics on customizing the application.
This example uses MCUboot as bootloader. You can make the following adjustments:
-
Recreate the prebuilt bootloader binary
mbed-mcuboot-bootloader_m467-iot_spif.bin.Follow MCUboot for Nuvoton's Mbed CE enabled boards, with target name being
NUMAKER_IOT_M467_SPIF -
Change attached signing keys
signing-keys.pemandsigning_keys.cfor production.-
Follow change signing keys. In
mbed-mcuboot-demo, recreate signing keys and then recreate bootloader binary.NOTE: Ignore the
mbed-mcuboot-blinkypart. -
In this example, replace the prebuilt bootloader binary
mbed-mcuboot-bootloader_m467-iot_spif.binwith the recreated one above. -
In this example. synchronize updated signing keys in
mbed-mcuboot-demo.
-
-
Change firmware candidate storage.
-
Follow support new target. In
mbed-mcuboot-demo, re-implementget_secondary_bd()and then recreate bootloader binary.NOTE: Ignore the
mbed-mcuboot-blinkypart. -
In this example, replace the prebuilt bootloader binary
mbed-mcuboot-bootloader_m467-iot_spif.binwith the recreated one above. -
In this example, remove default
get_secondary_bd()following below and replace with the re-implementedget_secondary_bd()inmbed-mcuboot-demo. The re-implementedget_secondary_bd()can be placed anywhere, for example, inmain.cpp.mbed_app.json5:
"azure-client-ota-mcuboot.provide-default-secondary-blockdevice" : null,
-
To provide your own device model enabling Device Update, according to ability to support custom model Id, you must follow the rules:
-
Extend Device Update Base Model or Device Update Model to define your device model.
-
Publish this device model. Private device model is not supported.
NOTE: Published Temperature Controller, which extends Device Update Base Model, can be used to quickly evaluate the implementation of custom device model.
Based on this example code, to implement the custom device model, you need to:
-
In
configs/aduc_user_config.h, overrideADUC_DEVICE_MODEL_IDwith the custom device model ID. -
In the supported component list below,
- Keep
deviceUpdate. - Keep
deviceInformationif the custom device model supports Device Information on its own, even though it just extends Device Update Base Model. - Keep
deviceInformationanddiagnosticInformationif the custom device model extends Device Update Model. - Add other components of your own.
static PnPComponentEntry componentList[] = { { g_aduPnPComponentName, &g_iotHubClientHandleForADUComponent, AzureDeviceUpdateCoreInterface_Create, AzureDeviceUpdateCoreInterface_Connected, AzureDeviceUpdateCoreInterface_DoWork, AzureDeviceUpdateCoreInterface_Destroy, AzureDeviceUpdateCoreInterface_PropertyUpdateCallback }, { g_deviceInfoPnPComponentName, &g_iotHubClientHandleForDeviceInfoComponent, DeviceInfoInterface_Create, DeviceInfoInterface_Connected, NULL /* DoWork method - not used */, DeviceInfoInterface_Destroy, NULL /* PropertyUpdateCallback - not used */ }, - Keep
-
To implement other components of your own, reference deviceInformation as starting point.
NOTE: For messaging, this intrinsic component invokes inbuilt ADUC_D2C_Message API, which doesn't support extrinsic components. Use ClientHandle API for extrinsic components instead.
-
Support no Device Provisioning Service.
Currently, support only connecting straight with IoT Hub.
-
Support no Diagnostics.
Dummy implementation of Diagnostic Information Interface is provided to meet resource constrained device.
-
Support no reference steps and no detached update manifests.
The above need file system to place extra downloaded update manifests. Support only inline step and not large update manifest.
-
Support no other step handlers
Support only custom step handler
mcubupdateto meet target device capability. Others likeapt,script, andswupdateare not supported.
-
In Device Update, device group or registered device doesn't show up.
Make sure the device has connected with IoT Hub at least one time for reporting its Device Update capability, and then initiate a device sync operation.
-
On Azure portal, device is not listed during deployment.
The issue is being tracked via the ticket.
-
Continuing above, after reboot, device will halt at the point below on reconnecting to IoT Hub.
Info: Attempting to create connection to IotHub using type: ADUC_ConnType_Device Info: IotHub Protocol: MQTT Info: IoTHub Device Twin callback registered. Info: Refreshing the handle for the PnP channels. Info: Successfully re-authenticated the IoT Hub connection. -> 1:43:28 CONNECT | VER: 4 | KEEPALIVE: 240 | FLAGS: 192 | USERNAME: hub-002.azure-devices.net/device-symm-002/?api-version=2020-09-30&DeviceClientType=iothubclient%2f1.9.1%20(native%3b%20mbedOS5%3b%20undefined)&model-id=dtmi%3aazure%3aiot%3adeviceUpdateModel%3b2 | PWD: XXXX | CLEAN: 0 Info: The connection is currently broken. Will try to authenticate in 17 seconds. <- 1:43:28 CONNACK | SESSION_PRESENT: true | RETURN_CODE: 0x0 Info: IotHub connection status: 0, reason: 6 -> 1:43:28 SUBSCRIBE | PACKET_ID: 2 | TOPIC_NAME: $iothub/twin/res/# | QOS: 0 <- 1:43:28 SUBACK | PACKET_ID: 2 | RETURN_CODE: 0 -> 1:43:28 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/twin/GET/?$rid=3This issue is unclear with Device Update service. Canceling and/or removing the active deployment can recover to normal situation.