diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4651bb2..71afda2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,7 @@ jobs: west build -p -b esp32c3_devkitm samples/counter -- -DEXTRA_CONF_FILE=ble.conf -DCONFIG_COMPILER_WARNINGS_AS_ERRORS=y west build -p -b esp32c3_devkitm samples/counter -- -DEXTRA_CONF_FILE=wifi_websocket.conf -DCONFIG_COMPILER_WARNINGS_AS_ERRORS=y west build -p -b native_posix samples/counter -- -DCONFIG_COMPILER_WARNINGS_AS_ERRORS=y + west build -p -b nrf52840dk_nrf52840 samples/counter -- -DEXTRA_CONF_FILE=dfu.conf -DCONFIG_COMPILER_WARNINGS_AS_ERRORS=y west build -p -b nucleo_l073rz samples/counter -- -DEXTRA_CONF_FILE=serial.conf -DCONFIG_COMPILER_WARNINGS_AS_ERRORS=y west build -p -b nucleo_l073rz samples/counter -- -DEXTRA_CONF_FILE=storage_eeprom.conf -DCONFIG_COMPILER_WARNINGS_AS_ERRORS=y west build -p -b native_posix samples/counter -- -DEXTRA_CONF_FILE=auth.conf -DCONFIG_COMPILER_WARNINGS_AS_ERRORS=y diff --git a/Kconfig b/Kconfig index 63ce8f9..3743092 100644 --- a/Kconfig +++ b/Kconfig @@ -9,6 +9,7 @@ if THINGSET_SDK rsource "src/Kconfig.auth" rsource "src/Kconfig.ble" rsource "src/Kconfig.can" +rsource "src/Kconfig.dfu" rsource "src/Kconfig.log_backend" rsource "src/Kconfig.lorawan" rsource "src/Kconfig.serial" diff --git a/include/thingset/sdk.h b/include/thingset/sdk.h index b661805..7caafee 100644 --- a/include/thingset/sdk.h +++ b/include/thingset/sdk.h @@ -65,7 +65,11 @@ extern "C" { #define TS_ID_NET_CAN_NODE_ADDR 0x28C /* Device Firmware Upgrade group items */ -#define TS_ID_DFU 0x2D +#define TS_ID_DFU 0x2D +#define TS_ID_DFU_INIT 0x2D0 +#define TS_ID_DFU_WRITE 0x2D1 +#define TS_ID_DFU_DATA 0x2D2 +#define TS_ID_DFU_BOOT 0x2D3 /* Log group items */ #define TS_ID_LOG 0x2E diff --git a/samples/counter/dfu.conf b/samples/counter/dfu.conf new file mode 100644 index 0000000..45b241d --- /dev/null +++ b/samples/counter/dfu.conf @@ -0,0 +1,16 @@ +# Copyright (c) The ThingSet Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_THINGSET_DFU=y + +# Flash driver to store firmware image +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_STREAM_FLASH=y +CONFIG_IMG_MANAGER=y + +# Application-side MCUboot configuration +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="bootloader/mcuboot/root-ec-p256.pem" +CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE=y diff --git a/scripts/thingset-dfu-can.py b/scripts/thingset-dfu-can.py new file mode 100755 index 0000000..e401118 --- /dev/null +++ b/scripts/thingset-dfu-can.py @@ -0,0 +1,119 @@ +#!/bin/python3 +# +# Copyright (c) The ThingSet Project Contributors +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import socket +import time +import argparse + +from datetime import datetime +from progress.bar import Bar + +parser = argparse.ArgumentParser(description='ThingSet CAN DFU tool.') +parser.add_argument('filename', help='Path to Zephyr .bin file to flash') +parser.add_argument('-c', '--can', default='can0', help='CAN interface') +parser.add_argument('-t', '--target', default=0xa0, type=lambda x: int(x,0), + help='Target CAN node address') +parser.add_argument('-s', '--source', default=0x00, type=lambda x: int(x,0), + help='Source CAN node address') +parser.add_argument('-tb', '--target-bus', default=0x0, type=lambda x: int(x,0), + help='Target CAN bus number') +parser.add_argument('-sb', '--source-bus', default=0x0, type=lambda x: int(x,0), + help='Source CAN bus number') + +args = parser.parse_args() +bin_file = args.filename + +if (args.target < 0x01 or args.target > 0xFD): + print('Target addresses must be between 0x01 and 0xFD') + exit(-1) + +if (args.source < 0x00 or args.source > 0xFD): + print('Source addresses must be between 0x00 and 0xFD') + exit(-1) + +if (args.target_bus < 0x0 or args.target_bus > 0xF): + print('Target bus must be between 0x0 and 0xF') + exit(-1) + +if (args.source_bus < 0x0 or args.source_bus > 0xF): + print('Source bus must be between 0x0 and 0xF') + exit(-1) + +page_size = 256 + +CAN_EFF_FLAG = 0x80000000 + +host2dev = 0x18000000 | CAN_EFF_FLAG | \ + (args.target_bus << 20) | (args.source_bus << 16) | \ + (args.target << 8) | (args.source) + +dev2host = 0x18000000 | CAN_EFF_FLAG | \ + (args.source_bus << 20) | (args.target_bus << 16) | \ + (args.source << 8) | (args.target) + +s = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) +s.bind((args.can, dev2host, host2dev)) +s.settimeout(3) + +def ts_request(req): + try: + sent_bytes = s.send(req) + if (sent_bytes == len(req)): + recv_bytes = s.recv(4095) + return recv_bytes[0] + except TimeoutError: + print("Could not connect to device, socket timed out.") + return 0xC3 # ThingSet error code: Service Unavailable + +start = datetime.now() + +print("Initializing DFU") +code = ts_request(b"\x02\x19\x02\xD0\x80") # !DFU/xInit [] +if code != 0x84: + print("Initializing DFU failed with code 0x%X" % code) + exit() + +with open(bin_file, "rb") as f: + offset = 0 + size = os.path.getsize(bin_file) / 1024 + progress = Bar("Flashing", max=size, suffix="%(index)d/%(max)d KiB = %(percent)d%%") + while True: + buf = f.read(page_size) + if not buf: + break + command = b"\x02\x19\x02\xD1" # exec node 0xD2 !DFU/xWrite + command += b"\x81" # array with one element + command += b"\x59" + len(buf).to_bytes(2, "big") + command += buf + + for tries in range(5): + sent_bytes = s.send(command) + if (sent_bytes == len(command)): + recv_bytes = s.recv(4095) + # check ThingSet status code and errno returned returned in payload + if recv_bytes[0] == 0x84 and recv_bytes[2] == 0x00: + offset += page_size + progress.next(n=(len(buf) / 1024)) + break + else: + print("Firmware upgrade failed") + print(recv_bytes) + else: + print("\nConnection timed out. Failed to send all data.") + exit() + + time.sleep(0.01) + +print("\nFinishing DFU") +code = ts_request(b"\x02\x19\x02\xD3\x80") # !DFU/xBoot [] +if code != 0x84: + print("Finishing DFU failed with code 0x%X" % code) + exit() + +stop = datetime.now() + +print("Total duration:", stop - start) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 122b40c..70c2edd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ zephyr_library_sources_ifdef(CONFIG_THINGSET_SDK packetizer.c) zephyr_library_sources_ifdef(CONFIG_THINGSET_AUTH auth.c) zephyr_library_sources_ifdef(CONFIG_THINGSET_BLE ble.c) zephyr_library_sources_ifdef(CONFIG_THINGSET_CAN can.c) +zephyr_library_sources_ifdef(CONFIG_THINGSET_DFU dfu.c) zephyr_library_sources_ifdef(CONFIG_THINGSET_LOG_BACKEND log_backend.c) zephyr_library_sources_ifdef(CONFIG_THINGSET_LORAWAN lorawan.c) zephyr_library_sources_ifdef(CONFIG_THINGSET_SERIAL serial.c) diff --git a/src/Kconfig.dfu b/src/Kconfig.dfu new file mode 100644 index 0000000..fa47330 --- /dev/null +++ b/src/Kconfig.dfu @@ -0,0 +1,22 @@ +# Copyright (c) The ThingSet Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +menuconfig THINGSET_DFU + bool "ThingSet device firmware upgrade" + depends on FLASH + depends on FLASH_MAP + depends on FLASH_PAGE_LAYOUT + depends on IMG_MANAGER + select THINGSET_BYTES_TYPE_SUPPORT + select REBOOT + +if THINGSET_DFU + +config THINGSET_DFU_CHUNK_SIZE + int "Firmware chunk size" + range 64 4096 + default 256 + help + Maximum chunk size that can be transmitted in one ThingSet message. + +endif # THINGSET_DFU diff --git a/src/dfu.c b/src/dfu.c new file mode 100644 index 0000000..58bccdd --- /dev/null +++ b/src/dfu.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) The ThingSet Project Contributors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(thingset_dfu, CONFIG_THINGSET_SDK_LOG_LEVEL); + +#define TARGET_IMAGE_AREA FIXED_PARTITION_ID(slot1_partition) + +static uint8_t bytes_buf[CONFIG_THINGSET_DFU_CHUNK_SIZE]; +static THINGSET_DEFINE_BYTES(bytes_item, bytes_buf, 0); + +static int32_t thingset_dfu_init(); +static int32_t thingset_dfu_write(); +static int32_t thingset_dfu_boot(); + +THINGSET_ADD_GROUP(TS_ID_ROOT, TS_ID_DFU, "DFU", THINGSET_NO_CALLBACK); +THINGSET_ADD_FN_INT32(TS_ID_DFU, TS_ID_DFU_INIT, "xInit", &thingset_dfu_init, THINGSET_ANY_RW); +THINGSET_ADD_FN_INT32(TS_ID_DFU, TS_ID_DFU_WRITE, "xWrite", &thingset_dfu_write, THINGSET_ANY_RW); +THINGSET_ADD_ITEM_BYTES(TS_ID_DFU_WRITE, TS_ID_DFU_DATA, "bData", &bytes_item, THINGSET_ANY_RW, 0); +THINGSET_ADD_FN_INT32(TS_ID_DFU, TS_ID_DFU_BOOT, "xBoot", &thingset_dfu_boot, THINGSET_ANY_RW); + +static bool dfu_initialized = false; + +static struct flash_img_context flash_img_ctx; + +static int32_t thingset_dfu_init() +{ + int err; + + LOG_INF("Initializing DFU"); + + if (!IS_ENABLED(CONFIG_IMG_ERASE_PROGRESSIVELY)) { + LOG_DBG("Erasing flash area"); + + err = boot_erase_img_bank(TARGET_IMAGE_AREA); + if (err) { + LOG_ERR("Failed to erase image bank (err %d)", err); + return err; + } + } + + err = flash_img_init_id(&flash_img_ctx, TARGET_IMAGE_AREA); + if (err) { + LOG_ERR("Failed to initialize flash img (err %d)", err); + return err; + } + + dfu_initialized = true; + + return 0; +} + +static int32_t thingset_dfu_write() +{ + int err; + + if (!dfu_initialized) { + LOG_ERR("DFU not initialized"); + return -EBUSY; + } + + err = flash_img_buffered_write(&flash_img_ctx, bytes_item.bytes, bytes_item.num_bytes, false); + if (err) { + LOG_ERR("Failed to write data (err %d)", err); + return err; + } + else { + size_t total_bytes = flash_img_bytes_written(&flash_img_ctx); + LOG_INF("Total bytes written: 0x%.6X (%d)", total_bytes, total_bytes); + } + + return 0; +} + +static int32_t thingset_dfu_boot() +{ + int err; + uint8_t dummy = 0; + + /* make sure that flash_img buffer is flushed */ + flash_img_buffered_write(&flash_img_ctx, &dummy, 0, true); + + err = boot_request_upgrade(BOOT_UPGRADE_TEST); + if (err) { + LOG_ERR("Failed to finish DFU (err %d)", err); + return err; + } + + LOG_INF("DFU finished, rebooting..."); + + sys_reboot(SYS_REBOOT_COLD); + + return 0; +} diff --git a/subsys/canbus/isotp_fast/Kconfig b/subsys/canbus/isotp_fast/Kconfig index 2d2aca1..86be1e1 100644 --- a/subsys/canbus/isotp_fast/Kconfig +++ b/subsys/canbus/isotp_fast/Kconfig @@ -40,7 +40,7 @@ endif # ISOTP_FAST_CUSTOM_ADDRESSING config ISOTP_FAST_RX_MAX_PACKET_COUNT int "Max packets for ISO-TP reception" - default 8 + default 40 help Max number of packets expected in a single ISO-TP message. diff --git a/west.yml b/west.yml index 32ea1ba..4f1c0cf 100644 --- a/west.yml +++ b/west.yml @@ -23,8 +23,10 @@ manifest: - hal_st - loramac-node - mbedtls - - tinycrypt + - mcuboot - picolibc + - segger + - tinycrypt - name: thingset-node-c remote: thingset revision: d3338c7623869637bc6ce0b95358e44db2a3443f