From 1b85d746490044cf7e7145a4e4e329e7c9938ddd Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 29 Sep 2026 15:44:41 +0300 Subject: [PATCH 01/15] cores: arduino: fix typos and remove empty lines Signed-off-by: Sercan Erat --- cores/arduino/CMakeLists.txt | 1 - cores/arduino/main.cpp | 4 +++- cores/arduino/zephyrSerial.h | 8 ++++---- extra/debug.conf | 1 - 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cores/arduino/CMakeLists.txt b/cores/arduino/CMakeLists.txt index 64e9926ca..9d5db4b9a 100644 --- a/cores/arduino/CMakeLists.txt +++ b/cores/arduino/CMakeLists.txt @@ -23,4 +23,3 @@ zephyr_sources(threads.cpp) endif() endif() - diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp index daa82b277..cbfc10360 100644 --- a/cores/arduino/main.cpp +++ b/cores/arduino/main.cpp @@ -40,7 +40,9 @@ int main(void) { loop(); #if 0 //(DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && CONFIG_USB_CDC_ACM) || // DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) - if (arduino::serialEventRun) arduino::serialEventRun(); + if (arduino::serialEventRun) { + arduino::serialEventRun(); + } #endif __loopHook(); } diff --git a/cores/arduino/zephyrSerial.h b/cores/arduino/zephyrSerial.h index e2a52c907..1dcbaa80f 100644 --- a/cores/arduino/zephyrSerial.h +++ b/cores/arduino/zephyrSerial.h @@ -111,7 +111,7 @@ class ZephyrSerial : public HardwareSerial { } // namespace arduino -/* Return the index of it if matched, oterwise return an empty string. */ +/* Return the index of it if matched, otherwise return an empty string. */ #define ZARD_SERIAL_MATCH(n, p, i, node) \ COND_CODE_1(DT_SAME_NODE(DT_PHANDLE_BY_IDX(n, p, i), node), (i), ()) @@ -126,9 +126,9 @@ class ZephyrSerial : public HardwareSerial { */ #define ZARD_SERIAL_NAME_BY_NODE(node) \ COND_CODE_0(IS_EMPTY(ZARD_SERIAL_INDEXOF(node)), \ - (ZARD_SERIAL_NAME(ZARD_SERIAL_INDEXOF(node))), \ - (COND_CODE_1(DT_SAME_NODE(node, ZARD_SERIALUSB_PHANDLE), \ - (SerialUSB), (unkown Serial object)))) + (ZARD_SERIAL_NAME(ZARD_SERIAL_INDEXOF(node))), \ + (COND_CODE_1(DT_SAME_NODE(node, ZARD_SERIALUSB_PHANDLE), \ + (SerialUSB), (unknown Serial object)))) /* Serial object associated with the Zephyr console. */ #define ARDUINO_CONSOLE_SERIAL ZARD_SERIAL_NAME_BY_NODE(DT_CHOSEN(zephyr_console)) diff --git a/extra/debug.conf b/extra/debug.conf index 44a35aff9..3223827a6 100644 --- a/extra/debug.conf +++ b/extra/debug.conf @@ -31,4 +31,3 @@ CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n # Enable runtime statistics CONFIG_SYS_HEAP_RUNTIME_STATS=y CONFIG_THREAD_RUNTIME_STATS=y - From da2a078d1378599c67cd6dfa3ad8427374c84f42 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 29 Sep 2026 15:50:41 +0300 Subject: [PATCH 02/15] loader: don't jump to invalid sketch Signed-off-by: Sercan Erat --- loader/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader/main.c b/loader/main.c index 0947559ea..92a8204db 100644 --- a/loader/main.c +++ b/loader/main.c @@ -268,7 +268,7 @@ static int loader(const struct shell *sh) { size_t sketch_buf_len = sketch_hdr->len; - if (sketch_hdr->flags & SKETCH_FLAG_LINKED) { + if (sketch_valid && (sketch_hdr->flags & SKETCH_FLAG_LINKED)) { #ifdef CONFIG_BOARD_ARDUINO_PORTENTA_C33 #if CONFIG_MPU barrier_dmem_fence_full(); From 5d2be968220489ec01c360bac331aa1be5b6b525 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 29 Sep 2026 17:16:14 +0300 Subject: [PATCH 03/15] build: split board args on macOS macOS ships bash 3.2 without mapfile and a BSD grep without -P, so splitting a board's zephyr_args failed there. On darwin, split them with perl, using the same regular expression. --- extra/build.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/extra/build.sh b/extra/build.sh index 4678b134a..b22b5e113 100755 --- a/extra/build.sh +++ b/extra/build.sh @@ -50,7 +50,14 @@ if ! [ -z "$chosen_board" ]; then # the args field is a single string; split it on unquoted whitespace, # keeping quotes so a quoted value with spaces stays one array element arg_token='(?:[^\s"'\'']+|"[^"]*"|'\''[^'\'']*'\'')+' - mapfile -t args < <(jq -cr '.args' <<< "$chosen_board" | grep -oP "$arg_token") + if [[ "$OSTYPE" == darwin* ]]; then + args=() + while IFS= read -r tok; do + args+=("$tok") + done < <(jq -cr '.args' <<< "$chosen_board" | perl -nle "print \$& while /${arg_token}/g") + else + mapfile -t args < <(jq -cr '.args' <<< "$chosen_board" | grep -oP "$arg_token") + fi upload_offset=$(jq -cr '.upload_offset' <<< "$chosen_board") # Check for debug flag and append From eb24ca1ec0e6564be4af349288ccc8fdb06ad80c Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 29 Sep 2026 17:16:14 +0300 Subject: [PATCH 04/15] west: add zcbor for MCUboot and loramac-node for RAKwireless boards Signed-off-by: Sercan Erat --- west.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/west.yml b/west.yml index 97aef0ede..f65e9549a 100644 --- a/west.yml +++ b/west.yml @@ -36,6 +36,8 @@ manifest: - nanopb - lvgl - fatfs + - loramac-node + - zcbor path-allowlist: - modules/hal/* From 619a2ded096c056dc96a0a9d40d0ed1e0340e8d5 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 29 Sep 2026 17:16:14 +0300 Subject: [PATCH 05/15] variants: rak4631: add RAKwireless RAK4631 core - Arduino pin map - Three USB CDC ACM ports: the sketch's Serial, plus shell and logging ports that only debug builds use - LoRaWAN through the SX1262 on the module - A flash layout and boot-mode retention area shared with MCUboot for a 1200-bps touch serial recovery Signed-off-by: Sercan Erat --- variants/rak4631_nrf52840/doc/index.txt | 101 +++++ .../rak4631_nrf52840/known_example_issues.txt | 12 + variants/rak4631_nrf52840/postvariant.h | 9 + .../rak4631_nrf52840/rak4631_nrf52840.conf | 100 +++++ .../rak4631_nrf52840/rak4631_nrf52840.overlay | 373 ++++++++++++++++++ .../rak4631_nrf52840_bootmode.dtsi | 50 +++ .../rak4631_nrf52840_partitions.dtsi | 56 +++ variants/rak4631_nrf52840/test_setup.sh | 21 + variants/rak4631_nrf52840/variant.h | 124 ++++++ 9 files changed, 846 insertions(+) create mode 100644 variants/rak4631_nrf52840/doc/index.txt create mode 100644 variants/rak4631_nrf52840/known_example_issues.txt create mode 100644 variants/rak4631_nrf52840/postvariant.h create mode 100644 variants/rak4631_nrf52840/rak4631_nrf52840.conf create mode 100644 variants/rak4631_nrf52840/rak4631_nrf52840.overlay create mode 100644 variants/rak4631_nrf52840/rak4631_nrf52840_bootmode.dtsi create mode 100644 variants/rak4631_nrf52840/rak4631_nrf52840_partitions.dtsi create mode 100644 variants/rak4631_nrf52840/test_setup.sh create mode 100644 variants/rak4631_nrf52840/variant.h diff --git a/variants/rak4631_nrf52840/doc/index.txt b/variants/rak4631_nrf52840/doc/index.txt new file mode 100644 index 000000000..d5dbc5a39 --- /dev/null +++ b/variants/rak4631_nrf52840/doc/index.txt @@ -0,0 +1,101 @@ +### RAK4631 Arduino pin map + +The RAK4631 features a 40-pin header with various I/O interfaces for the WisBlock +ecosystem. The pinout is as follows: + ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| Used | Name | Pin | Pin | Name | Used | ++=============================+==========+=====+=====+==========+=============================+ +| NC | VBAT | 1 | 2 | VBAT | NC | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| GND | GND | 3 | 4 | GND | GND | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| 3V3 | 3V3 | 5 | 6 | 3V3 | 3V3 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| USB_P | USB_P | 7 | 8 | USB_N | USB_N | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| NC | VBUS | 9 | 10 | SW1 | P1.01 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.20 / UART0_TX | TXD0 | 11 | 12 | RXD0 | P0.19 / UART0_RX | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| RESET | RESET | 13 | 14 | LED1 | P1.03 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P1.04 | LED2 | 15 | 16 | LED3 | P0.02 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| 3V3 | VDD | 17 | 18 | VDD | 3V3 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.13 / I2C0_SDA | I2C1_SDA | 19 | 20 | I2C1_SCL | P0.14 / I2C0_SCL | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.05 / ADC_VBAT | AIN0 | 21 | 22 | AIN1 | P0.31 / ADC | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| BOOT | BOOT0 | 23 | 24 | IO7 | P0.28 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.26 / SPI2_CS | SPI_CS | 25 | 26 | SPI_CLK | P0.03 / SPI2_SCK | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.29 / SPI2_MISO | SPI_MISO | 27 | 28 | SPI_MOSI | P0.30 / SPI2_MOSI | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.17 | IO1 | 29 | 30 | IO2 | P1.02 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.21 | IO3 | 31 | 32 | IO4 | P0.04 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.16 / UART1_TX | TXD1 | 33 | 34 | RXD1 | P0.15 / UART1_RX | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.24 / I2C1_SDA | I2C2_SDA | 35 | 36 | I2C2_SCL | P0.25 / I2C1_SCL | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| P0.09 | IO5 | 37 | 38 | IO6 | P0.10 | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ +| GND | GND | 39 | 40 | GND | GND | ++-----------------------------+----------+-----+-----+----------+-----------------------------+ + +Signal names follow the WisBlock connector specification (see the WisBlock Technical +Specification v1.0). + +| Arduino pin | WisBlock signal | nRF52840 pin | Notes | +|-------------|-----------------|--------------|------------------------------------------| +| D0 | IO1 | P0.17 | PWM | +| D1 | IO2 | P1.02 | PWM; 3V3_S sensor-slot power enable | +| D2 | IO3 | P0.21 | PWM | +| D3 | IO4 | P0.04 | PWM, also A2 | +| D4 | IO5 | P0.09 | PWM (NFC pin, used as GPIO) | +| D5 | IO6 | P0.10 | PWM (NFC pin, used as GPIO) | +| D6 | IO7 | P0.28 | PWM, also A3 | +| D7 | LED1 (green) | P1.03 | PWM, `LED_BUILTIN` | +| D8 | LED2 (blue) | P1.04 | PWM | +| D9 | AIN0 | P0.05 | Also A0 | +| D10 | AIN1 | P0.31 | Also A1 | +| D11 | SPI_CS | P0.26 | SPI chip select | +| D12 | SW1 | P1.01 | WisBlock connector pin 10 | +| D13 | LED3 | P0.02 | Connector pin 16; shared with QSPI DIO3 | + +Buses: + - `Serial` is the USB CDC sketch port + - `Serial1` is UART0 (TXD0 P0.20 / RXD0 P0.19) + - `Serial2` is UART1 (TXD1 P0.16 / RXD1 P0.15) + - `Wire` is I2C0, the WisBlock I2C1 bus (SDA P0.13 / SCL P0.14) + - `SPI` is the WisBlock header SPI (SCK P0.03 / MOSI P0.30 / MISO P0.29) + +On the RAK19007 base board: + +- Sensor slots A-D get their switched 3V3_S rail through WB_IO2 (D1): + IO2=1 on, IO2=0 off. The loader drives it low at boot, so the rail is off until a + sketch enables it. Modules that only use VDD, such as the RAK1901, are unaffected. +- A0 is also wired to the battery voltage divider + (1.5 MOhm / 2.5 MOhm), so `VBAT = analogRead(A0)` scaled by 5/3 + (and by the ADC reference); IO-slot modules driving AIN0 conflict + with it. + +How to use MCUboot bootloader with arduino-cli: + +Requirement: arduino-cli uploads through MCUboot's serial recovery (mcumgr over +USB), so MCUboot must already be on the board. Before the first arduino-cli +upload, flash the MCUboot hex over SWD with a J-Link or a RAKDAP1 probe. The +core ships it as `firmwares/zephyr-rak4631_nrf52840.mcuboot.hex`, and +`extra/build.sh rak4631` writes it to the same path. arduino-cli cannot install +MCUboot itself. Images are signed with `key/arduino_key_ecdsa-p256.pem`, +which is public in this repository, so this does not provide secure boot. + +Entering serial recovery: `arduino-cli upload` first sends a 1200-bps touch to +the board's USB port. A running sketch handles it (the core opens `Serial` +before `setup()`); with no sketch on the board (or with a `--debug` loader) the +loader handles it. A sketch that crashes before USB comes up leaves no port for +the touch; recover the board over SWD. diff --git a/variants/rak4631_nrf52840/known_example_issues.txt b/variants/rak4631_nrf52840/known_example_issues.txt new file mode 100644 index 000000000..b9aee7130 --- /dev/null +++ b/variants/rak4631_nrf52840/known_example_issues.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +# This file contains a list of examples that are shipped with the core (or used +# by continuous integration tests), but are known to be currently failing for +# this specific variant. +# +# Each line in this file is treated as a regular expression and will be matched +# against the path of each sketch found in this repo. If a match is found, the +# sketch compilation result will be ignored. + diff --git a/variants/rak4631_nrf52840/postvariant.h b/variants/rak4631_nrf52840/postvariant.h new file mode 100644 index 000000000..c78c29718 --- /dev/null +++ b/variants/rak4631_nrf52840/postvariant.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include diff --git a/variants/rak4631_nrf52840/rak4631_nrf52840.conf b/variants/rak4631_nrf52840/rak4631_nrf52840.conf new file mode 100644 index 000000000..abb90be02 --- /dev/null +++ b/variants/rak4631_nrf52840/rak4631_nrf52840.conf @@ -0,0 +1,100 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_HCI_RAW_H4=y +CONFIG_BT_HCI_RAW_H4_ENABLE=y +CONFIG_BT_BUF_ACL_RX_SIZE=255 +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_MAX_CONN=4 +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_PERIODIC=y +CONFIG_BT_RX_STACK_SIZE=4096 + +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USB_DEVICE_PRODUCT="RAKwireless RAK4631" +CONFIG_USB_DEVICE_MANUFACTURER="RAKwireless" +# pid.codes test VID/PID; MCUboot enumerates as 0x0004 +CONFIG_USB_DEVICE_VID=0x1209 +CONFIG_USB_DEVICE_PID=0x0003 +CONFIG_USB_CONFIGURATION_STRING_DESC_ENABLE=n + +CONFIG_USBD_CDC_ACM_CLASS=y +# The loader brings USB up itself, once it knows whether this is a debug boot. +CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n +CONFIG_USBD_CDC_ACM_BUF_POOL=y +CONFIG_UART_LINE_CTRL=y +CONFIG_USBD_CDC_ACM_LOG_LEVEL_ERR=y +CONFIG_UDC_DRIVER_LOG_LEVEL_ERR=y +CONFIG_USBD_LOG_LEVEL_ERR=y + +# Keep optimizing for size. +CONFIG_SIZE_OPTIMIZATIONS=y + +# Boot mode (rak4631_nrf52840_bootmode.dtsi): the 1200 bps touch in +# variant.cpp (sketch) or loader/main.c (no sketch, debug shell) asks +# MCUboot for serial recovery through it. +CONFIG_RETAINED_MEM=y +CONFIG_RETENTION=y +CONFIG_RETENTION_BOOT_MODE=y +# sys_reboot() after the loader's 1200 bps touch +CONFIG_REBOOT=y + +CONFIG_GPIO=y +CONFIG_I2C=y +CONFIG_SPI=y +CONFIG_ADC=y +CONFIG_PWM=y +CONFIG_COUNTER=y + +CONFIG_LORA=y +CONFIG_LORAWAN=y +CONFIG_LORAWAN_REGION_AS923=y +CONFIG_LORAWAN_REGION_AU915=y +CONFIG_LORAWAN_REGION_CN470=y +CONFIG_LORAWAN_REGION_EU433=y +CONFIG_LORAWAN_REGION_EU868=y +CONFIG_LORAWAN_REGION_KR920=y +CONFIG_LORAWAN_REGION_IN865=y +CONFIG_LORAWAN_REGION_US915=y +CONFIG_LORAWAN_REGION_RU864=y +CONFIG_LORAWAN_SERVICES=n +CONFIG_LORAWAN_NVM_SETTINGS=y + +# NVS required to store LoRaWAN DevNonce +# as per LoRaWAN 1.0.4/1.1 Specifications. +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# Link the firmware image into zephyr,code-partition, +# instead of assuming it owns the whole flash device. +CONFIG_USE_DT_CODE_PARTITION=y + +CONFIG_NVS=y +CONFIG_SETTINGS=y + +CONFIG_HEAP_MEM_POOL_SIZE=16384 +CONFIG_LLEXT_HEAP_SIZE=128 +CONFIG_MAIN_STACK_SIZE=16384 + +CONFIG_FPU=y + +CONFIG_STACK_USAGE=n +CONFIG_STACK_SENTINEL=n +CONFIG_STACK_CANARIES=n + +# The board defconfig enables HW_STACK_PROTECTION, but the loader +# disables ARM_MPU (prj.conf), making it unsatisfiable. +CONFIG_HW_STACK_PROTECTION=n + +# Logging +CONFIG_LOG=y +CONFIG_LOG_PRINTK=y +CONFIG_LOG_BACKEND_RTT=n +CONFIG_LOG_BACKEND_UART=y +CONFIG_LOG_BACKEND_UART_AUTOSTART=n +CONFIG_LOG_DEFAULT_LEVEL=2 diff --git a/variants/rak4631_nrf52840/rak4631_nrf52840.overlay b/variants/rak4631_nrf52840/rak4631_nrf52840.overlay new file mode 100644 index 000000000..94d5d5a9d --- /dev/null +++ b/variants/rak4631_nrf52840/rak4631_nrf52840.overlay @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Flash layout, shared with the MCUboot image as well. */ +#include "rak4631_nrf52840_partitions.dtsi" +/* Boot-mode retention area, shared with the MCUboot image as well. */ +#include "rak4631_nrf52840_bootmode.dtsi" + +/ { + chosen { + zephyr,console = &board_cdc_acm_logging; + zephyr,log-uart = &log_uarts; + zephyr,shell-uart = &board_cdc_acm_shell; + zephyr,code-partition = &code_partition; + /delete-property/ zephyr,uart-mcumgr; + }; + + aliases { + /* LED_BUILTIN resolves to the green LED (D7). */ + led0 = &green_led; + }; + + log_uarts: log_uarts { + compatible = "zephyr,log-uart"; + uarts = <&board_cdc_acm_logging>; + }; +}; + +/* + * USB CDC-ACM ports, in enumeration order: + * 1. sketch: the sketch's 'Serial' (SerialUSB) + * 2. shell: Zephyr shell (debug builds only, unused otherwise) + * 3. logging: console (printk/stdout msg) + log backend (debug builds only, unused otherwise) + */ +&zephyr_udc0 { + board_cdc_acm_sketch: board_cdc_acm_sketch { + compatible = "zephyr,cdc-acm-uart"; + status = "okay"; + }; + + board_cdc_acm_shell: board_cdc_acm_shell { + compatible = "zephyr,cdc-acm-uart"; + status = "okay"; + }; + + board_cdc_acm_logging: board_cdc_acm_logging { + compatible = "zephyr,cdc-acm-uart"; + status = "okay"; + }; + + num-bidir-endpoints = <1>; + num-in-endpoints = <6>; + num-out-endpoints = <3>; + num-isoin-endpoints = <0>; + num-isoout-endpoints = <0>; +}; + +/* + * The RAK4631 LEDs are active high: green on P1.03 (D7), + * blue on P1.04 (D8). The base board declares them active low. + */ +&green_led { + gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; +}; + +&blue_led { + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; +}; + +/* + * WisBlock 3V3_S: IO2 (P1.02, WB_IO2 / D1) + * IO2=1 on, IO2=0 off. + */ +&gpio1 { + wb-3v3-s { + gpio-hog; + gpios = <2 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "3V3_S"; + }; +}; + +/* WB_IO5/WB_IO6 (P0.09/P0.10) are the NFC pins; use them as GPIOs. */ +&nfct { + status = "disabled"; +}; + +&uicr { + nfct-pins-as-gpios; +}; + +/* RTC backend for the Arduino RTC library */ +&rtc2 { + status = "okay"; +}; + +/* Shares the TWI1/SPI1 instance with the SX1262 SPI; keep disabled. */ +&i2c1 { + status = "disabled"; +}; + +/* + * The board enables QSPI on P0.03/P0.30/P0.29/P0.28/P0.02/P0.26, but + * these pins are repurposed as SPI2, A3/D6, the D11 CS GPIO and the + * D13 LED3 GPIO. + */ +&qspi { + status = "disabled"; + pinctrl-0 = <&qspi_default>; + pinctrl-1 = <&qspi_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* WisBlock header SPI: SCK P0.03, MOSI P0.30, MISO P0.29 */ +&spi2 { + compatible = "nordic,nrf-spi"; + status = "okay"; + cs-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi2_default>; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* Arduino API pin mappings */ +/ { + zephyr,user { + digital-pin-gpios = <&gpio0 17 0>, /* D0 - WB_IO1 */ + <&gpio1 2 0>, /* D1 - WB_IO2 */ + <&gpio0 21 0>, /* D2 - WB_IO3 */ + <&gpio0 4 0>, /* D3 - WB_IO4 */ + <&gpio0 9 0>, /* D4 - WB_IO5 */ + <&gpio0 10 0>, /* D5 - WB_IO6 */ + <&gpio0 28 0>, /* D6 - WB_IO7 (AIN4) */ + <&gpio1 3 0>, /* D7 - green LED */ + <&gpio1 4 0>, /* D8 - blue LED */ + <&gpio0 5 0>, /* D9 - WB_A0 (AIN3) */ + <&gpio0 31 0>, /* D10 - WB_A1 (AIN7) */ + <&gpio0 26 0>, /* D11 - WB_SPI_CS */ + <&gpio1 1 0>, /* D12 - WB_SW1 */ + <&gpio0 2 0>; /* D13 - WB_LED3 */ + + pwm-pin-gpios = <&gpio1 3 0>, /* D7 - green LED */ + <&gpio1 4 0>, /* D8 - blue LED */ + <&gpio0 17 0>, /* D0 - WB_IO1 */ + <&gpio1 2 0>, /* D1 - WB_IO2 */ + <&gpio0 21 0>, /* D2 - WB_IO3 */ + <&gpio0 4 0>, /* D3 - WB_IO4 */ + <&gpio0 9 0>, /* D4 - WB_IO5 */ + <&gpio0 10 0>, /* D5 - WB_IO6 */ + <&gpio0 28 0>; /* D6 - WB_IO7 */ + + /* + * WisBlock analog pins. On the RAK19007 base board, A0 + * (WB_A0/AIN0) is also wired to the battery voltage + * divider (1.5 MOhm / 2.5 MOhm): VBAT = A0 * 5 / 3. + */ + adc-pin-gpios = <&gpio0 5 0>, /* A0 - WB_A0 */ + <&gpio0 31 0>, /* A1 - WB_A1 */ + <&gpio0 4 0>, /* A2 - WB_IO4 (D3) */ + <&gpio0 28 0>; /* A3 - WB_IO7 (D6) */ + + /* + * The period cell is in nanoseconds. The PWM counter runs at + * 16 MHz, so one count is 62.5 ns and pwm_set_pulse_dt() turns + * both the period and the pulse into counts: + * + * counts = ns * 16000000 / 1000000000 = ns / 62.5 + * COUNTERTOP = 2000000 / 62.5 = 32000 (2 ms, 500 Hz) + * + * analogWrite(pin, v) asks for v/255 of the period, so: + * + * pulse_ns = v * period_ns / 255 + * COMPARE = pulse_ns / 62.5 + * duty = COMPARE / COUNTERTOP + * + * e.g. v = 64 -> 501960 ns -> COMPARE 8031 -> 25 %. + * + * COMPARE is a whole number, so the period must hold at least + * 255 counts (>= 16 us) for all 256 values of v to differ. + * 500 Hz has 32000 of them and stays well above flicker. + */ + pwms = <&pwm0 0 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm0 1 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm0 2 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm0 3 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm1 0 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm1 1 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm2 0 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm2 1 PWM_HZ(500) PWM_POLARITY_NORMAL>, + <&pwm2 2 PWM_HZ(500) PWM_POLARITY_NORMAL>; + + io-channels = <&adc 3>, /* A0 - WB_A0 (AIN3) */ + <&adc 7>, /* A1 - WB_A1 (AIN7) */ + <&adc 2>, /* A2 - WB_IO4 (AIN2) */ + <&adc 4>; /* A3 - WB_IO7 (AIN4) */ + + i2cs = <&i2c0>; /* 'Wire': WisBlock sensor I2C (SDA P0.13, SCL P0.14) */ + spis = <&spi2>; /* 'SPI': WisBlock header SPI */ + + cdc-acm-serial = <&board_cdc_acm_sketch>; /* 'Serial' object is managed by SerialUSB */ + serials = <&uart0>, <&uart1>; + }; +}; + +&adc { + #address-cells = <1>; + #size-cells = <0>; + + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* P0.05 - WB_A0 */ + zephyr,resolution = <10>; + }; + + channel@7 { + reg = <7>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* P0.31 - WB_A1 */ + zephyr,resolution = <10>; + }; + + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* P0.04 - WB_IO4 */ + zephyr,resolution = <10>; + }; + + channel@4 { + reg = <4>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* P0.28 - WB_IO7 */ + zephyr,resolution = <10>; + }; +}; + +&pwm0 { + status = "okay"; + zephyr,deferred-init; + pinctrl-0 = <&empty_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-2 = <&pwm0_default>; + pinctrl-names = "default", "sleep", "arduino"; +}; + +&pwm1 { + status = "okay"; + zephyr,deferred-init; + pinctrl-0 = <&empty_default>; + pinctrl-1 = <&pwm1_sleep>; + pinctrl-2 = <&pwm1_default>; + pinctrl-names = "default", "sleep", "arduino"; +}; + +&pwm2 { + status = "okay"; + zephyr,deferred-init; + pinctrl-0 = <&empty_default>; + pinctrl-1 = <&pwm2_sleep>; + pinctrl-2 = <&pwm2_default>; + pinctrl-names = "default", "sleep", "arduino"; +}; + +&pinctrl { + empty_default: empty_default { + group1 { + psels = <>; + }; + }; + + pwm0_default: pwm0_default { + group1 { + psels = , + , + , + ; + }; + }; + + pwm0_sleep: pwm0_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + pwm1_default: pwm1_default { + group1 { + psels = , + ; + }; + }; + + pwm1_sleep: pwm1_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + pwm2_default: pwm2_default { + group1 { + psels = , + , + ; + }; + }; + + pwm2_sleep: pwm2_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + spi2_default: spi2_default { + group1 { + psels = , + , + ; + }; + }; + + spi2_sleep: spi2_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + qspi_default: qspi_default { + group1 { + psels = , + , + , + , + , + ; + }; + }; + + qspi_sleep: qspi_sleep { + group1 { + psels = , + , + , + , + , + ; + low-power-enable; + }; + }; +}; diff --git a/variants/rak4631_nrf52840/rak4631_nrf52840_bootmode.dtsi b/variants/rak4631_nrf52840/rak4631_nrf52840_bootmode.dtsi new file mode 100644 index 000000000..e39f419d9 --- /dev/null +++ b/variants/rak4631_nrf52840/rak4631_nrf52840_bootmode.dtsi @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Boot-mode retention area, shared by the loader and the MCUboot image. + * + * It lives in the last 1 KB of SRAM. RAM keeps its content across pin and + * software resets and loses it on a power cycle. + */ + +/ { + chosen { + zephyr,boot-mode = &boot_mode0; + }; + + sram@2003fc00 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x2003fc00 0x400>; + ranges = <0x0 0x2003fc00 0x400>; + zephyr,memory-region = "BootMode"; + status = "okay"; + #address-cells = <1>; + #size-cells = <1>; + + bootmode_retained_mem: bootmode_retained_mem { + compatible = "zephyr,retained-ram"; + status = "okay"; + ranges; + #address-cells = <1>; + #size-cells = <1>; + + /* 2-byte prefix + 1 boot-mode byte + CRC-8 */ + boot_mode0: retention@0 { + compatible = "zephyr,retention"; + status = "okay"; + reg = <0x0 0x4>; + prefix = [52 4b]; + checksum = <1>; + }; + }; + }; +}; + +/* Hand the last 1 KB to the retained area above. */ +&sram0 { + reg = <0x20000000 DT_SIZE_K(255)>; +}; diff --git a/variants/rak4631_nrf52840/rak4631_nrf52840_partitions.dtsi b/variants/rak4631_nrf52840/rak4631_nrf52840_partitions.dtsi new file mode 100644 index 000000000..3c88ac5d7 --- /dev/null +++ b/variants/rak4631_nrf52840/rak4631_nrf52840_partitions.dtsi @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * RAK4631 flash layout, shared by the loader and the MCUboot image. + * + * MCUboot and the mcumgr image group address the slots through the + * slot0_partition / slot1_partition labels; the loader addresses the same + * partitions as code_partition / user_sketch. + */ + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x00010000>; + read-only; + }; + + /* Loader; the MCUboot image header takes its first 0x200 bytes. */ + slot0_partition: code_partition: partition@10000 { + label = "image-0"; + reg = <0x00010000 0x00070000>; + read-only; + }; + + /* + * Sketch: a zsk blob, not an MCUboot image. The slot1 label only + * makes it "image 2" for mcumgr direct uploads. + */ + slot1_partition: user_sketch: partition@80000 { + label = "image-1"; + reg = <0x00080000 0x00078000>; + }; + + /* + * Storage partition will be used by FCB/LittleFS/NVS + * if enabled. + */ + storage_partition: partition@f8000 { + label = "storage"; + reg = <0x000f8000 0x00008000>; + }; + }; +}; diff --git a/variants/rak4631_nrf52840/test_setup.sh b/variants/rak4631_nrf52840/test_setup.sh new file mode 100644 index 000000000..0443a02d1 --- /dev/null +++ b/variants/rak4631_nrf52840/test_setup.sh @@ -0,0 +1,21 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +# This script is sourced by extra/ci_apply_tests.sh to customize the CI test +# list for this board variant. Three helper functions are available: +# get_branch_tip [ ...] +# get_latest_release [ ...] +# skip_for_this_board +# +# get_branch_tip / get_latest_release add external repos to the test list +# (the repos must have been downloaded by ci_fetch_tests.sh first). +# skip_for_this_board removes all tests under the given path prefix. + +skip_for_this_board libraries/Arduino_SecureElement +skip_for_this_board libraries/Camera +skip_for_this_board libraries/Ethernet +skip_for_this_board libraries/Storage +skip_for_this_board libraries/WiFi +skip_for_this_board libraries/Zephyr_SDRAM +skip_for_this_board libraries/CAN diff --git a/variants/rak4631_nrf52840/variant.h b/variants/rak4631_nrf52840/variant.h new file mode 100644 index 000000000..3f960c1d3 --- /dev/null +++ b/variants/rak4631_nrf52840/variant.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This header only provides the BSP-compatible names! */ + +#ifndef _VARIANT_RAK4631_ +#define _VARIANT_RAK4631_ + +/* + * Internal Pins + * + * RAK4630 module-internal signals. They are not routed to the WisBlock + * connector and have no Arduino pin number, so a sketch cannot (and must + * not) drive them: the SX1262 radio is owned by the Zephyr 'lora0' device + * on SPI1, and the low-frequency crystal by the clock control driver. + * + * P0.00 XL1 32.768 kHz crystal + * P0.01 XL2 32.768 kHz crystal + * P1.05 ANT_SW SX1262 RX enable, RF switch + * P1.06 NRESET SX1262 reset + * P1.07 DIO2 SX1262 TX enable (driven by the radio itself) + * P1.10 SPI_NSS SX1262 chip select (active low) + * P1.11 SPI_CLK SX1262 SPI clock + * P1.12 SPI_MOSI SX1262 SPI MOSI + * P1.13 SPI_MISO SX1262 SPI MISO + * P1.14 BUSY SX1262 busy + * P1.15 DIO1 SX1262 interrupt + */ + +/* + * Connector Pins + * + * WisBlock connector signals without an Arduino pin number: the bus + * pins are set up by devicetree pin control and belong to a Zephyr + * peripheral, the rest are not mapped by this variant. The number in + * brackets is the connector pin. + * + * UART0 ('Serial1'): P0.20 TXD0, P0.19 RXD0 + * UART1 ('Serial2'): P0.16 TXD1, P0.15 RXD1 + * I2C0 ('Wire'): P0.13 I2C1_SDA, P0.14 I2C1_SCL + * I2C1 (disabled): - + * SPI2 ('SPI'): P0.03 SPI_CLK, P0.30 SPI_MOSI, + * P0.29 SPI_MISO, P0.26 SPI_CS + * QSPI: P0.03 SCK, P0.30 DIO0, P0.29 DIO1, P0.28 DIO2, + * P0.02 DIO3, P0.26 CSN + * ADC: P0.05 A0, P0.31 A1, P0.04 A2, P0.28 A3 + * Others: P1.01 SW1, P1.02 SW2, P0.18 RESET + * + * I2C1 is disabled because it shares the TWI1/SPI1 hardware instance + * with the SX1262 SPI. QSPI shares its pins with SPI2, with A3/D6 (P0.28) + * and with LED3/D13 (P0.02), so QSPI and the WisBlock SPI cannot be used + * at the same time. P0.18 is the nRF52840 reset line (UICR 'gpio-as-nreset'), + * not a usable GPIO. + */ + +/* + * WisBlock Base GPIO definitions + */ +#define WB_IO1 D0 /* P0.17, SLOT_A SLOT_B IO_SLOT, PWM */ +#define WB_IO2 D1 /* P1.02, SLOT_A SLOT_B IO_SLOT, PWM, 3V3_S power enable */ +#define WB_IO3 D2 /* P0.21, SLOT_C IO_SLOT, PWM */ +#define WB_IO4 D3 /* P0.04, SLOT_C IO_SLOT, PWM, also A2 */ +#define WB_IO5 D4 /* P0.09, SLOT_D IO_SLOT, PWM (NFC pin used as GPIO) */ +#define WB_IO6 D5 /* P0.10, SLOT_D IO_SLOT, PWM (NFC pin used as GPIO) */ +#define WB_IO7 D6 /* P0.28, IO_SLOT, PWM, also A3 */ +#define WB_LED1 D7 /* P1.03, green LED, PWM */ +#define WB_LED2 D8 /* P1.04, blue LED, PWM */ +#define WB_A0 D9 /* P0.05, IO_SLOT */ +#define WB_A1 D10 /* P0.31, IO_SLOT */ +#define WB_SPI_CS D11 /* P0.26, SLOT_A SLOT_B SLOT_C SLOT_D IO_SLOT */ +#define WB_SW1 D12 /* P1.01, IO_SLOT */ +#define WB_LED3 D13 /* P0.02, base board LED3, shared with QSPI DIO3 */ + +/* + * LEDs (active high) + * + * LED1/LED2 are fitted on the RAK4631 module; LED3 is only a connector + * signal, so its polarity depends on the base board. + */ +#define PIN_LED1 WB_LED1 +#define PIN_LED2 WB_LED2 +#define PIN_LED3 WB_LED3 + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 +#define GREEN_LED PIN_LED1 +#define BLUE_LED PIN_LED2 + +#define LED_STATE_ON 1 + +/* + * Analog pins (A0..A3 map to SAADC channels 3, 7, 2, 4) + */ +#define PIN_A0 A0 /* WB_A0 */ +#define PIN_A1 A1 /* WB_A1 */ +#define PIN_A2 A2 /* WB_IO4 */ +#define PIN_A3 A3 /* WB_IO7 */ + +/* + * On the RAK19007 base board, A0 also reads the battery voltage + * divider (1.5 MOhm / 2.5 MOhm): VBAT = A0 * 5 / 3. + */ +#define PIN_VBAT PIN_A0 + +/* + * SPI interfaces + */ +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_CS WB_SPI_CS +#define SS PIN_SPI_CS + +/* + * Wire interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#endif /* _VARIANT_RAK4631_ */ From 35df2df380e47faa7c9d172095b95914a927af45 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 29 Sep 2026 17:16:14 +0300 Subject: [PATCH 06/15] upload: add the mcumgr tool Signed-off-by: Sercan Erat --- platform.txt | 34 ++++++++++++++++++++++++++++++++-- programmers.txt | 8 ++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/platform.txt b/platform.txt index 4eee89658..8548fc09f 100644 --- a/platform.txt +++ b/platform.txt @@ -62,7 +62,7 @@ compiler.zephyr.common_ldflags=-fno-exceptions -fno-rtti -fno-threadsafe-statics compiler.zephyr.extra_cxxflags= compiler.zephyr.extra_ldflags=-lstdc++ -lsupc++ -lm -lgcc -lc -# these can be overriden in boards.txt +# these can be overridden in boards.txt build.extra_flags= build.extra_ldflags= build.link_mode=dynamic @@ -106,7 +106,7 @@ compiler.objcopy.eep.extra_flags= compiler.elf2hex.extra_flags= # {build.library_discovery_phase} is set to 1 by the builder during library discovery. -# (this is available since arduino-builder>=1.5.5, keeping the default here for backward compatiblity) +# (this is available since arduino-builder>=1.5.5, keeping the default here for backward compatibility) build.library_discovery_phase=0 build.library_discovery_phase_flag=-DARDUINO_LIBRARY_DISCOVERY_PHASE={build.library_discovery_phase} @@ -368,6 +368,36 @@ tools.jlink.upload.params.quiet= tools.jlink.upload.pattern="{path}/{cmd}" -CommanderScript "{build.path}/flasher.jlink" +# +# MCUmgr WRAPPER +# + +tools.mcumgr.path={runtime.tools.mcumgr.path} +tools.mcumgr.cmd=mcumgr +tools.mcumgr.cmd.windows=mcumgr.exe +tools.mcumgr.conn=--conntype serial --connstring "dev={serial.port},baud=115200,mtu=4096" + +tools.mcumgr.loader.cmd="{path}/{cmd}" {conn} image upload -n 1 "{upload.artifacts.loader}" +tools.mcumgr.sketch.cmd="{path}/{cmd}" {conn} image upload -n 2 "{upload.artifacts.sketch}" +tools.mcumgr.reset.cmd="{path}/{cmd}" {conn} reset + +# arduino-cli never sends the 1200-bps touch for burn-bootloader, so the +# bootloader pattern sends it itself and waits for MCUboot's port. +tools.mcumgr.touch.cmd=stty -F {serial.port} 1200 +tools.mcumgr.touch.cmd.macosx=stty -f {serial.port} 1200 +tools.mcumgr.touch.cmd.windows=mode {serial.port} BAUD=1200 +tools.mcumgr.wait.cmd=sleep 2 +tools.mcumgr.wait.cmd.windows=powershell -NoProfile -Command Start-Sleep -Seconds 2 + +tools.mcumgr.upload.params.verbose= +tools.mcumgr.upload.params.quiet= +tools.mcumgr.upload.pattern={tools.patterns.flash-loader-and-sketch} :: run {reset.cmd} + +tools.mcumgr.bootloader.params.verbose= +tools.mcumgr.bootloader.params.quiet= +tools.mcumgr.bootloader.pattern="{runtime.tools.scripting-tools.path}/scripting-tools" run {touch.cmd} :: run {wait.cmd} :: run {loader.cmd} :: run {reset.cmd} + + # # Debugger # diff --git a/programmers.txt b/programmers.txt index e84569ceb..133c83d10 100644 --- a/programmers.txt +++ b/programmers.txt @@ -21,3 +21,11 @@ openocd.protocol=SWD openocd.program.tool=openocd openocd.program.tool.default=openocd openocd.program.extra_params= + +mcumgr.name=MCUmgr +mcumgr.communication=serial +mcumgr.protocol= +mcumgr.program.protocol= +mcumgr.program.tool=mcumgr +mcumgr.program.tool.default=mcumgr +mcumgr.program.extra_params= From 5f312e748621899b18b76efe75613a4a6f933c65 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 29 Sep 2026 17:16:14 +0300 Subject: [PATCH 07/15] loader: add sysbuild support and RAK4631 Wisblock core related symbols Signed-off-by: Sercan Erat --- boards.txt | 53 +++++++++++++++ extra/build.sh | 61 +++++++++++++---- key/arduino_key_ecdsa-p256.pem | 5 ++ loader/CMakeLists.txt | 4 +- loader/llext_exports.c | 29 ++++++++ loader/main.c | 66 ++++++++++++++++++- loader/overlay_mcuboot.conf | 11 ++++ loader/sysbuild.conf | 14 ++++ .../mcuboot/boards/rak4631_nrf52840.conf | 17 +++++ .../mcuboot/boards/rak4631_nrf52840.overlay | 42 ++++++++++++ loader/sysbuild/mcuboot/prj.conf | 48 ++++++++++++++ variants/rak4631_nrf52840/variant.cpp | 14 ++++ 12 files changed, 349 insertions(+), 15 deletions(-) create mode 100644 key/arduino_key_ecdsa-p256.pem create mode 100644 loader/overlay_mcuboot.conf create mode 100644 loader/sysbuild.conf create mode 100644 loader/sysbuild/mcuboot/boards/rak4631_nrf52840.conf create mode 100644 loader/sysbuild/mcuboot/boards/rak4631_nrf52840.overlay create mode 100644 loader/sysbuild/mcuboot/prj.conf create mode 100644 variants/rak4631_nrf52840/variant.cpp diff --git a/boards.txt b/boards.txt index 798564387..99ca1765c 100644 --- a/boards.txt +++ b/boards.txt @@ -19,6 +19,7 @@ menu.debug=Debug menu.link_mode=Link mode menu.wait_linux_boot=Startup mode +menu.baseboard=WisBlock Base ########################################################################################## @@ -727,3 +728,55 @@ ventunoq.bootloader.tool=remoteocd ventunoq.bootloader.tool.default=remoteocd ventunoq.bootloader.file=zephyr-{build.variant}.elf ventunoq.bootloader.target=stm32h5f5xx + +########################################################################################## + +rak4631.name=RAKwireless RAK4631 +rak4631.build.core=arduino +rak4631.build.crossprefix=arm-zephyr-eabi- +rak4631.build.compiler_path={runtime.tools.arm-zephyr-eabi-1.0.1.path}/bin/ + +rak4631.menu.debug.false=Standard +rak4631.menu.debug.true=Debug +rak4631.menu.debug.true.build.zsk_args.debug=-debug + +rak4631.menu.link_mode.dynamic=Dynamic +rak4631.menu.link_mode.static=Static +rak4631.menu.link_mode.static.build.link_mode=static +rak4631.menu.link_mode.static.upload.extension=bin-zsk.bin + +# Sketches select the pinout with the matching WISBLOCK_BASE_* macro. +rak4631.menu.baseboard.none=None (no baseboard macro) +rak4631.menu.baseboard.rak19007=RAK19007 +rak4631.menu.baseboard.rak19007.build.extra_flags=-DWISBLOCK_BASE_19007 + +rak4631.build.zephyr_target=rak4631/nrf52840 +rak4631.build.zephyr_args=--sysbuild +rak4631.build.zephyr_hals=hal_nordic +rak4631.build.zephyr_toolchain=arm-zephyr-eabi +rak4631.build.variant=rak4631_nrf52840 + +rak4631.build.extra_flags= +rak4631.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +rak4631.build.board=RAK4631 +rak4631.vid.0=0x1209 +rak4631.pid.0=0x0003 +rak4631.upload_port.0.vid=0x1209 +rak4631.upload_port.0.pid=0x0003 +rak4631.upload_port.1.vid=0x1209 +rak4631.upload_port.1.pid=0x0004 +rak4631.upload.target=nrf52840 + +rak4631.upload.tool=mcumgr +rak4631.upload.tool.default=mcumgr +rak4631.upload.protocol= +rak4631.upload.transport= +rak4631.upload.use_1200bps_touch=true +rak4631.upload.wait_for_upload_port=true +rak4631.upload.native_usb=true + +rak4631.bootloader.tool=mcumgr +rak4631.bootloader.tool.default=mcumgr +rak4631.bootloader.file=zephyr-{build.variant}.bin + +########################################################################################## diff --git a/extra/build.sh b/extra/build.sh index b22b5e113..5583c7647 100755 --- a/extra/build.sh +++ b/extra/build.sh @@ -60,9 +60,19 @@ if ! [ -z "$chosen_board" ]; then fi upload_offset=$(jq -cr '.upload_offset' <<< "$chosen_board") + # Boards with '--sysbuild' in their zephyr_args boot the loader through MCUboot + conf="" + if [[ " ${args[*]} " == *" --sysbuild "* ]]; then + mcuboot=1 + conf="overlay_mcuboot.conf" + fi + # Check for debug flag and append if [ x$2 == x"--debug" ]; then - args+=(-- -DEXTRA_CONF_FILE=../extra/debug.conf) + conf="${conf:+$conf;}../extra/debug.conf" + fi + if [ -n "$conf" ]; then + args+=(-- "-DEXTRA_CONF_FILE=$conf") fi else # expect Zephyr-compatible target and args @@ -100,12 +110,17 @@ fi BUILD_DIR=build/${variant} VARIANT_DIR=variants/${variant} rm -rf ${BUILD_DIR} -west build -d ${BUILD_DIR} -b ${target} loader -t llext-edk "${args[@]}" +west build -d ${BUILD_DIR} -b ${target} loader "${args[@]}" + +# With sysbuild the loader is one image among several and has its own build +# directory as loader; the EDK target only exists there. +LOADER_DIR=${BUILD_DIR}${mcuboot:+/loader} +west build -d ${LOADER_DIR} -t llext-edk # Extract the generated EDK tarball and copy it to the variant directory mkdir -p ${VARIANT_DIR} firmwares -(set -e ; cd ${BUILD_DIR} && rm -rf llext-edk && tar xf zephyr/llext-edk.tar.Z) -rsync -a --delete ${BUILD_DIR}/llext-edk ${VARIANT_DIR}/ +(set -e ; cd ${LOADER_DIR} && rm -rf llext-edk && tar xf zephyr/llext-edk.tar.Z) +rsync -a --delete ${LOADER_DIR}/llext-edk ${VARIANT_DIR}/ # remove all inline comments in macro definitions # (especially from devicetree_generated.h and sys/util_internal.h) @@ -123,20 +138,40 @@ edk_funcs='int32_t k_sleep\(k_timeout_t timeout\)|void k_busy_wait\(uint32_t use perl -0pi -e "s/__pinned_func\nstatic inline ($edk_funcs)/$edk_qual \$1/g" "$syscalls_hdr" perl -0pi -e "s/__syscall ($edk_funcs);/$edk_qual \$1;/g" "$kernel_hdr" +IMG=${LOADER_DIR}/zephyr/zephyr +# a signed build that produced no signed bin/hex would brick the board, so bail +# out before touching firmwares/ +if [ -n "$mcuboot" ]; then + for ext in bin hex; do + if [ ! -f $IMG.signed.$ext ]; then + echo "error: MCUboot build produced no $IMG.signed.$ext" >&2 + exit 1 + fi + done +fi for ext in elf bin hex uf2; do - rm -f firmwares/zephyr-$variant.$ext - if [ -f ${BUILD_DIR}/zephyr/zephyr.$ext ]; then - cp ${BUILD_DIR}/zephyr/zephyr.$ext firmwares/zephyr-$variant.$ext - fi + rm -f firmwares/zephyr-$variant.$ext + # under MCUboot only the signed image boots, so prefer it when it exists + for src in $IMG.signed.$ext $IMG.$ext; do + if [ -f $src ]; then + cp $src firmwares/zephyr-$variant.$ext + break + fi + done done -cp ${BUILD_DIR}/zephyr/zephyr.dts firmwares/zephyr-$variant.dts -cp ${BUILD_DIR}/zephyr/.config firmwares/zephyr-$variant.config +# named zephyr-$variant.* so that package_core.sh ships it with the core +rm -f firmwares/zephyr-$variant.mcuboot.hex +if [ -n "$mcuboot" ]; then + cp ${BUILD_DIR}/mcuboot/zephyr/zephyr.hex firmwares/zephyr-$variant.mcuboot.hex +fi +cp ${LOADER_DIR}/zephyr/zephyr.dts firmwares/zephyr-$variant.dts +cp ${LOADER_DIR}/zephyr/.config firmwares/zephyr-$variant.config # Generate the provides.ld file for linked builds echo "Generating exported symbol scripts" -extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -T > ${VARIANT_DIR}/tls-syms.S -extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -L > ${VARIANT_DIR}/syms-dynamic.ld -extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -LF \ +extra/gen_provides.py "${LOADER_DIR}/zephyr/zephyr.elf" -T > ${VARIANT_DIR}/tls-syms.S +extra/gen_provides.py "${LOADER_DIR}/zephyr/zephyr.elf" -L > ${VARIANT_DIR}/syms-dynamic.ld +extra/gen_provides.py "${LOADER_DIR}/zephyr/zephyr.elf" -LF \ "+kheap_llext_heap" \ "+kheap__system_heap" \ "*sketch_base_addr=_sketch_start" \ diff --git a/key/arduino_key_ecdsa-p256.pem b/key/arduino_key_ecdsa-p256.pem new file mode 100644 index 000000000..10a8119ac --- /dev/null +++ b/key/arduino_key_ecdsa-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoDdEoVMhi3aY27HX +cxBaM5+nqp9tNbm0Q4PHywRo3HuhRANCAATEGlLJf8OiiVl0DJcddiM2JuNPlraW +AV6J6SbzclJna5YXFKSKPk8JwHhQ81d4nFFzRJKnoHQ8XcwTl3X0PAvC +-----END PRIVATE KEY----- diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 5e6f2c9f2..6681ca51a 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -5,7 +5,9 @@ cmake_minimum_required(VERSION 3.20.0) # get value of NORMALIZED_BOARD_TARGET early -find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} COMPONENTS yaml boards) +# 'basic_settings' must load before 'boards' so the board lookup works under +# sysbuild. +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} COMPONENTS basic_settings yaml boards) set(DTC_OVERLAY_FILE ${CMAKE_CURRENT_LIST_DIR}/../variants/${NORMALIZED_BOARD_TARGET}/${NORMALIZED_BOARD_TARGET}.overlay) set(EXTRA_CONF_FILE ${CMAKE_CURRENT_LIST_DIR}/../variants/${NORMALIZED_BOARD_TARGET}/${NORMALIZED_BOARD_TARGET}.conf) diff --git a/loader/llext_exports.c b/loader/llext_exports.c index 4c2c6e8a9..bab9c846c 100644 --- a/loader/llext_exports.c +++ b/loader/llext_exports.c @@ -528,4 +528,33 @@ FORCE_EXPORT_SYM(regulator_enable); FORCE_EXPORT_SYM(regulator_disable); #endif +#if defined(CONFIG_LORAWAN) +FORCE_EXPORT_SYM(lorawan_start); +FORCE_EXPORT_SYM(lorawan_join); +FORCE_EXPORT_SYM(lorawan_send); +FORCE_EXPORT_SYM(lorawan_set_region); +FORCE_EXPORT_SYM(lorawan_set_class); +FORCE_EXPORT_SYM(lorawan_set_datarate); +FORCE_EXPORT_SYM(lorawan_set_channels_mask); +FORCE_EXPORT_SYM(lorawan_set_conf_msg_tries); +FORCE_EXPORT_SYM(lorawan_enable_adr); +FORCE_EXPORT_SYM(lorawan_get_min_datarate); +FORCE_EXPORT_SYM(lorawan_get_payload_sizes); +FORCE_EXPORT_SYM(lorawan_request_link_check); +FORCE_EXPORT_SYM(lorawan_request_device_time); +FORCE_EXPORT_SYM(lorawan_device_time_get); +FORCE_EXPORT_SYM(lorawan_register_battery_level_callback); +FORCE_EXPORT_SYM(lorawan_register_downlink_callback); +FORCE_EXPORT_SYM(lorawan_register_dr_changed_callback); +FORCE_EXPORT_SYM(lorawan_register_link_check_ans_callback); +#endif + +#if defined(CONFIG_RETENTION_BOOT_MODE) +FORCE_EXPORT_SYM(bootmode_set); +#endif + +#if defined(CONFIG_REBOOT) +FORCE_EXPORT_SYM(sys_reboot); +#endif + EXPORT_SYMBOL(sketch_header_v1_verify); diff --git a/loader/main.c b/loader/main.c index 92a8204db..8271f858b 100644 --- a/loader/main.c +++ b/loader/main.c @@ -25,6 +25,11 @@ LOG_MODULE_REGISTER(sketch); #include #include "../cores/arduino/zephyr_sketch_header.h" +#if defined(CONFIG_RETENTION_BOOT_MODE) +#include +#include +#endif + #define SKETCH_RAM_BUFFER_LEN 131072 /* Need to replicate logic from zephyrSerial.h to avoid C++ here */ @@ -59,6 +64,21 @@ static void loader_usb_msg_cb(struct usbd_context *const ctx, const struct usbd_ usbd_enable(ctx); } } + +#if defined(CONFIG_RETENTION_BOOT_MODE) + /* 1200-bps touch while the loader owns USB for no sketch yet case. */ + if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING && msg->dev == usb_dev) { + uint32_t baudrate = 0; + + uart_line_ctrl_get(usb_dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); + if (baudrate == 1200) { + k_sleep(K_MSEC(100)); + usbd_disable(ctx); + bootmode_set(BOOT_MODE_TYPE_BOOTLOADER); + sys_reboot(SYS_REBOOT_COLD); + } + } +#endif } int loader_usb_enable(void) { @@ -77,6 +97,20 @@ int loader_usb_enable(void) { } #if CONFIG_SHELL +/* + * If the zephyr,shell-uart chosen device is a CDC-ACM port of its own, not the + * sketch's 'Serial' port, the shell stays there. Otherwise it is moved to the + * sketch's USB port once USB is enabled. + */ +#define SHELL_ON_OWN_CDC_PORT \ + (DT_HAS_CHOSEN(zephyr_shell_uart) && \ + DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_shell_uart), zephyr_cdc_acm_uart) && \ + !DT_SAME_NODE(DT_CHOSEN(zephyr_shell_uart), \ + DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), cdc_acm_serial, 0))) + +#if SHELL_ON_OWN_CDC_PORT +static const struct device *const shell_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart)); +#else static int enable_shell_usb(void) { bool log_backend = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > 0; uint32_t level = (CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > LOG_LEVEL_DBG) ? @@ -90,6 +124,7 @@ static int enable_shell_usb(void) { } #endif #endif +#endif #ifdef CONFIG_USERSPACE K_THREAD_STACK_DEFINE(llext_stack, CONFIG_MAIN_STACK_SIZE); @@ -155,7 +190,36 @@ static int loader(const struct shell *sh) { #if ZARD_FIRST_SERIAL_IS_SERIALUSB int debug = (!sketch_valid) || (sketch_hdr->flags & SKETCH_FLAG_DEBUG); -#if CONFIG_SHELL +#if CONFIG_SHELL && SHELL_ON_OWN_CDC_PORT + if (strcmp(k_thread_name_get(k_current_get()), "main") == 0) { + // the shell already runs on its own port, only enable USB + loader_usb_enable(); + if (debug) { + int dtr; + do { + // wait for the shell port to open + uart_line_ctrl_get(shell_dev, UART_LINE_CTRL_DTR, &dtr); + k_sleep(K_MSEC(100)); + } while (!dtr); + LOG_INF("shell: port open (DTR set)"); + } + } +#if CONFIG_LOG + for (int i = 0; i < log_backend_count_get(); i++) { + const struct log_backend *backend; + backend = log_backend_get(i); + if (log_backend_is_active(backend)) { + // autostarted or already managed by the shell + continue; + } + log_backend_init(backend); + log_backend_enable(backend, backend->cb->ctx, CONFIG_LOG_DEFAULT_LEVEL); + if (!debug) { + break; + } + } +#endif +#elif CONFIG_SHELL if (strcmp(k_thread_name_get(k_current_get()), "main") == 0) { // disables default shell on UART shell_uninit(shell_backend_uart_get_ptr(), NULL); diff --git a/loader/overlay_mcuboot.conf b/loader/overlay_mcuboot.conf new file mode 100644 index 000000000..2d1b05e54 --- /dev/null +++ b/loader/overlay_mcuboot.conf @@ -0,0 +1,11 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +# Sysbuild always passes CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE to the loader; +# the symbol only exists with the image manager (library only, no mcumgr). +# Enable flash operations for footer. +CONFIG_IMG_MANAGER=y +CONFIG_STREAM_FLASH=y + +CONFIG_BOOTLOADER_MCUBOOT=y diff --git a/loader/sysbuild.conf b/loader/sysbuild.conf new file mode 100644 index 000000000..54def7e9c --- /dev/null +++ b/loader/sysbuild.conf @@ -0,0 +1,14 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +# Enable MCUboot bootloader support +SB_CONFIG_BOOTLOADER_MCUBOOT=y + +# Use your own key at your production code! +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${APP_DIR}/../key/arduino_key_ecdsa-p256.pem" +SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +# Serial recovery writes the loader straight into slot0 +# and the sketch straight into slot1; no swap ever runs. +SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y diff --git a/loader/sysbuild/mcuboot/boards/rak4631_nrf52840.conf b/loader/sysbuild/mcuboot/boards/rak4631_nrf52840.conf new file mode 100644 index 000000000..06e801b2d --- /dev/null +++ b/loader/sysbuild/mcuboot/boards/rak4631_nrf52840.conf @@ -0,0 +1,17 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_SERIAL_UART=n +CONFIG_BOOT_SERIAL_CDC_ACM=y + +CONFIG_WARN_DEPRECATED=n + +# USB identity of the bootloader; a different PID from the loader (0x0003). +CONFIG_USB_DEVICE_PRODUCT="RAKwireless RAK4631" +CONFIG_USB_DEVICE_MANUFACTURER="RAKwireless" +CONFIG_USB_DEVICE_VID=0x1209 +CONFIG_USB_DEVICE_PID=0x0004 +# MCUboot enables USB itself when it enters recovery. +CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n +CONFIG_USB_CDC_ACM_RINGBUF_SIZE=4096 diff --git a/loader/sysbuild/mcuboot/boards/rak4631_nrf52840.overlay b/loader/sysbuild/mcuboot/boards/rak4631_nrf52840.overlay new file mode 100644 index 000000000..aee2b840d --- /dev/null +++ b/loader/sysbuild/mcuboot/boards/rak4631_nrf52840.overlay @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Same flash layout as the loader. */ +#include "../../../../variants/rak4631_nrf52840/rak4631_nrf52840_partitions.dtsi" +/* Same boot-mode retention area as the loader. */ +#include "../../../../variants/rak4631_nrf52840/rak4631_nrf52840_bootmode.dtsi" + +/ { + aliases { + mcuboot-led0 = &blue_led; + }; +}; + +/ { + chosen { + zephyr,code-partition = &boot_partition; + zephyr,uart-mcumgr = &board_cdc_acm_mcuboot; + }; +}; + +&zephyr_udc0 { + board_cdc_acm_mcuboot: board_cdc_acm_mcuboot { + compatible = "zephyr,cdc-acm-uart"; + status = "okay"; + }; +}; + +/* + * The RAK4631 LEDs are active high: green on P1.03 (D7), + * blue on P1.04 (D8). The base board declares them active low. + */ +&green_led { + gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; +}; + +&blue_led { + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; +}; diff --git a/loader/sysbuild/mcuboot/prj.conf b/loader/sysbuild/mcuboot/prj.conf new file mode 100644 index 000000000..c8894216c --- /dev/null +++ b/loader/sysbuild/mcuboot/prj.conf @@ -0,0 +1,48 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_MAIN_STACK_SIZE=10240 + +# Enable Serial Recovery +CONFIG_MCUBOOT_SERIAL=y +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_MCUBOOT_INDICATION_LED=y + +# Enable Serial boot mode +CONFIG_BOOT_SERIAL_ENTRANCE_GPIO=n +CONFIG_BOOT_SERIAL_WAIT_FOR_DFU=n +CONFIG_BOOT_SERIAL_NO_APPLICATION=y +CONFIG_BOOT_SERIAL_BOOT_MODE=y + +# Larger SMP packets +CONFIG_BOOT_LINE_BUFS=32 +CONFIG_BOOT_SERIAL_MAX_RECEIVE_SIZE=4096 + +# Force ECDSA-P256, since this is not necessarily the default +CONFIG_BOOT_SIGNATURE_TYPE_RSA=n +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +# Enter serial recovery when the boot-mode retention area asks for it +CONFIG_RETAINED_MEM=y +CONFIG_RETENTION=y +CONFIG_RETENTION_BOOT_MODE=y + +# The build won't fit on the partition allocated for it without size +# optimizations. +CONFIG_SIZE_OPTIMIZATIONS=y + +# Use minimal C library instead of the Picolib +CONFIG_MINIMAL_LIBC=y + +CONFIG_FLASH=y +CONFIG_REBOOT=y +CONFIG_CBPRINTF_NANO=y + +CONFIG_LOG=n +CONFIG_LOG_PRINTK=n + +CONFIG_BOOT_BANNER=n +CONFIG_DEBUG_OPTIMIZATIONS=n +CONFIG_RTT_CONSOLE=n +CONFIG_STDOUT_CONSOLE=n diff --git a/variants/rak4631_nrf52840/variant.cpp b/variants/rak4631_nrf52840/variant.cpp new file mode 100644 index 000000000..a2ca7998f --- /dev/null +++ b/variants/rak4631_nrf52840/variant.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* MCUboot sees the boot mode after the reset and enters serial recovery. */ +void _on_1200_bps() { + (void)bootmode_set(BOOT_MODE_TYPE_BOOTLOADER); + sys_reboot(SYS_REBOOT_COLD); +} From 6ce0a7598d6a540abaa478f405879e0671874d7c Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Wed, 30 Sep 2026 16:52:20 +0300 Subject: [PATCH 08/15] extra: install MCUboot Python requirements with bootstrap.sh Signed-off-by: Sercan Erat --- extra/bootstrap.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/extra/bootstrap.sh b/extra/bootstrap.sh index f9ccde109..d7f20768a 100755 --- a/extra/bootstrap.sh +++ b/extra/bootstrap.sh @@ -70,6 +70,7 @@ west config manifest.project-filter -- "$HAL_FILTER" west update $WEST_MODULES_CACHE "$@" west zephyr-export pip3 install -r ../zephyr/scripts/requirements-base.txt +pip3 install -r ../bootloader/mcuboot/scripts/requirements.txt log_msg "endgroup" TOOLCHAIN_VERSIONS=$(for tc in $NEEDED_TOOLCHAINS; do From 5e4639b78fd7dd0fd8b89da8eea1af8d13ea7ead Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Wed, 30 Sep 2026 16:58:05 +0300 Subject: [PATCH 09/15] upload: flash only the sketch on upload with mcumgr Signed-off-by: Sercan Erat --- platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.txt b/platform.txt index 8548fc09f..246cda9a1 100644 --- a/platform.txt +++ b/platform.txt @@ -391,7 +391,7 @@ tools.mcumgr.wait.cmd.windows=powershell -NoProfile -Command Start-Sleep -Second tools.mcumgr.upload.params.verbose= tools.mcumgr.upload.params.quiet= -tools.mcumgr.upload.pattern={tools.patterns.flash-loader-and-sketch} :: run {reset.cmd} +tools.mcumgr.upload.pattern="{runtime.tools.scripting-tools.path}/scripting-tools" run {sketch.cmd} :: run {reset.cmd} tools.mcumgr.bootloader.params.verbose= tools.mcumgr.bootloader.params.quiet= From 844f8442263df88d12eee0c68ec6dfcaaa8ab18f Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Wed, 30 Sep 2026 16:58:51 +0300 Subject: [PATCH 10/15] extra: disable shell log backend for debug configuration Signed-off-by: Sercan Erat --- extra/debug.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/extra/debug.conf b/extra/debug.conf index 3223827a6..92ac77e71 100644 --- a/extra/debug.conf +++ b/extra/debug.conf @@ -24,6 +24,7 @@ CONFIG_DEBUG_THREAD_INFO=y CONFIG_SHELL=y #CONFIG_NET_SHELL=y #CONFIG_NET_L2_WIFI_SHELL=y +CONFIG_SHELL_LOG_BACKEND=n CONFIG_SHELL_STACK_SIZE=8192 CONFIG_LOG_BACKEND_UART_AUTOSTART=y CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n From 3954275d13fce1334a1747b3e363b0c5e6773f37 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Thu, 1 Oct 2026 12:37:18 +0300 Subject: [PATCH 11/15] tools: add mcumgr tool to upload over arduino-cli Signed-off-by: Sercan Erat --- tools/mcumgr/.gitignore | 1 + tools/mcumgr/README.md | 19 ++++++ tools/mcumgr/go.mod | 11 ++++ tools/mcumgr/go.sum | 135 ++++++++++++++++++++++++++++++++++++++++ tools/mcumgr/main.go | 105 +++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+) create mode 100644 tools/mcumgr/.gitignore create mode 100644 tools/mcumgr/README.md create mode 100644 tools/mcumgr/go.mod create mode 100644 tools/mcumgr/go.sum create mode 100644 tools/mcumgr/main.go diff --git a/tools/mcumgr/.gitignore b/tools/mcumgr/.gitignore new file mode 100644 index 000000000..46b25f285 --- /dev/null +++ b/tools/mcumgr/.gitignore @@ -0,0 +1 @@ +mcumgr diff --git a/tools/mcumgr/README.md b/tools/mcumgr/README.md new file mode 100644 index 000000000..8e5c9ffb8 --- /dev/null +++ b/tools/mcumgr/README.md @@ -0,0 +1,19 @@ +## MCUmgr + +This directory packages the [MCUmgr] command-line tool from the Apache Mynewt +project as an Arduino tool, so the core can upload images to boards running +MCUboot. + +**None of the code here is original work.** It is a copy of the upstream +MCUmgr CLI, and all rights to it belong to MCUmgr contributors. + +[MCUmgr]: https://github.com/apache/mynewt-mcumgr-cli + +### Building manually + +To build the tool, you need to have the Go programming language installed; make +sure you have the `go` command available in your PATH. Then, use the `go build` +command to build the tool for your platform. + +To build the full set of binaries for all platforms, run +`extra/package_tool.sh tools/mcumgr` from the repository root. diff --git a/tools/mcumgr/go.mod b/tools/mcumgr/go.mod new file mode 100644 index 000000000..1b056f721 --- /dev/null +++ b/tools/mcumgr/go.mod @@ -0,0 +1,11 @@ +module github.com/arduino/mcumgr + +go 1.12 + +require ( + github.com/JuulLabs-OSS/ble v0.0.0-20200716215611-d4fcc9d598bb // indirect + github.com/go-ble/ble v0.0.0-20181002102605-e78417b510a3 // indirect + golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect + mynewt.apache.org/newt v0.0.0-20201028015609-b57111dbd19f + mynewt.apache.org/newtmgr v0.0.0-20201028150837-60b2da78788c +) diff --git a/tools/mcumgr/go.sum b/tools/mcumgr/go.sum new file mode 100644 index 000000000..5aaed4ea6 --- /dev/null +++ b/tools/mcumgr/go.sum @@ -0,0 +1,135 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/JuulLabs-OSS/ble v0.0.0-20200716215611-d4fcc9d598bb h1:kIZ7fr8RxucJXNHifPxm71yiWuzpw0SmjlafMzoOd0U= +github.com/JuulLabs-OSS/ble v0.0.0-20200716215611-d4fcc9d598bb/go.mod h1:6deIuswYSv6W1l3sM/nonw0OKWtIZCn7ZOWvIREoq2A= +github.com/JuulLabs-OSS/cbgo v0.0.1 h1:A5JdglvFot1J9qYR0POZ4qInttpsVPN9lqatjaPp2ro= +github.com/JuulLabs-OSS/cbgo v0.0.1/go.mod h1:L4YtGP+gnyD84w7+jN66ncspFRfOYB5aj9QSXaFHmBA= +github.com/NickBall/go-aes-key-wrap v0.0.0-20170929221519-1c3aa3e4dfc5/go.mod h1:w5D10RxC0NmPYxmQ438CC1S07zaC1zpvuNW7s5sUk2Q= +github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= +github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8= +github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= +github.com/apache/mynewt-artifact v0.0.16/go.mod h1:vFUd47t74KPQMzSBhQ2qp5Hc7D29OU/Tl3xHtFwN3k8= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/cheggaaa/pb v2.0.7+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20171208011716-f6d7a1f6fbf3/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cosiner/argv v0.0.1/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI= +github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-ble/ble v0.0.0-20181002102605-e78417b510a3/go.mod h1:UMPB54/KFpdTdfH7Yovhk3J6kzgzE88e3QZi8cbayis= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joaojeronimo/go-crc16 v0.0.0-20140729130949-59bd0194935e h1:LY29wmnTcSR92avOm1dW0LSjeE3d9Xnhm/mpGzMT/wc= +github.com/joaojeronimo/go-crc16 v0.0.0-20140729130949-59bd0194935e/go.mod h1:+X++CLDTje8Yr7J4bGuUYx5LVbNpXPeO5ZLpI42hKVk= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k= +github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab h1:n8cgpHzJ5+EDyDri2s/GC7a9+qK3/YEGnBsd0uS/8PY= +github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab/go.mod h1:y1pL58r5z2VvAjeG1VLGc8zOQgSOzbKN7kMHPvFXJ+8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/raff/goble v0.0.0-20190909174656-72afc67d6a99/go.mod h1:CxaUhijgLFX0AROtH5mluSY71VqpjQBw9JXE2UKZmc4= +github.com/raff/goble v0.0.0-20200327175727-d63360dcfd80 h1:IZkjNgPZXcE4USkGzmJQyHco3KFLmhcLyFdxCOiY6cQ= +github.com/raff/goble v0.0.0-20200327175727-d63360dcfd80/go.mod h1:CxaUhijgLFX0AROtH5mluSY71VqpjQBw9JXE2UKZmc4= +github.com/runtimeco/go-coap v0.0.0-20190911184520-8e5532820fc0 h1:WOVbcRe5kFuLbhZdyz8PKVR6L22C6I48fWIUS5ucpGY= +github.com/runtimeco/go-coap v0.0.0-20190911184520-8e5532820fc0/go.mod h1:x/rLuxedbWFDN497ZirUaDklkWz6JhZLjGdgurMysxM= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v2.20.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= +github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.starlark.net v0.0.0-20190919145610-979af19b165c/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0= +golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710= +gopkg.in/abiosoft/ishell.v2 v2.0.0 h1:/J5yh3nWYSSGFjALcitTI9CLE0Tu27vBYHX0srotqOc= +gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= +gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v2 v2.0.7/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4= +gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0= +gopkg.in/mattn/go-colorable.v0 v0.1.0/go.mod h1:BVJlBXzARQxdi3nZo6f6bnl5yR20/tOL6p+V0KejgSY= +gopkg.in/mattn/go-isatty.v0 v0.0.4/go.mod h1:wt691ab7g0X4ilKZNmMII3egK0bTxl37fEn/Fwbd8gc= +gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +mynewt.apache.org/newt v0.0.0-20200409145402-c5d1e422bfa3/go.mod h1:GTc1Q2t1eVSWNtTWGLyZW8UeRM81SQnq3urDorrJ6AA= +mynewt.apache.org/newt v0.0.0-20201028015609-b57111dbd19f h1:ZZM0GfqgTGAtTWiBk/Xo0ePsBnbEDb5F1iKDII3OWDw= +mynewt.apache.org/newt v0.0.0-20201028015609-b57111dbd19f/go.mod h1:0HULbbiMWkyIHsKyv3KozsdVzCZyK2lY9sjeexub4ac= +mynewt.apache.org/newtmgr v0.0.0-20201028150837-60b2da78788c h1:6361yI7aHLT2VPKYdNjZ2EqDm3PgQiupOUGwgGGqRrw= +mynewt.apache.org/newtmgr v0.0.0-20201028150837-60b2da78788c/go.mod h1:qUNQJ6Jz87jAtOZzPWlKMK7WX/o7baHr8ubtcSwvSTc= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/tools/mcumgr/main.go b/tools/mcumgr/main.go new file mode 100644 index 000000000..8187c4ed6 --- /dev/null +++ b/tools/mcumgr/main.go @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/** + * This file mirrors mcumgr/mcumgr.go from github.com/apache/mynewt-mcumgr-cli + * (Apache-2.0) on top of the same newtmgr libraries. + */ + +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + + "mynewt.apache.org/newt/util" + "mynewt.apache.org/newtmgr/newtmgr/cli" + "mynewt.apache.org/newtmgr/newtmgr/config" + "mynewt.apache.org/newtmgr/newtmgr/nmutil" + "mynewt.apache.org/newtmgr/nmxact/nmserial" +) + +func stopXport() { + x, err := cli.GetXportIfOpen() + if err == nil { + // Don't attempt to close a serial transport. Attempting to close + // the serial port while a read is in progress (in MacOS) just + // blocks until the read completes. Instead, let the OS close the + // port on termination. + if _, ok := x.(*nmserial.SerialXport); !ok { + x.Stop() + } + } +} + +func closeSesn() { + s, err := cli.GetSesnIfOpen() + if err == nil { + s.Close() + } +} + +func cleanup() { + closeSesn() + stopXport() +} + +func main() { + nmutil.ToolInfo = nmutil.ToolInfoType{ + ExeName: "mcumgr", + ShortName: "mcumgr", + LongName: "mcumgr", + VersionString: "0.0.0-dev", + CfgFilename: ".mcumgr.cp.json", + } + + if err := config.InitGlobalConnProfileMgr(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) + os.Exit(1) + } + + defer cleanup() + cli.SetOnExit(cleanup) + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan) + + go func() { + for { + s := <-sigChan + switch s { + case os.Interrupt, syscall.SIGTERM: + cli.SilenceErrors() + cli.NmExit(1) + + case syscall.SIGQUIT: + util.PrintStacks() + } + } + }() + + cli.Commands().Execute() +} From 156afc15987498c602950aaa0c6d023967351d75 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Thu, 1 Oct 2026 12:52:16 +0300 Subject: [PATCH 12/15] ci: add mcumgr to the dependabot configuration Signed-off-by: Sercan Erat --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ed4f36657..62e42f6ff 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,11 @@ updates: schedule: interval: "weekly" + - package-ecosystem: "gomod" + directory: "/tools/mcumgr" + schedule: + interval: "weekly" + - package-ecosystem: "gomod" directory: "/tools/sync-zephyr-artifacts" schedule: From fa70537490485bd742c78e432f9b2b71cab3abac Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 13 Oct 2026 18:09:30 +0300 Subject: [PATCH 13/15] ci: pick the loader memory table from sysbuild output Signed-off-by: Sercan Erat --- .github/workflows/package_core.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/package_core.yml b/.github/workflows/package_core.yml index 754d67f3b..cbd72a710 100644 --- a/.github/workflows/package_core.yml +++ b/.github/workflows/package_core.yml @@ -221,11 +221,12 @@ jobs: grep -i "warning:" $REPORT.stdout > $REPORT.warnings || true # extract the memory usage table (from the header to the first non-% line) + # There is one table per image with sysbuild, so skip the MCUboot one. # override the size of the Flash with the size of the loader partition # and add the size of the sketch partition (not reported by Zephyr) LOADER_SIZE=$(( $(cat variants/${{ matrix.variant }}/syms-static.ld | grep '_loader_max_size' | cut -d '=' -f 2 | tr -d ') ;') )) SKETCH_SIZE=$(( $(cat variants/${{ matrix.variant }}/syms-static.ld | grep '_sketch_max_size' | cut -d '=' -f 2 | tr -d ') ;') )) - cat $REPORT.stdout | sed -n '/^Memory region/,/^[^%]*$/p' | head -n -1 \ + cat $REPORT.stdout | awk '/^Memory region/ {t=""; f=1; next} f && /%/ {t=t $0 "\n"; next} f {f=0; if ($0 !~ /\/mcuboot\//) printf "%s", t}' \ | awk 'BEGIN {split("B KB MB GB", u); for(i in u) m[u[i]]=1024^(i-1)} /:/ {print "[\"" $1 "\"," $2*m[$3] "," $4*m[$5] "]"}' \ | sort \ | jq -s "map((select(.[0] == \"FLASH:\") | .[2]) |= ${LOADER_SIZE}) + [ [ \"SKETCH:\", 0, ${SKETCH_SIZE} ] ]" > $REPORT.meminfo From e8f1d414f1d0295963ccd84e5c7df8cb4da4153a Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Tue, 13 Oct 2026 18:10:21 +0300 Subject: [PATCH 14/15] ci: build the MCUmgr tool on a macOS runner Signed-off-by: Sercan Erat --- .github/workflows/package_tool.yml | 3 ++- extra/package_tool.sh | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/package_tool.yml b/.github/workflows/package_tool.yml index bacd2afbd..90f130833 100644 --- a/.github/workflows/package_tool.yml +++ b/.github/workflows/package_tool.yml @@ -97,7 +97,8 @@ jobs: build-tool: name: Build ${{ matrix.tool }} ${{ matrix.version }} needs: setup - runs-on: ubuntu-latest + # MCUmgr's macOS binaries need cgo, so they cannot be cross-compiled on Linux. + runs-on: ${{ matrix.tool == 'mcumgr' && 'macos-latest' || 'ubuntu-latest' }} strategy: matrix: ${{ fromJSON(needs.setup.outputs.matrix) }} steps: diff --git a/extra/package_tool.sh b/extra/package_tool.sh index ed3126a36..42b819444 100755 --- a/extra/package_tool.sh +++ b/extra/package_tool.sh @@ -22,8 +22,8 @@ hash_file() { plat=$1 file=$2 name=$(basename $file) - hash=$(sha256sum $file | cut -d ' ' -f 1) - len=$(stat -c %s $file) + hash=$(shasum -a 256 $file | cut -d ' ' -f 1) + len=$(wc -c < $file | tr -d ' ') cat << EOF { "host": "$plat", @@ -55,9 +55,14 @@ build_for_arch() { local build_file="$TOOL_NAME$app_ext" local package_file="$tool_stem-$plat$pkg_ext" + # The macOS binaries of MCUmgr need cgo, which Go only enables for the + # native target: force it on for both macOS targets when the host is a Mac + local cgo_env="" + [ "$os" == "darwin" ] && [ "$(uname)" == "Darwin" ] && cgo_env="CGO_ENABLED=1" + echo "Building $TOOL_NAME for $os/$arch ($plat)" mkdir -p "$build_dir" - (cd $TOOL_DIR && GOOS="$os" GOARCH="$arch" go build -o "$build_dir/$build_file") + (cd $TOOL_DIR && env $cgo_env GOOS="$os" GOARCH="$arch" go build -o "$build_dir/$build_file") (cd "$tool_stem" && $pkg_cmd "$package_file" $plat/) hash_file $plat "$package_file" > $build_dir.json } From 9f05724e14eda6c3c69a7ad31e66451597444848 Mon Sep 17 00:00:00 2001 From: Sercan Erat Date: Thu, 15 Oct 2026 17:18:45 +0300 Subject: [PATCH 15/15] sync: fix the leftovers of the dev/variant-rak4631 merge Signed-off-by: Sercan Erat --- .editorconfig-checker.json | 4 ++++ .github/workflows/package_core.yml | 2 +- .github/workflows/rak-build.yml | 2 +- .vscode/flash_loader.jlink | 2 +- .vscode/tasks.json | 1 - cores/arduino/main.cpp | 8 -------- extra/debug.conf | 3 +++ extra/rak/checkall.py | 15 ++++++++++----- extra/rak/license_check.py | 2 +- .../blinky_arduino/boards/rak4631_nrf52840.conf | 5 ++--- .../hello_arduino/boards/rak4631_nrf52840.conf | 5 ++--- variants/rak4631_nrf52840/doc/index.txt | 4 +++- 12 files changed, 28 insertions(+), 25 deletions(-) diff --git a/.editorconfig-checker.json b/.editorconfig-checker.json index 378291c67..7fb58abf3 100644 --- a/.editorconfig-checker.json +++ b/.editorconfig-checker.json @@ -12,6 +12,10 @@ "loader/blobs/", "llext-edk/", "extra/build.sh", + "extra/bootstrap.sh", + "extra/package_tool.sh", + "\\.github/workflows/package_core\\.yml", + "\\.github/workflows/package_tool\\.yml", "loader/CMakeLists.txt", "^CMakeLists\\.txt$" ] diff --git a/.github/workflows/package_core.yml b/.github/workflows/package_core.yml index cbd72a710..1b7f39508 100644 --- a/.github/workflows/package_core.yml +++ b/.github/workflows/package_core.yml @@ -246,7 +246,7 @@ jobs: ls reports/* >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - # Preprare an archive of the built binaries (and build dir on failure) + # Prepare an archive of the built binaries (and build dir on failure) [ "${{ job.status }}" == "failure" ] && FULL_BUILD_DIR="build/${{ matrix.variant }}/" sed -e '/^#/d' boards.local.txt > boards.local_${{ matrix.variant }}.txt tar chf - \ diff --git a/.github/workflows/rak-build.yml b/.github/workflows/rak-build.yml index b31cb4d1a..bfaa1357e 100644 --- a/.github/workflows/rak-build.yml +++ b/.github/workflows/rak-build.yml @@ -67,7 +67,7 @@ jobs: run: | mkdir -p venv/bin && touch venv/bin/activate ./extra/build.sh nrf52840dk - ./extra/build.sh rak4631 --mcuboot + ./extra/build.sh rak4631 - name: Build samples # Standalone Zephyr build of the samples. The sample list comes diff --git a/.vscode/flash_loader.jlink b/.vscode/flash_loader.jlink index 57fccf76c..8f66b95eb 100644 --- a/.vscode/flash_loader.jlink +++ b/.vscode/flash_loader.jlink @@ -1,4 +1,4 @@ -loadfile firmwares/mcuboot-rak4631_nrf52840.hex +loadfile firmwares/zephyr-rak4631_nrf52840.mcuboot.hex loadfile firmwares/zephyr-rak4631_nrf52840.hex r g diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d264e2e70..d2ddafae6 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -34,7 +34,6 @@ "args": [ "${BOARD_ARD}", // "--debug", - "--mcuboot" ], "group": "build", "problemMatcher": ["$gcc"] diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp index 5e47c3e2b..cbfc10360 100644 --- a/cores/arduino/main.cpp +++ b/cores/arduino/main.cpp @@ -25,15 +25,7 @@ void __attribute__((weak)) __loopHook(void) { int main(void) { #if ZARD_BOARD_HAS_SERIALUSB - /* - * Skip the auto-begin only when a shell runs on the sketch's own CDC - * port (chosen zephyr,shell-uart == cdc-acm-serial). - */ -#if !(defined(CONFIG_SHELL) && DT_HAS_CHOSEN(zephyr_shell_uart) && \ - DT_SAME_NODE(DT_CHOSEN(zephyr_shell_uart), \ - DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), cdc_acm_serial, 0))) SerialUSB.begin(115200); -#endif #endif initVariant(); diff --git a/extra/debug.conf b/extra/debug.conf index 92ac77e71..94e86ebf6 100644 --- a/extra/debug.conf +++ b/extra/debug.conf @@ -1,4 +1,7 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com # SPDX-License-Identifier: Apache-2.0 + # Debug configuration for Arduino Core Zephyr # Enable debug mode diff --git a/extra/rak/checkall.py b/extra/rak/checkall.py index ceb2f66b0..f709b52d5 100644 --- a/extra/rak/checkall.py +++ b/extra/rak/checkall.py @@ -62,12 +62,17 @@ def _build_checks(board): if not variant or not target: log.die(f"Board '{board}' not found in boards.txt / boards.local.txt") + # Boards with --sysbuild in their zephyr_args boot through MCUboot (see + # extra/build.sh); the loader is then one image among several and has + # its own build directory, which is where compile_commands.json lives. + zephyr_args = utils.board_property(board, "build.zephyr_args") or "" + build_dir = f"build/{variant}" + if "--sysbuild" in zephyr_args.split(): + build_dir += "/loader" + return [ - (f"loader build ({board})", ["./extra/build.sh", board, "--mcuboot"]), - ( - "cppcheck static analysis", - ["west", "rak-lint", "-b", f"build/{variant}/loader"], - ), + (f"loader build ({board})", ["./extra/build.sh", board]), + ("cppcheck static analysis", ["west", "rak-lint", "-b", build_dir]), (f"sketch compilation ({board})", ["west", "rak-sketch-check", "-b", board]), (f"sample build ({target})", ["west", "rak-sample-check", "-b", target]), ] diff --git a/extra/rak/license_check.py b/extra/rak/license_check.py index 536d244f7..d25b239be 100644 --- a/extra/rak/license_check.py +++ b/extra/rak/license_check.py @@ -35,7 +35,7 @@ } # Never checked, matched by exact file name (dotfiles have no suffix). -IGNORE_NAMES = {".gitignore", "VERSION"} +IGNORE_NAMES = {".gitignore", "VERSION", "dependabot.yml"} # Checked even though their suffix is ignored. SOURCE_NAMES = {"CMakeLists.txt"} diff --git a/samples/blinky_arduino/boards/rak4631_nrf52840.conf b/samples/blinky_arduino/boards/rak4631_nrf52840.conf index 0d16c0332..80eb919c4 100644 --- a/samples/blinky_arduino/boards/rak4631_nrf52840.conf +++ b/samples/blinky_arduino/boards/rak4631_nrf52840.conf @@ -9,9 +9,8 @@ CONFIG_RAK_REGOUT0_FIX=y CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USB_DEVICE_PRODUCT="RAKwireless RAK4631" CONFIG_USB_DEVICE_MANUFACTURER="RAKwireless" -# Use Nordic's VID and PID -CONFIG_USB_DEVICE_VID=0x1915 -CONFIG_USB_DEVICE_PID=0x5300 +CONFIG_USB_DEVICE_VID=0x1209 +CONFIG_USB_DEVICE_PID=0x0003 CONFIG_USB_CONFIGURATION_STRING_DESC_ENABLE=n CONFIG_USBD_CDC_ACM_CLASS=y diff --git a/samples/hello_arduino/boards/rak4631_nrf52840.conf b/samples/hello_arduino/boards/rak4631_nrf52840.conf index 0d16c0332..80eb919c4 100644 --- a/samples/hello_arduino/boards/rak4631_nrf52840.conf +++ b/samples/hello_arduino/boards/rak4631_nrf52840.conf @@ -9,9 +9,8 @@ CONFIG_RAK_REGOUT0_FIX=y CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USB_DEVICE_PRODUCT="RAKwireless RAK4631" CONFIG_USB_DEVICE_MANUFACTURER="RAKwireless" -# Use Nordic's VID and PID -CONFIG_USB_DEVICE_VID=0x1915 -CONFIG_USB_DEVICE_PID=0x5300 +CONFIG_USB_DEVICE_VID=0x1209 +CONFIG_USB_DEVICE_PID=0x0003 CONFIG_USB_CONFIGURATION_STRING_DESC_ENABLE=n CONFIG_USBD_CDC_ACM_CLASS=y diff --git a/variants/rak4631_nrf52840/doc/index.txt b/variants/rak4631_nrf52840/doc/index.txt index d5dbc5a39..9cf47babf 100644 --- a/variants/rak4631_nrf52840/doc/index.txt +++ b/variants/rak4631_nrf52840/doc/index.txt @@ -98,4 +98,6 @@ Entering serial recovery: `arduino-cli upload` first sends a 1200-bps touch to the board's USB port. A running sketch handles it (the core opens `Serial` before `setup()`); with no sketch on the board (or with a `--debug` loader) the loader handles it. A sketch that crashes before USB comes up leaves no port for -the touch; recover the board over SWD. +the touch. In that case press the reset button, then press it again while the +blue LED is on (500 ms): MCUboot stays in serial recovery and opens its own USB +port; upload to that port. SWD is only needed when MCUboot itself is missing.