diff --git a/.editorconfig-checker.json b/.editorconfig-checker.json index 2ed2559f0..378291c67 100644 --- a/.editorconfig-checker.json +++ b/.editorconfig-checker.json @@ -11,6 +11,8 @@ "firmwares/", "loader/blobs/", "llext-edk/", - "extra/build.sh" + "extra/build.sh", + "loader/CMakeLists.txt", + "^CMakeLists\\.txt$" ] } diff --git a/.github/workflows/rak-build.yml b/.github/workflows/rak-build.yml index 53d4ec391..b31cb4d1a 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 + ./extra/build.sh rak4631 --mcuboot - name: Build samples # Standalone Zephyr build of the samples. The sample list comes @@ -91,7 +91,7 @@ jobs: if: ${{ github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'upstream-sync') }} working-directory: ${{ env.MODULE_PATH }} - run: west rak-lint -v -b build/rak4631_nrf52840 + run: west rak-lint -v -b build/rak4631_nrf52840/loader - name: Install arduino-cli # Pinned (script and binary) for reproducible sketch builds. @@ -102,7 +102,7 @@ jobs: arduino-cli version - name: Install the Arduino Zephyr toolchain - run: arduino-cli core install arduino:zephyr_main@0.90.0 + run: arduino-cli core install arduino:zephyr_main@1.0.0 - name: Install the sketch libraries # Third-party libraries the sketches in sketch/ include, pinned diff --git a/.gitignore b/.gitignore index 39e50c86b..8965cf832 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,3 @@ includes.txt syms-dynamic.ld syms-static.ld tls-syms.S - -__pycache__/ diff --git a/.vscode/flash_loader.jlink b/.vscode/flash_loader.jlink index d9d91340e..57fccf76c 100644 --- a/.vscode/flash_loader.jlink +++ b/.vscode/flash_loader.jlink @@ -1,3 +1,4 @@ +loadfile firmwares/mcuboot-rak4631_nrf52840.hex loadfile firmwares/zephyr-rak4631_nrf52840.hex r g diff --git a/.vscode/flash_sketch.jlink b/.vscode/flash_sketch.jlink index 5171b8e99..149ed8d73 100644 --- a/.vscode/flash_sketch.jlink +++ b/.vscode/flash_sketch.jlink @@ -1,4 +1,4 @@ -loadbin sketch/wisblock_rak13800/build/rak.zephyr.rak4631/wisblock_rak13800.ino.elf-zsk.bin, 0x70000 +loadbin sketch/arduino_blinky/build/rak.zephyr.rak4631/arduino_blinky.ino.elf-zsk.bin, 0x80000 r g exit diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 09919db82..d264e2e70 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,9 +9,10 @@ "BOARD_WEST": "rak4631/nrf52840", "VARIANT": "rak4631_nrf52840", "DEVICE": "NRF52840_XXAA", - "SKETCH": "sketch/wisblock_rak13800", + "SKETCH": "sketch/arduino_blinky", "SKETCH_DEBUG": "false", "SAMPLE": "samples/blinky_arduino", + "SERIAL_PORT": "/dev/cu.usbmodem1101" }, "cwd": "${workspaceFolder}" }, @@ -32,7 +33,8 @@ "command": "extra/build.sh", "args": [ "${BOARD_ARD}", - "--debug" + // "--debug", + "--mcuboot" ], "group": "build", "problemMatcher": ["$gcc"] @@ -50,6 +52,19 @@ ], "problemMatcher": [] }, + { + // Board in MCUboot serial recovery (double-tap reset); MCUboot itself needs J-Link. + "label": "Loader: Upload (mcumgr)", + "type": "shell", + "command": "arduino-cli", + "args": [ + "burn-bootloader", + "-b", "rak:zephyr:${BOARD_ARD}", + "-P", "mcumgr", + "-p", "${SERIAL_PORT}" + ], + "problemMatcher": [] + }, { "label": "Loader: Clean", "type": "shell", @@ -114,6 +129,19 @@ ], "problemMatcher": [] }, + { + "label": "Sketch: Upload (mcumgr)", + "type": "shell", + "command": "arduino-cli", + "args": [ + "upload", + "-b", "rak:zephyr:${BOARD_ARD}", + "-p", "${SERIAL_PORT}", + "--input-dir", "${SKETCH}/build/rak.zephyr.${BOARD_ARD}", + "${SKETCH}" + ], + "problemMatcher": [] + }, { "label": "Sketch: Clean", "type": "shell", diff --git a/CMakeLists.txt b/CMakeLists.txt index 59aeebe90..c4a01af39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,9 @@ +# Copyright (c) Arduino s.r.l. and/or its affiliated companies +# Copyright (c) 2026 RAKwireless Technology Limited # SPDX-License-Identifier: Apache-2.0 +add_subdirectory(modules) + # Do nothing unless the Arduino API is enabled in the Zephyr project if(NOT CONFIG_ARDUINO_API) return() diff --git a/Kconfig.rak b/Kconfig.rak index eca81397d..d675fdeca 100644 --- a/Kconfig.rak +++ b/Kconfig.rak @@ -12,3 +12,5 @@ config RAK_RUI_API With this option disabled the core behaves like upstream ArduinoCore-zephyr and none of the RAK sources are compiled. + +rsource "modules/Kconfig" diff --git a/README.rak.md b/README.rak.md index 11a52fb84..fc1ad817e 100644 --- a/README.rak.md +++ b/README.rak.md @@ -162,11 +162,11 @@ arduino-cli version # verify the installation ### Install the Arduino Zephyr toolchain (one time) ```shell -arduino-cli core install arduino:zephyr_main@0.90.0 +arduino-cli core install arduino:zephyr_main@1.0.0 ``` The official `arduino:zephyr_main` core supplies the cross compiler -referenced by `boards.txt` (`arm-zephyr-eabi` 0.16.8) and the +referenced by `boards.txt` (`arm-zephyr-eabi` 1.0.1) and the post-processing tools (`gen-rodata-ld`, `zephyr-sketch-tool`, `zephyr-check-size`) that the `{runtime.tools.*}` recipes in `platform.txt` resolve against. @@ -181,6 +181,7 @@ cd ~/rak-arduino-zephyr/ArduinoCore-zephyr (cd tools/zephyr-sketch-tool && go build) (cd tools/gen-rodata-ld && go build) (cd tools/zephyr-check-size && go build) +(cd tools/mcumgr && go build) ``` Each binary is produced inside its own tool directory diff --git a/boards.txt b/boards.txt index dc64e7ca6..f732bdb4f 100644 --- a/boards.txt +++ b/boards.txt @@ -734,8 +734,7 @@ ventunoq.bootloader.target=stm32h5f5xx nrf52840dk.name=Nordic nRF52840 DK nrf52840dk.build.core=arduino nrf52840dk.build.crossprefix=arm-zephyr-eabi- -nrf52840dk.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ - +nrf52840dk.build.compiler_path={runtime.tools.arm-zephyr-eabi-1.0.1.path}/bin/ nrf52840dk.menu.debug.false=Standard nrf52840dk.menu.debug.true=Debug nrf52840dk.menu.debug.true.build.zsk_args.debug=-debug @@ -764,7 +763,7 @@ nrf52840dk.upload.target=nrf52840 rak4631.name=RAKwireless RAK4631 rak4631.build.core=arduino rak4631.build.crossprefix=arm-zephyr-eabi- -rak4631.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ +rak4631.build.compiler_path={runtime.tools.arm-zephyr-eabi-1.0.1.path}/bin/ rak4631.menu.debug.false=Standard rak4631.menu.debug.true=Debug @@ -783,6 +782,7 @@ rak4631.menu.baseboard.rak19007.build.extra_flags=-DWISBLOCK_BASE_19007 rak4631.build.zephyr_target=rak4631/nrf52840 rak4631.build.zephyr_args= rak4631.build.zephyr_hals=hal_nordic +rak4631.build.zephyr_toolchain=arm-zephyr-eabi rak4631.build.variant=rak4631_nrf52840 rak4631.build.extra_flags= @@ -792,6 +792,20 @@ rak4631.vid.0=0x1915 rak4631.pid.0=0x5300 rak4631.upload_port.0.vid=0x1915 rak4631.upload_port.0.pid=0x5300 +rak4631.upload_port.1.vid=0x1915 +rak4631.upload_port.1.pid=0x5301 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..68b3c560a 100755 --- a/extra/build.sh +++ b/extra/build.sh @@ -21,7 +21,7 @@ fi if [ $# -eq 0 ] || [ x$1 == x"-h" ] || [ x$1 == x"--help" ]; then cat << EOF Usage: - $0 + $0 [--debug] [--mcuboot] $0 [] Build the loader for the given target. @@ -60,9 +60,25 @@ if ! [ -z "$chosen_board" ]; then fi upload_offset=$(jq -cr '.upload_offset' <<< "$chosen_board") - # Check for debug flag and append - if [ x$2 == x"--debug" ]; then - args+=(-- -DEXTRA_CONF_FILE=../extra/debug.conf) + # Check for debug and mcuboot flags and append + for opt in "${@:2}"; do + case "$opt" in + --debug) debug=1 ;; + --mcuboot) mcuboot=1 ;; + *) echo "Unknown option: $opt"; exit 1 ;; + esac + done + + conf="" + if [ -n "$mcuboot" ]; then + args+=(--sysbuild) + conf="overlay_mcuboot.conf" + fi + if [ -n "$debug" ]; then + 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 +116,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 +144,39 @@ 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 +rm -f firmwares/mcuboot-$variant.hex +if [ -n "$mcuboot" ]; then + cp ${BUILD_DIR}/mcuboot/zephyr/zephyr.hex firmwares/mcuboot-$variant.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/extra/rak/checkall.py b/extra/rak/checkall.py index 2015e3790..ceb2f66b0 100644 --- a/extra/rak/checkall.py +++ b/extra/rak/checkall.py @@ -63,8 +63,11 @@ def _build_checks(board): log.die(f"Board '{board}' not found in boards.txt / boards.local.txt") return [ - (f"loader build ({board})", ["./extra/build.sh", board]), - ("cppcheck static analysis", ["west", "rak-lint", "-b", f"build/{variant}"]), + (f"loader build ({board})", ["./extra/build.sh", board, "--mcuboot"]), + ( + "cppcheck static analysis", + ["west", "rak-lint", "-b", f"build/{variant}/loader"], + ), (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 df0f36f5d..536d244f7 100644 --- a/extra/rak/license_check.py +++ b/extra/rak/license_check.py @@ -22,7 +22,18 @@ sys.path.insert(0, str(Path(__file__).resolve().parent)) import rak_utils as utils -IGNORE_SUFFIXES = {".md", ".rst", ".txt", ".pde", ".json", ".svg"} +IGNORE_SUFFIXES = { + ".md", + ".rst", + ".txt", + ".pde", + ".json", + ".svg", + ".pem", + ".sum", + ".mod", +} + # Never checked, matched by exact file name (dotfiles have no suffix). IGNORE_NAMES = {".gitignore", "VERSION"} # Checked even though their suffix is ignored. 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..851e974ce 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (c) Arduino s.r.l. and/or its affiliated companies +# Copyright (c) 2026 RAKwireless Technology Limited # SPDX-License-Identifier: Apache-2.0 #set(DTC_OVERLAY_FILE ${BOARD}.overlay) @@ -5,7 +7,10 @@ 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: it creates the sysbuild_cache target that the boards module reads. +# It is a no-op in a non-sysbuild build, so plain builds are unaffected. +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 5d89422a5..bab9c846c 100644 --- a/loader/llext_exports.c +++ b/loader/llext_exports.c @@ -549,4 +549,12 @@ 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 5741cf9ab..fa90bceef 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) { @@ -310,7 +330,8 @@ static int loader(const struct shell *sh) { size_t sketch_buf_len = sketch_hdr->len; - if (sketch_hdr->flags & SKETCH_FLAG_LINKED) { + // an erased sketch area reads flags 0xff, never jump into it + if (sketch_valid && (sketch_hdr->flags & SKETCH_FLAG_LINKED)) { #ifdef CONFIG_BOARD_ARDUINO_PORTENTA_C33 #if CONFIG_MPU barrier_dmem_fence_full(); diff --git a/loader/mcuboot.md b/loader/mcuboot.md new file mode 100644 index 000000000..8abbe4403 --- /dev/null +++ b/loader/mcuboot.md @@ -0,0 +1,50 @@ +### How to use MCUmgr: + +Install go tool: + +```shell +brew install go +``` + +If a new key will be used, please run following commands: +```shell +cd +/bin/python3 -m pip install -r bootloader/mcuboot/scripts/requirements.txt +/bin/python3 bootloader/mcuboot/scripts/imgtool.py keygen -k ArduinoCore-zephyr/key/arduino_key_ecdsa-p256.pem -t ecdsa-p256 +``` + +# Updating MCU over UART/USB-CDC +Firstly, go must be installed. After that, mcumgr and scripting-tools +can be installed via following command: +```shell +go install github.com/apache/mynewt-mcumgr-cli/mcumgr@latest +go install github.com/arduino/scripting-tools@latest +``` + +mcumgr path can be find via following command: +```shell +go env GOPATH +``` + +To test, send a string to the remote target device and have it echo it back: +```shell +mcumgr version +mcumgr --conntype serial --connstring "/dev/cu.usbserial-xxx,baud=115200" echo hello +``` + +Other commands to update mcu: +```shell +mcumgr --conntype serial --connstring image upload -n 1 ../firmwares/zephyr-rak4631_nrf52840.bin +mcumgr --conntype serial --connstring image upload -n 2 ../sketch/arduino_blinky/build/rak.zephyr.rak4631/arduino_blinky.ino.bin-zsk.bin + +mcumgr --conntype serial --connstring image list +mcumgr --conntype serial --connstring image confirm +mcumgr --conntype serial --connstring reset +``` + +# Entering serial recovery +A 1200-bps touch on the board's USB port reboots into MCUboot serial +recovery. A sketch handles it after `Serial.begin()`; the loader handles +it when no sketch is on the board or when it is a `--debug` build. Without +a USB port (the sketch never opens `Serial`, or it has crashed), double-tap +the reset button instead. 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..31ba83690 --- /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 + +# Do not forget to 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..87021a79e --- /dev/null +++ b/loader/sysbuild/mcuboot/boards/rak4631_nrf52840.conf @@ -0,0 +1,20 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_RAK_REGOUT0_FIX=y +CONFIG_RAK_DOUBLE_TAP=y + +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 (0x5300). +CONFIG_USB_DEVICE_PRODUCT="RAKwireless RAK4631" +CONFIG_USB_DEVICE_MANUFACTURER="RAKwireless" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x5301 +# 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..bb5f7902c --- /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/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 000000000..4b463590a --- /dev/null +++ b/modules/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_RAK_DOUBLE_TAP rak_double_tap) +add_subdirectory_ifdef(CONFIG_RAK_REGOUT0_FIX rak_regout0_fix) diff --git a/modules/Kconfig b/modules/Kconfig new file mode 100644 index 000000000..33008cb1e --- /dev/null +++ b/modules/Kconfig @@ -0,0 +1,8 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +menu "RAKwireless Modules" +rsource "rak_double_tap/Kconfig" +rsource "rak_regout0_fix/Kconfig" +endmenu diff --git a/modules/rak_double_tap/CMakeLists.txt b/modules/rak_double_tap/CMakeLists.txt new file mode 100644 index 000000000..784030c55 --- /dev/null +++ b/modules/rak_double_tap/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(double_tap.c) diff --git a/modules/rak_double_tap/Kconfig b/modules/rak_double_tap/Kconfig new file mode 100644 index 000000000..5bed3fa25 --- /dev/null +++ b/modules/rak_double_tap/Kconfig @@ -0,0 +1,26 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +menuconfig RAK_DOUBLE_TAP + bool "Enter the bootloader on a double tap reset" + depends on BOARD_RAK4631_NRF52840 + depends on RETENTION_BOOT_MODE + select HWINFO + help + Pressing the reset button twice within RAK_DOUBLE_TAP_WINDOW_MS duration + to set the boot-mode to BOOT_MODE_TYPE_BOOTLOADER through the retention + area chosen as zephyr,boot-mode. + +if RAK_DOUBLE_TAP + +config RAK_DOUBLE_TAP_WINDOW_MS + int "Double-tap window in milliseconds" + default 500 + range 50 5000 + help + Time after a press of the reset button during which a second press + enters the bootloader. The bootloader busy-waits for this long after + every such press, so it adds directly to that boot's time. + +endif # RAK_DOUBLE_TAP diff --git a/modules/rak_double_tap/double_tap.c b/modules/rak_double_tap/double_tap.c new file mode 100644 index 000000000..9a81351e4 --- /dev/null +++ b/modules/rak_double_tap/double_tap.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026 RAKwireless Technology Limited + * Author: sercan.erat@rakwireless.com + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Double-tap reset: press the reset button twice within + * CONFIG_RAK_DOUBLE_TAP_WINDOW_MS duration to enter the bootloader's recovery mode. + */ + +#include +#include +#include +#include +#include + +#define RAK_BOOT_MODE_DOUBLE_TAP_ARMED 0xD7 + +#ifdef CONFIG_MCUBOOT_INDICATION_LED +static const struct gpio_dt_spec window_led = GPIO_DT_SPEC_GET(DT_ALIAS(mcuboot_led0), gpios); +#endif + +static void window_led_set(bool on) { +#ifdef CONFIG_MCUBOOT_INDICATION_LED + (void)gpio_pin_configure_dt(&window_led, on ? GPIO_OUTPUT_ACTIVE : GPIO_OUTPUT_INACTIVE); +#else + ARG_UNUSED(on); +#endif +} + +static bool reset_by_button(void) { + uint32_t cause = 0; + + int ret = hwinfo_get_reset_cause(&cause); + (void)hwinfo_clear_reset_cause(); + + if (ret != 0) { + return false; + } + + /* + * Consume only RESET_PIN: the loader and the sketch still need the + * remaining bits to tell a power-on from a watchdog or soft reset. + */ + if (cause & RESET_PIN) { + (void)hwinfo_clear_reset_cause(); + } + + return (cause & RESET_PIN) != 0; +} + +static int double_tap_check(void) { + bool button = reset_by_button(); + + if (bootmode_check(BOOT_MODE_TYPE_BOOTLOADER) == 1) { + /* 1200 bps touch: same as a second tap. */ + window_led_set(true); + return 0; + } + + if (!button) { + (void)bootmode_clear(); + return 0; + } + + if (bootmode_check(RAK_BOOT_MODE_DOUBLE_TAP_ARMED) == 1) { + /* Second tap: keep the LED on; MCUboot takes it over in recovery. */ + window_led_set(true); + (void)bootmode_set(BOOT_MODE_TYPE_BOOTLOADER); + return 0; + } + + if (bootmode_set(RAK_BOOT_MODE_DOUBLE_TAP_ARMED) != 0) { + return 0; + } + + window_led_set(true); + k_busy_wait(CONFIG_RAK_DOUBLE_TAP_WINDOW_MS * USEC_PER_MSEC); + window_led_set(false); + + (void)bootmode_clear(); + + return 0; +} + +SYS_INIT(double_tap_check, APPLICATION, 0); diff --git a/modules/rak_regout0_fix/CMakeLists.txt b/modules/rak_regout0_fix/CMakeLists.txt new file mode 100644 index 000000000..69ffae478 --- /dev/null +++ b/modules/rak_regout0_fix/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(regout0_fix.c) diff --git a/modules/rak_regout0_fix/Kconfig b/modules/rak_regout0_fix/Kconfig new file mode 100644 index 000000000..4c1779bce --- /dev/null +++ b/modules/rak_regout0_fix/Kconfig @@ -0,0 +1,13 @@ +# Copyright (c) 2026 RAKwireless Technology Limited +# Author: sercan.erat@rakwireless.com +# SPDX-License-Identifier: Apache-2.0 + +config RAK_REGOUT0_FIX + bool "Program UICR REGOUT0 to 3.3 V on a VDDH-powered RAK4630" + depends on BOARD_RAK4631_NRF52840 + help + Early-boot hook for the RAK4630 module, whose nRF52840 is supplied + through VDDH. A full chip erase leaves UICR REGOUT0 at its 1.8 V + default, which underpowers the LEDs and 3.3 V peripherals. The hook + programs REGOUT0 to 3.3 V once and resets the chip. It is a no-op + when the chip runs in normal-voltage mode or REGOUT0 is already set. diff --git a/loader/rak4630_regout0.c b/modules/rak_regout0_fix/regout0_fix.c similarity index 83% rename from loader/rak4630_regout0.c rename to modules/rak_regout0_fix/regout0_fix.c index 14dec3865..527a7dc89 100644 --- a/loader/rak4630_regout0.c +++ b/modules/rak_regout0_fix/regout0_fix.c @@ -17,13 +17,12 @@ * boards powered in normal-voltage mode. */ -#include - -#if defined(CONFIG_BOARD_NRF52840DK) || defined(CONFIG_BOARD_RAK4631_NRF52840) #include #include -static int rak4630_vddh_regout0_3v3(void) { +#include + +static int rak_vddh_regout0_3v3(void) { if ((nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) && ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) == (UICR_REGOUT0_VOUT_DEFAULT << UICR_REGOUT0_VOUT_Pos))) { @@ -46,5 +45,4 @@ static int rak4630_vddh_regout0_3v3(void) { return 0; } -SYS_INIT(rak4630_vddh_regout0_3v3, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); -#endif /* CONFIG_BOARD_NRF52840DK || CONFIG_BOARD_RAK4631_NRF52840 */ +SYS_INIT(rak_vddh_regout0_3v3, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/platform.txt b/platform.txt index 4eee89658..246cda9a1 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="{runtime.tools.scripting-tools.path}/scripting-tools" run {sketch.cmd} :: 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= diff --git a/samples/blinky_arduino/boards/rak4631_nrf52840.conf b/samples/blinky_arduino/boards/rak4631_nrf52840.conf index 62c41b919..0d16c0332 100644 --- a/samples/blinky_arduino/boards/rak4631_nrf52840.conf +++ b/samples/blinky_arduino/boards/rak4631_nrf52840.conf @@ -4,6 +4,8 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_RAK_REGOUT0_FIX=y + CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USB_DEVICE_PRODUCT="RAKwireless RAK4631" CONFIG_USB_DEVICE_MANUFACTURER="RAKwireless" diff --git a/samples/hello_arduino/boards/rak4631_nrf52840.conf b/samples/hello_arduino/boards/rak4631_nrf52840.conf index 62c41b919..0d16c0332 100644 --- a/samples/hello_arduino/boards/rak4631_nrf52840.conf +++ b/samples/hello_arduino/boards/rak4631_nrf52840.conf @@ -4,6 +4,8 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_RAK_REGOUT0_FIX=y + CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USB_DEVICE_PRODUCT="RAKwireless RAK4631" CONFIG_USB_DEVICE_MANUFACTURER="RAKwireless" 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..5533f5ab6 --- /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() +} diff --git a/variants/rak4631_nrf52840/doc/index.txt b/variants/rak4631_nrf52840/doc/index.txt index d6e2873e8..852501b81 100644 --- a/variants/rak4631_nrf52840/doc/index.txt +++ b/variants/rak4631_nrf52840/doc/index.txt @@ -47,9 +47,8 @@ ecosystem. The pinout is as follows: | GND | GND | 39 | 40 | GND | GND | +-----------------------------+----------+-----+-----+----------+-----------------------------+ -Signal names follow the WisBlock connector specification (see the -[upstream WisBlock platform PR](https://github.com/zephyrproject-rtos/zephyr/pull/113140) -and the WisBlock Technical Specification v1.0 attached to it). +Signal names follow the WisBlock connector specification (see the WisBlock Technical +Specification v1.0). | Arduino pin | WisBlock signal | nRF52840 pin | Notes | |-------------|-----------------|--------------|------------------------------------------| @@ -78,10 +77,24 @@ Buses: 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 - (`regulator-fixed` node in the overlay), so the rail is off until a + 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 (`firmwares/mcuboot-rak4631_nrf52840.hex`, built by +`extra/build.sh rak4631 --mcuboot`) over SWD with a J-Link or a RAKDAP1 probe. +arduino-cli cannot install MCUboot itself. + +Entering serial recovery: `arduino-cli upload` first sends a 1200-bps touch to +the board's USB port. A sketch that has called `Serial.begin()` handles it; with +no sketch on the board (or with a `--debug` loader) the loader handles it. When +the board shows no USB port (the sketch never opens `Serial`, or it has +crashed), the touch is lost: double-tap the reset button to enter serial +recovery, then upload. 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/rak4631_nrf52840.conf b/variants/rak4631_nrf52840/rak4631_nrf52840.conf index f3f92a149..bb557390c 100644 --- a/variants/rak4631_nrf52840/rak4631_nrf52840.conf +++ b/variants/rak4631_nrf52840/rak4631_nrf52840.conf @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 CONFIG_RAK_RUI_API=y +CONFIG_RAK_REGOUT0_FIX=y CONFIG_BT=y CONFIG_BT_HCI_RAW=y @@ -34,6 +35,15 @@ CONFIG_USBD_CDC_ACM_LOG_LEVEL_ERR=y CONFIG_UDC_DRIVER_LOG_LEVEL_ERR=y CONFIG_USBD_LOG_LEVEL_ERR=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 diff --git a/variants/rak4631_nrf52840/rak4631_nrf52840.overlay b/variants/rak4631_nrf52840/rak4631_nrf52840.overlay index d79912e01..861e1423b 100644 --- a/variants/rak4631_nrf52840/rak4631_nrf52840.overlay +++ b/variants/rak4631_nrf52840/rak4631_nrf52840.overlay @@ -4,12 +4,18 @@ * 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 { @@ -25,22 +31,22 @@ /* * USB CDC-ACM ports, in enumeration order: - * 1. logging: console (printk/stdout msg)+ log backend - * 2. sketch: the sketch's 'Serial' (SerialUSB) - * 3. shell: Zephyr shell (debug builds only, unused otherwise) + * 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_logging: board_cdc_acm_logging { + board_cdc_acm_sketch: board_cdc_acm_sketch { compatible = "zephyr,cdc-acm-uart"; status = "okay"; }; - board_cdc_acm_sketch: board_cdc_acm_sketch { + board_cdc_acm_shell: board_cdc_acm_shell { compatible = "zephyr,cdc-acm-uart"; status = "okay"; }; - board_cdc_acm_shell: board_cdc_acm_shell { + board_cdc_acm_logging: board_cdc_acm_logging { compatible = "zephyr,cdc-acm-uart"; status = "okay"; }; @@ -118,40 +124,6 @@ pinctrl-names = "default", "sleep"; }; -/delete-node/ &boot_partition; -/delete-node/ &slot0_partition; -/delete-node/ &slot1_partition; - -&flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - code_partition: partition@0 { - label = "code"; - reg = <0x00000000 0x00070000>; - read-only; - }; - - user_sketch: partition@70000 { - label = "user"; - reg = <0x070000 0x88000>; - }; - - /* - * The flash starting at 0x000f8000 and ending at - * 0x000fffff is reserved for use by the application. - * - * Storage partition will be used by FCB/LittleFS/NVS - * if enabled. - */ - storage_partition: partition@f8000 { - label = "storage"; - reg = <0x000f8000 0x00008000>; - }; - }; -}; /* Arduino API pin mappings */ / { 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.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); +} diff --git a/west.yml b/west.yml index ab7246bf3..35fad7049 100644 --- a/west.yml +++ b/west.yml @@ -38,6 +38,7 @@ manifest: - lvgl - fatfs - loramac-node + - zcbor - hal_nordic # path-allowlist: # - modules/hal/*