An open API service indexing awesome lists of open source software.

https://github.com/libmcu/template-nrf52


https://github.com/libmcu/template-nrf52

Last synced: 3 months ago
JSON representation

Awesome Lists containing this project

README

          

# madi

Firmware for MADI devices across multiple MCU targets and platform stacks.
This repository supports nRF52 (Zephyr/nRF5 SDK) and ESP32-class targets (ESP-IDF) with a shared, port-based architecture.

## Project Structure

```
.
├── CMakeLists.txt # Top-level entry (Zephyr or nRF5 SDK)
├── include/ # Public headers (version.h, metrics.def …)
├── src/ # Application sources
├── ports/
│ ├── nrf52/ # nRF5 SDK port (legacy)
│ └── zephyr/
│ ├── boards/nordic/madi_nrf52840/ # Board definition (DTS, Kconfig)
│ ├── prj.conf # Application Kconfig
│ └── mcuboot.conf # MCUboot Kconfig overlay
├── projects/
│ └── platforms/
│ ├── zephyr.cmake # Zephyr build wiring (signing key, modules)
│ └── madi_nrf52840.cmake # nRF5 SDK CMake platform config
├── secrets/
│ └── dfu_signing_dev.key # Dev signing key — never commit to VCS
├── tests/ # Unit tests (Make-based)
└── external/ # libmcu and other third-party sources
```

Key files:

| File | Purpose |
|------|---------|
| `ports/zephyr/boards/nordic/madi_nrf52840/madi_nrf52840.dts` | Flash partitions, QSPI, GPIO |
| `ports/zephyr/prj.conf` | Application Kconfig |
| `ports/zephyr/mcuboot.conf` | MCUboot Kconfig (signature algorithm, swap, QSPI) |
| `projects/platforms/zephyr.cmake` | Signing key path, Zephyr module list |

---

## Prerequisites

### Zephyr / nRF52

#### 1. Zephyr workspace

Install the Zephyr SDK outside this repo using
[west](https://docs.zephyrproject.org/latest/develop/getting_started/index.html):

```bash
west init $HOME/zephyr
cd $HOME/zephyr
west update
```

Expected layout:

```
$HOME/zephyr/
├── zephyr/ # Zephyr kernel ($ZEPHYR_BASE)
├── modules/ # hal_nordic, mbedtls, littlefs, segger …
└── bootloader/ # mcuboot
```

#### 2. ARM GNU Toolchain (GCC 12+)

Download **arm-none-eabi** from
[developer.arm.com](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads)
and extract, e.g. to `~/.local/gcc-arm-none-eabi/`.

#### 3. Build tools

```bash
brew install ninja # macOS
sudo apt install ninja-build # Ubuntu / Debian
```

#### 4. Flash tools

| Tool | Use |
|------|-----|
| `nrfjprog` | J-Link / nRF5 — initial flash and app updates |
| `pyocd` | OpenOCD-based alternative to nrfjprog |
| `mcumgr` | OTA / DFU over serial or BLE |

#### 5. imgtool

```bash
python3 -m venv .venv
source .venv/bin/activate
export PATH=.venv/bin:$PATH

export ZEPHYR_BASE=$HOME/zephyr/zephyr
export ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb
export GNUARMEMB_TOOLCHAIN_PATH=~/.local/gcc-arm-none-eabi
```

```bash
pip install -r $ZEPHYR_BASE/../bootloader/mcuboot/scripts/requirements.txt
```

#### 6. Zephyr Python requirements (required for `west build`)

Install Zephyr script dependencies in the active Python environment:

```bash
pip install -r $ZEPHYR_BASE/scripts/requirements.txt
```

> Missing this step commonly fails with `ModuleNotFoundError` (for example
> `pykwalify`) during board discovery.

### ESP-IDF

#### 1. ESP-IDF checkout and tools path

- IDF root (example): `$HOME/esp/esp-idf`
- tools path (default `IDF_TOOLS_PATH`): `$HOME/.espressif`

#### 2. Build tools

```bash
brew install ninja # macOS
sudo apt install ninja-build # Ubuntu / Debian
```

---

## Environment Setup

### Zephyr

Add to your shell profile (`.bashrc` / `.zshrc`) and reload:

```bash
source .venv/bin/activate
export PATH=.venv/bin:$PATH

export ZEPHYR_BASE=$HOME/zephyr/zephyr
export ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb
export GNUARMEMB_TOOLCHAIN_PATH=~/.local/gcc-arm-none-eabi

# Install the official mcumgr CLI (Go required)
go install github.com/apache/mynewt-mcumgr-cli/mcumgr@latest

# Add Go bin to PATH if needed
export PATH="$(go env GOPATH)/bin:$PATH"
```

### ESP-IDF

Run before ESP32 build/flash commands:

```bash
export IDF_TOOLS_PATH=$HOME/.espressif
source $HOME/esp/esp-idf/export.sh
```

---

## Initial Setup

### Zephyr

Build MCUboot and the app, then flash everything to a blank device.

#### 1. Build MCUboot

#### west

```bash
west build -b madi_nrf52840 -d build/mcuboot \
$ZEPHYR_BASE/../bootloader/mcuboot/boot/zephyr \
-- -DBOARD_ROOT=$(pwd)/ports/zephyr \
"-DEXTRA_CONF_FILE=$(pwd)/ports/zephyr/mcuboot.conf" \
"-DCONFIG_BOOT_SIGNATURE_KEY_FILE=\"$(pwd)/secrets/dfu_signing_dev.key\""
```

#### CMake

```bash
cmake -B build/mcuboot \
-S $ZEPHYR_BASE/../bootloader/mcuboot/boot/zephyr \
-DBOARD=madi_nrf52840 \
-DBOARD_ROOT=$(pwd)/ports/zephyr \
"-DEXTRA_CONF_FILE=$(pwd)/ports/zephyr/mcuboot.conf" \
"-DCONFIG_BOOT_SIGNATURE_KEY_FILE=$(pwd)/secrets/dfu_signing_dev.key" \
-G Ninja
cmake --build build/mcuboot
```

Output: `build/mcuboot/zephyr/zephyr.hex`

#### 2. Build App

#### NCS sysbuild

```bash
west build \
-b madi_nrf52840 \
-d build \
--sysbuild \
-- \
-DCONF_FILE=ports/zephyr/prj.conf \
-DBOARD_ROOT=ports/zephyr
```

#### west

```bash
# First time — configure and build
west build -b madi_nrf52840 -d build
# or
west build -b madi_nrf52840 -d build -- -DBOARD_ROOT=$(pwd)/ports/zephyr

# Subsequent builds — configuration is cached
west build -d build
```

#### CMake

```bash
cmake -B build -DTARGET_PLATFORM=madi_nrf52840 -G Ninja
cmake --build build
```

Signed outputs in `build/zephyr/`:

| File | Use |
|------|-----|
| `zephyr.signed.hex` | Test image — reverts on next reset if not confirmed |
| `zephyr.signed.bin` | Signed binary for OTA upload |
| `zephyr.signed.confirmed.hex` | Pre-confirmed — no runtime confirmation needed |

#### 3. Flash (first-time)

#### nrfjprog

```bash
# Erase chip
nrfjprog --family NRF52 --eraseall

# Flash MCUboot
nrfjprog --family NRF52 --program build/mcuboot/zephyr/zephyr.hex --verify

# Flash confirmed app to slot0 (--sectorerase preserves MCUboot at 0x0)
nrfjprog --family NRF52 \
--program build/zephyr/zephyr.signed.confirmed.hex \
--verify --sectorerase

nrfjprog --family NRF52 --reset
```

#### west

```bash
nrfjprog --family NRF52 --eraseall
west flash -d build/mcuboot
west flash -d build
```

#### pyocd

```bash
pyocd flash -t nrf52840 --erase chip build/mcuboot/zephyr/zephyr.hex
pyocd flash -t nrf52840 --erase sector \
build/zephyr/zephyr.signed.confirmed.hex
```

### ESP-IDF

Build (choose one):

```bash
idf.py -B build -DTARGET_PLATFORM=jc8012wp4a1 build
```

```bash
cmake -B build -DTARGET_PLATFORM=jc8012wp4a1 -G Ninja && cmake --build build
```

Flash and monitor:

```bash
idf.py -B build -DTARGET_PLATFORM=jc8012wp4a1 flash monitor
```

---

## Development Workflow

MCUboot stays on the device. Only rebuild and reflash the app.

### Build App

#### west

```bash
west build -d build
```

#### CMake

```bash
cmake --build build
```

#### Make (nRF5 SDK — no MCUboot)

```bash
make
```

> The Make build targets the nRF5 SDK directly (no Zephyr, no MCUboot).
> See [Legacy Build (nRF5 SDK)](#legacy-build-nrf5-sdk) for details.

### Flash App

#### nrfjprog

```bash
nrfjprog --family NRF52 \
--program build/zephyr/zephyr.signed.confirmed.hex \
--verify --sectorerase
nrfjprog --family NRF52 --reset
```

#### west

```bash
west flash -d build
```

#### pyocd

```bash
pyocd flash -t nrf52840 --erase sector \
build/zephyr/zephyr.signed.confirmed.hex
```

### OTA / DFU

The board exposes a USB CDC ACM port for mcumgr SMP. Connect the USB cable and
wait for the RTT log to show `USB CDC ACM ready` before running commands.

> **Port name**: `/dev/ttyACM0` on Linux, `/dev/tty.usbmodem*` on macOS.

```bash
# Upload image to slot 1
mcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \
image upload build/zephyr/zephyr.signed.bin

# List images — copy the hash of the uploaded image
mcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \
image list

# Mark as pending (test mode — reverts if not confirmed before next reset)
mcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \
image test

# Trigger swap
mcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 reset

# After the new image boots successfully, confirm it permanently
mcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \
image confirm
```

Or confirm from application code:

```c
boot_write_img_confirmed(); /* requires CONFIG_MCUBOOT_IMG_MANAGER=y */
```

> **Rollback**: If confirmation is not received before the next reset,
> MCUboot automatically reverts to the previous image in slot 0.

---

## Flash Partition Layout

| Flash | Partition | Label | Offset | Size |
|-------|-----------|-------|--------|------|
| Internal — nRF52840 (1 MiB) | `boot_partition` | `mcuboot` | `0x000000` | 48 KiB |
| Internal | `slot0_partition` | `image-0` | `0x00C000` | 976 KiB |
| External — MX25R1635F (2 MiB) | `slot1_partition` | `image-1` | `0x000000` | 976 KiB |
| External | `storage_partition` | `storage` | `0x0F4000` | 1 MiB |

MCUboot uses **swap-using-move**: slot 0 and slot 1 must be identical in size
(976 KiB). No scratch partition is required.

---

## Signing Key

The build uses `secrets/dfu_signing_dev.key` (ECDSA P-256) by default,
configured in `projects/platforms/zephyr.cmake`.

### Check an existing key

```bash
openssl pkey -in secrets/dfu_signing_dev.key -text -noout | head -5
# ECDSA P-256: "Public Key Algorithm: id-ecPublicKey" + "ASN1 OID: prime256v1"
# RSA-2048: "Public-Key: (2048 bit)"
# Ed25519: "Public Key Algorithm: ED25519"
```

### Generate a new key

```bash
# ECDSA P-256 — recommended (fast on Cortex-M4, matches mcuboot.conf default)
imgtool keygen -k secrets/dfu_signing_prod.pem -t ecdsa-p256

# Ed25519 — smallest MCUboot footprint
imgtool keygen -k secrets/dfu_signing_prod.pem -t ed25519

# RSA-2048 — most conservative choice
imgtool keygen -k secrets/dfu_signing_prod.pem -t rsa-2048
```

> ⚠️ Never commit private keys. Ensure `secrets/*.pem` and `secrets/*.key`
> are in `.gitignore`.

### Switch to a different key

Update two places:

1. **`projects/platforms/zephyr.cmake`** — app signing key:

```cmake
set(MCUBOOT_SIGNATURE_KEY_FILE
"${CMAKE_CURRENT_LIST_DIR}/../../secrets/dfu_signing_prod.pem"
CACHE STRING "MCUboot signing key" FORCE)
```

2. **MCUboot build** — pass the new key via `-DCONFIG_BOOT_SIGNATURE_KEY_FILE`
and update `CONFIG_BOOT_SIGNATURE_TYPE_*` in `ports/zephyr/mcuboot.conf`
if the algorithm changes:

| Option | Algorithm |
|--------|-----------|
| `CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y` | ECDSA P-256 (default) |
| `CONFIG_BOOT_SIGNATURE_TYPE_ED25519=y` | Ed25519 |
| `CONFIG_BOOT_SIGNATURE_TYPE_RSA=y` | RSA-2048 |

Rebuild and reflash MCUboot after any key or algorithm change — the old
bootloader rejects images signed with a different key.

---

## Legacy Build (nRF5 SDK)

Standalone build without Zephyr or MCUboot, targeting the nRF5 SDK directly.

### Build

#### CMake

```bash
cmake -S . -B build -DTARGET_PLATFORM=madi_nrf52840
cmake --build build
```

Build outputs: `build/madi.elf`, `build/madi.hex`, `build/madi.bin`

#### Make

```bash
make
```

### Flash

#### CMake targets

```bash
cmake --build build --target flash # nrfjprog
cmake --build build --target flash_usb # dfu-util
cmake --build build --target flash_softdevice
```

### Tests

#### Make

```bash
make test
make coverage
```

---

## Board Notes

- **Board name**: `madi_nrf52840`
- **Console**: RTT (Segger J-Link) — logging and shell via RTT
- **USB**: USB CDC ACM — mcumgr SMP transport (`/dev/ttyACM0` on Linux, `/dev/tty.usbmodem*` on macOS)
- **UART0**: TX=P0.23, RX=P0.25 — disabled (pinctrl defined but not in use)
- **LED**: P0.20 (active-low, alias `led0`)
- **QSPI flash**: CS=P0.07, SCK=P0.08, IO0=P1.09, IO1=P1.08, IO2=P0.12, IO3=P0.06