Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DFU over ThingSet and CAN DFU script #48

Merged
merged 4 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 5 additions & 1 deletion include/thingset/sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions samples/counter/dfu.conf
Original file line number Diff line number Diff line change
@@ -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
119 changes: 119 additions & 0 deletions scripts/thingset-dfu-can.py
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions src/Kconfig.dfu
Original file line number Diff line number Diff line change
@@ -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
107 changes: 107 additions & 0 deletions src/dfu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright (c) The ThingSet Project Contributors
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/device.h>
#include <zephyr/dfu/flash_img.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/sys/reboot.h>

#include <thingset.h>
#include <thingset/sdk.h>

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;
}
2 changes: 1 addition & 1 deletion subsys/canbus/isotp_fast/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
4 changes: 3 additions & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ manifest:
- hal_st
- loramac-node
- mbedtls
- tinycrypt
- mcuboot
- picolibc
- segger
- tinycrypt
- name: thingset-node-c
remote: thingset
revision: d3338c7623869637bc6ce0b95358e44db2a3443f
Expand Down
Loading