{"id":48188769,"url":"https://github.com/libmcu/template-nrf52","last_synced_at":"2026-04-04T17:53:02.508Z","repository":{"id":221481189,"uuid":"754405034","full_name":"libmcu/template-nrf52","owner":"libmcu","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-30T01:00:09.000Z","size":21912,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-30T04:28:49.862Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/libmcu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-02-08T01:13:34.000Z","updated_at":"2026-03-17T16:22:06.000Z","dependencies_parsed_at":"2024-02-08T08:38:44.652Z","dependency_job_id":null,"html_url":"https://github.com/libmcu/template-nrf52","commit_stats":null,"previous_names":["libmcu/template-nrf52"],"tags_count":1,"template":true,"template_full_name":null,"purl":"pkg:github/libmcu/template-nrf52","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libmcu%2Ftemplate-nrf52","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libmcu%2Ftemplate-nrf52/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libmcu%2Ftemplate-nrf52/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libmcu%2Ftemplate-nrf52/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/libmcu","download_url":"https://codeload.github.com/libmcu/template-nrf52/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libmcu%2Ftemplate-nrf52/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31407655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T17:53:02.419Z","updated_at":"2026-04-04T17:53:02.495Z","avatar_url":"https://github.com/libmcu.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# madi\n\nFirmware for MADI devices across multiple MCU targets and platform stacks.\nThis repository supports nRF52 (Zephyr/nRF5 SDK) and ESP32-class targets (ESP-IDF) with a shared, port-based architecture.\n\n## Project Structure\n\n```\n.\n├── CMakeLists.txt                 # Top-level entry (Zephyr or nRF5 SDK)\n├── include/                       # Public headers (version.h, metrics.def …)\n├── src/                           # Application sources\n├── ports/\n│   ├── nrf52/                     # nRF5 SDK port (legacy)\n│   └── zephyr/\n│       ├── boards/nordic/madi_nrf52840/  # Board definition (DTS, Kconfig)\n│       ├── prj.conf               # Application Kconfig\n│       └── mcuboot.conf           # MCUboot Kconfig overlay\n├── projects/\n│   └── platforms/\n│       ├── zephyr.cmake           # Zephyr build wiring (signing key, modules)\n│       └── madi_nrf52840.cmake    # nRF5 SDK CMake platform config\n├── secrets/\n│   └── dfu_signing_dev.key        # Dev signing key — never commit to VCS\n├── tests/                         # Unit tests (Make-based)\n└── external/                      # libmcu and other third-party sources\n```\n\nKey files:\n\n| File | Purpose |\n|------|---------|\n| `ports/zephyr/boards/nordic/madi_nrf52840/madi_nrf52840.dts` | Flash partitions, QSPI, GPIO |\n| `ports/zephyr/prj.conf` | Application Kconfig |\n| `ports/zephyr/mcuboot.conf` | MCUboot Kconfig (signature algorithm, swap, QSPI) |\n| `projects/platforms/zephyr.cmake` | Signing key path, Zephyr module list |\n\n---\n\n## Prerequisites\n\n### Zephyr / nRF52\n\n#### 1. Zephyr workspace\n\nInstall the Zephyr SDK outside this repo using\n[west](https://docs.zephyrproject.org/latest/develop/getting_started/index.html):\n\n```bash\nwest init $HOME/zephyr\ncd $HOME/zephyr\nwest update\n```\n\nExpected layout:\n\n```\n$HOME/zephyr/\n├── zephyr/          # Zephyr kernel ($ZEPHYR_BASE)\n├── modules/         # hal_nordic, mbedtls, littlefs, segger …\n└── bootloader/      # mcuboot\n```\n\n#### 2. ARM GNU Toolchain (GCC 12+)\n\nDownload **arm-none-eabi** from\n[developer.arm.com](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads)\nand extract, e.g. to `~/.local/gcc-arm-none-eabi/`.\n\n#### 3. Build tools\n\n```bash\nbrew install ninja              # macOS\nsudo apt install ninja-build    # Ubuntu / Debian\n```\n\n#### 4. Flash tools\n\n| Tool | Use |\n|------|-----|\n| `nrfjprog` | J-Link / nRF5 — initial flash and app updates |\n| `pyocd` | OpenOCD-based alternative to nrfjprog |\n| `mcumgr` | OTA / DFU over serial or BLE |\n\n#### 5. imgtool\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\nexport PATH=.venv/bin:$PATH\n\nexport ZEPHYR_BASE=$HOME/zephyr/zephyr\nexport ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb\nexport GNUARMEMB_TOOLCHAIN_PATH=~/.local/gcc-arm-none-eabi\n```\n\n```bash\npip install -r $ZEPHYR_BASE/../bootloader/mcuboot/scripts/requirements.txt\n```\n\n#### 6. Zephyr Python requirements (required for `west build`)\n\nInstall Zephyr script dependencies in the active Python environment:\n\n```bash\npip install -r $ZEPHYR_BASE/scripts/requirements.txt\n```\n\n\u003e Missing this step commonly fails with `ModuleNotFoundError` (for example\n\u003e `pykwalify`) during board discovery.\n\n### ESP-IDF\n\n#### 1. ESP-IDF checkout and tools path\n\n- IDF root (example): `$HOME/esp/esp-idf`\n- tools path (default `IDF_TOOLS_PATH`): `$HOME/.espressif`\n\n#### 2. Build tools\n\n```bash\nbrew install ninja              # macOS\nsudo apt install ninja-build    # Ubuntu / Debian\n```\n\n---\n\n## Environment Setup\n\n### Zephyr\n\nAdd to your shell profile (`.bashrc` / `.zshrc`) and reload:\n\n```bash\nsource .venv/bin/activate\nexport PATH=.venv/bin:$PATH\n\nexport ZEPHYR_BASE=$HOME/zephyr/zephyr\nexport ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb\nexport GNUARMEMB_TOOLCHAIN_PATH=~/.local/gcc-arm-none-eabi\n\n# Install the official mcumgr CLI (Go required)\ngo install github.com/apache/mynewt-mcumgr-cli/mcumgr@latest\n\n# Add Go bin to PATH if needed\nexport PATH=\"$(go env GOPATH)/bin:$PATH\"\n```\n\n### ESP-IDF\n\nRun before ESP32 build/flash commands:\n\n```bash\nexport IDF_TOOLS_PATH=$HOME/.espressif\nsource $HOME/esp/esp-idf/export.sh\n```\n\n---\n\n## Initial Setup\n\n### Zephyr\n\nBuild MCUboot and the app, then flash everything to a blank device.\n\n#### 1. Build MCUboot\n\n#### west\n\n```bash\nwest build -b madi_nrf52840 -d build/mcuboot \\\n    $ZEPHYR_BASE/../bootloader/mcuboot/boot/zephyr \\\n    -- -DBOARD_ROOT=$(pwd)/ports/zephyr \\\n    \"-DEXTRA_CONF_FILE=$(pwd)/ports/zephyr/mcuboot.conf\" \\\n    \"-DCONFIG_BOOT_SIGNATURE_KEY_FILE=\\\"$(pwd)/secrets/dfu_signing_dev.key\\\"\"\n```\n\n#### CMake\n\n```bash\ncmake -B build/mcuboot \\\n      -S $ZEPHYR_BASE/../bootloader/mcuboot/boot/zephyr \\\n      -DBOARD=madi_nrf52840 \\\n      -DBOARD_ROOT=$(pwd)/ports/zephyr \\\n      \"-DEXTRA_CONF_FILE=$(pwd)/ports/zephyr/mcuboot.conf\" \\\n      \"-DCONFIG_BOOT_SIGNATURE_KEY_FILE=$(pwd)/secrets/dfu_signing_dev.key\" \\\n      -G Ninja\ncmake --build build/mcuboot\n```\n\nOutput: `build/mcuboot/zephyr/zephyr.hex`\n\n#### 2. Build App\n\n#### NCS sysbuild\n\n```bash\nwest build \\\n    -b madi_nrf52840 \\\n    -d build \\\n    --sysbuild \\\n    -- \\\n    -DCONF_FILE=ports/zephyr/prj.conf \\\n    -DBOARD_ROOT=ports/zephyr\n ```\n\n#### west\n\n```bash\n# First time — configure and build\nwest build -b madi_nrf52840 -d build\n# or\nwest build -b madi_nrf52840 -d build -- -DBOARD_ROOT=$(pwd)/ports/zephyr\n\n# Subsequent builds — configuration is cached\nwest build -d build\n```\n\n#### CMake\n\n```bash\ncmake -B build -DTARGET_PLATFORM=madi_nrf52840 -G Ninja\ncmake --build build\n```\n\nSigned outputs in `build/zephyr/`:\n\n| File | Use |\n|------|-----|\n| `zephyr.signed.hex` | Test image — reverts on next reset if not confirmed |\n| `zephyr.signed.bin` | Signed binary for OTA upload |\n| `zephyr.signed.confirmed.hex` | Pre-confirmed — no runtime confirmation needed |\n\n#### 3. Flash (first-time)\n\n#### nrfjprog\n\n```bash\n# Erase chip\nnrfjprog --family NRF52 --eraseall\n\n# Flash MCUboot\nnrfjprog --family NRF52 --program build/mcuboot/zephyr/zephyr.hex --verify\n\n# Flash confirmed app to slot0 (--sectorerase preserves MCUboot at 0x0)\nnrfjprog --family NRF52 \\\n    --program build/zephyr/zephyr.signed.confirmed.hex \\\n    --verify --sectorerase\n\nnrfjprog --family NRF52 --reset\n```\n\n#### west\n\n```bash\nnrfjprog --family NRF52 --eraseall\nwest flash -d build/mcuboot\nwest flash -d build\n```\n\n#### pyocd\n\n```bash\npyocd flash -t nrf52840 --erase chip build/mcuboot/zephyr/zephyr.hex\npyocd flash -t nrf52840 --erase sector \\\n    build/zephyr/zephyr.signed.confirmed.hex\n```\n\n### ESP-IDF\n\nBuild (choose one):\n\n```bash\nidf.py -B build -DTARGET_PLATFORM=jc8012wp4a1 build\n```\n\n```bash\ncmake -B build -DTARGET_PLATFORM=jc8012wp4a1 -G Ninja \u0026\u0026 cmake --build build\n```\n\nFlash and monitor:\n\n```bash\nidf.py -B build -DTARGET_PLATFORM=jc8012wp4a1 flash monitor\n```\n\n---\n\n## Development Workflow\n\nMCUboot stays on the device. Only rebuild and reflash the app.\n\n### Build App\n\n#### west\n\n```bash\nwest build -d build\n```\n\n#### CMake\n\n```bash\ncmake --build build\n```\n\n#### Make (nRF5 SDK — no MCUboot)\n\n```bash\nmake\n```\n\n\u003e The Make build targets the nRF5 SDK directly (no Zephyr, no MCUboot).\n\u003e See [Legacy Build (nRF5 SDK)](#legacy-build-nrf5-sdk) for details.\n\n### Flash App\n\n#### nrfjprog\n\n```bash\nnrfjprog --family NRF52 \\\n    --program build/zephyr/zephyr.signed.confirmed.hex \\\n    --verify --sectorerase\nnrfjprog --family NRF52 --reset\n```\n\n#### west\n\n```bash\nwest flash -d build\n```\n\n#### pyocd\n\n```bash\npyocd flash -t nrf52840 --erase sector \\\n    build/zephyr/zephyr.signed.confirmed.hex\n```\n\n### OTA / DFU\n\nThe board exposes a USB CDC ACM port for mcumgr SMP. Connect the USB cable and\nwait for the RTT log to show `USB CDC ACM ready` before running commands.\n\n\u003e **Port name**: `/dev/ttyACM0` on Linux, `/dev/tty.usbmodem*` on macOS.\n\n```bash\n# Upload image to slot 1\nmcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \\\n    image upload build/zephyr/zephyr.signed.bin\n\n# List images — copy the hash of the uploaded image\nmcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \\\n    image list\n\n# Mark as pending (test mode — reverts if not confirmed before next reset)\nmcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \\\n    image test \u003chash\u003e\n\n# Trigger swap\nmcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 reset\n\n# After the new image boots successfully, confirm it permanently\nmcumgr --conntype serial --connstring /dev/ttyACM0,baud=115200 \\\n    image confirm\n```\n\nOr confirm from application code:\n\n```c\nboot_write_img_confirmed();   /* requires CONFIG_MCUBOOT_IMG_MANAGER=y */\n```\n\n\u003e **Rollback**: If confirmation is not received before the next reset,\n\u003e MCUboot automatically reverts to the previous image in slot 0.\n\n---\n\n## Flash Partition Layout\n\n| Flash | Partition | Label | Offset | Size |\n|-------|-----------|-------|--------|------|\n| Internal — nRF52840 (1 MiB) | `boot_partition` | `mcuboot` | `0x000000` | 48 KiB |\n| Internal | `slot0_partition` | `image-0` | `0x00C000` | 976 KiB |\n| External — MX25R1635F (2 MiB) | `slot1_partition` | `image-1` | `0x000000` | 976 KiB |\n| External | `storage_partition` | `storage` | `0x0F4000` | 1 MiB |\n\nMCUboot uses **swap-using-move**: slot 0 and slot 1 must be identical in size\n(976 KiB). No scratch partition is required.\n\n---\n\n## Signing Key\n\nThe build uses `secrets/dfu_signing_dev.key` (ECDSA P-256) by default,\nconfigured in `projects/platforms/zephyr.cmake`.\n\n### Check an existing key\n\n```bash\nopenssl pkey -in secrets/dfu_signing_dev.key -text -noout | head -5\n# ECDSA P-256:  \"Public Key Algorithm: id-ecPublicKey\" + \"ASN1 OID: prime256v1\"\n# RSA-2048:     \"Public-Key: (2048 bit)\"\n# Ed25519:      \"Public Key Algorithm: ED25519\"\n```\n\n### Generate a new key\n\n```bash\n# ECDSA P-256 — recommended (fast on Cortex-M4, matches mcuboot.conf default)\nimgtool keygen -k secrets/dfu_signing_prod.pem -t ecdsa-p256\n\n# Ed25519 — smallest MCUboot footprint\nimgtool keygen -k secrets/dfu_signing_prod.pem -t ed25519\n\n# RSA-2048 — most conservative choice\nimgtool keygen -k secrets/dfu_signing_prod.pem -t rsa-2048\n```\n\n\u003e ⚠️ Never commit private keys. Ensure `secrets/*.pem` and `secrets/*.key`\n\u003e are in `.gitignore`.\n\n### Switch to a different key\n\nUpdate two places:\n\n1. **`projects/platforms/zephyr.cmake`** — app signing key:\n\n   ```cmake\n   set(MCUBOOT_SIGNATURE_KEY_FILE\n       \"${CMAKE_CURRENT_LIST_DIR}/../../secrets/dfu_signing_prod.pem\"\n       CACHE STRING \"MCUboot signing key\" FORCE)\n   ```\n\n2. **MCUboot build** — pass the new key via `-DCONFIG_BOOT_SIGNATURE_KEY_FILE`\n   and update `CONFIG_BOOT_SIGNATURE_TYPE_*` in `ports/zephyr/mcuboot.conf`\n   if the algorithm changes:\n\n   | Option | Algorithm |\n   |--------|-----------|\n   | `CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y` | ECDSA P-256 (default) |\n   | `CONFIG_BOOT_SIGNATURE_TYPE_ED25519=y` | Ed25519 |\n   | `CONFIG_BOOT_SIGNATURE_TYPE_RSA=y` | RSA-2048 |\n\nRebuild and reflash MCUboot after any key or algorithm change — the old\nbootloader rejects images signed with a different key.\n\n---\n\n## Legacy Build (nRF5 SDK)\n\nStandalone build without Zephyr or MCUboot, targeting the nRF5 SDK directly.\n\n### Build\n\n#### CMake\n\n```bash\ncmake -S . -B build -DTARGET_PLATFORM=madi_nrf52840\ncmake --build build\n```\n\nBuild outputs: `build/madi.elf`, `build/madi.hex`, `build/madi.bin`\n\n#### Make\n\n```bash\nmake\n```\n\n### Flash\n\n#### CMake targets\n\n```bash\ncmake --build build --target flash          # nrfjprog\ncmake --build build --target flash_usb      # dfu-util\ncmake --build build --target flash_softdevice\n```\n\n### Tests\n\n#### Make\n\n```bash\nmake test\nmake coverage\n```\n\n---\n\n## Board Notes\n\n- **Board name**: `madi_nrf52840`\n- **Console**: RTT (Segger J-Link) — logging and shell via RTT\n- **USB**: USB CDC ACM — mcumgr SMP transport (`/dev/ttyACM0` on Linux, `/dev/tty.usbmodem*` on macOS)\n- **UART0**: TX=P0.23, RX=P0.25 — disabled (pinctrl defined but not in use)\n- **LED**: P0.20 (active-low, alias `led0`)\n- **QSPI flash**: CS=P0.07, SCK=P0.08, IO0=P1.09, IO1=P1.08, IO2=P0.12, IO3=P0.06\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flibmcu%2Ftemplate-nrf52","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flibmcu%2Ftemplate-nrf52","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flibmcu%2Ftemplate-nrf52/lists"}