diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6c9f0b7..cd8337f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1024,7 +1024,8 @@ New drivers in `patches/zephyr-new/` (LR11xx, LR20xx, native-Linux SPI/GPIO, DTS ## 10. Board Matrix -Build strings and flash methods: `boards/supported_boards.md` and `boards/example_board/README.md`. +Build strings: `docs/supported_boards.md`. Flash methods: `docs/BUILDING.md`. Adding a board: +`zephcore/boards/example_board/README.md`. | Board | SoC | Radio | GPS | Display | Notable extras | |-------|-----|-------|-----|---------|----------------| diff --git a/zephcore/Kconfig b/zephcore/Kconfig index 444b11d..826c22f 100644 --- a/zephcore/Kconfig +++ b/zephcore/Kconfig @@ -708,8 +708,8 @@ config ZEPHCORE_SD_FWID Only applicable to nRF52 boards with Adafruit bootloader. Common values: - 0x00B6 - S140 v6.1.1 (RAK4631, Wio Tracker, T1000-E) - 0x0123 - S140 v7.3.0 (XIAO nRF52840, Ikoka Nano) + 0x00B6 - S140 v6.1.1 (RAK4631, ThinkNode M1, Heltec T114) + 0x0123 - S140 v7.3.0 (XIAO nRF52840, Wio Tracker L1, T1000-E) config ZEPHCORE_VBAT_MV_MULTIPLIER int "Battery voltage ADC multiplier (fallback)" diff --git a/zephcore/boards/common/nrf52_wakeup.dtsi b/zephcore/boards/common/nrf52_wakeup.dtsi index d60c92e..3562fca 100644 --- a/zephcore/boards/common/nrf52_wakeup.dtsi +++ b/zephcore/boards/common/nrf52_wakeup.dtsi @@ -4,8 +4,9 @@ * NOTE: On nRF52, this property is currently inert — the nRF GPIO driver * does not implement GPIO_INT_WAKEUP, so wakeup-source alone does not set * the GPIO SENSE bits required for System OFF wakeup. Actual SENSE - * configuration is done explicitly in action_deep_sleep() (ui_task.c) - * via nrf_gpio_cfg_sense_input() right before sys_poweroff(). + * configuration is done explicitly for the sw0 alias in + * adapters/board/zephyr_poweroff.c via nrf_gpio_cfg_sense_input() right + * before sys_poweroff(). * * Kept here so that: * 1. If a future Zephyr nRF GPIO driver gains GPIO_INT_WAKEUP support, @@ -15,7 +16,7 @@ * on other platforms where the driver does honour GPIO_INT_WAKEUP. * * Include this in any nRF52 board DTS that defines a 'buttons' gpio-keys node. - * Boards without buttons (e.g. ikoka_nano, rak4631) should NOT include this. + * Boards without buttons (e.g. ikoka_nano, rak3401_1watt) should NOT include this. * * SPDX-License-Identifier: MIT */ diff --git a/zephcore/boards/example_board/README.md b/zephcore/boards/example_board/README.md index ea4b2cd..573f4e3 100644 --- a/zephcore/boards/example_board/README.md +++ b/zephcore/boards/example_board/README.md @@ -1,476 +1,274 @@ -ZephCore — Adding a New Board -============================= +# ZephCore: Adding a Board +This directory is the template for a new board. It is not a buildable board. -Supported Boards ----------------- +| File | Copy it when | What it is | +|---|---|---| +| [`board.conf`](board.conf) | always | Kconfig that cannot be derived from devicetree: name, SoftDevice ID, radio type | +| [`board.overlay`](board.overlay) | always, unless a custom `.dts` carries everything | Devicetree: radio, flash layout, console, peripherals. Every example is an `#if 0` block taken from a real board | +| [`partitions.overlay`](partitions.overlay) | ESP32-S3, ESP32-C3, ESP32-C6 | The flash map, shared by the app and MCUboot | +| [`zephcore.yml`](zephcore.yml) | always | The board manifest: registers the board for releases, the catalog and the docs | + +Looking for something else? + +- **Which boards exist, and their build strings:** [docs/supported_boards.md](../../../docs/supported_boards.md) + (generated from the manifests). +- **Building, roles, flashing, the config chain:** [docs/BUILDING.md](../../../docs/BUILDING.md). +- **Native Linux presets** (not boards): [docs/LINUX_NATIVE.md](../../../docs/LINUX_NATIVE.md). + +## 1. Two patterns + +**Pattern 1: the board already exists in Zephyr.** You add three or four files and no board definition. + +``` +zephcore/boards/// + board.conf + board.overlay + partitions.overlay ESP32-S3 / C3 / C6 only + zephcore.yml +``` + +References: `nrf52840/rak4631`, `nrf52840/wio_tracker_l1`, `nrf54l/xiao_nrf54l15`, `mg24/xiao_mg24`, +`esp32/xiao_esp32c3`, `esp32/xiao_esp32s3`, `esp32/heltec_wifi_lora32_v3`, `esp32/ttgo_tbeam` (classic ESP32), +`stm32wl/lora_e5_mini`. + +**Pattern 2: a custom board.** The same directory also holds a complete Zephyr board definition: + +| File | Purpose | +|---|---| +| `board.yml` | Board name, vendor, SoC | +| `Kconfig.` | Selects the SoC | +| `[_[_]].dts` | The devicetree. Named after the build target, as Zephyr expects; copy a sibling's naming | +| `-pinctrl.dtsi` | Pin control | +| `_defconfig` | Minimal defconfig | +| `board.cmake` | Flash runners (optional: a UF2-only board needs none) | +| `pre_dt_board.cmake`, `Kconfig.defconfig`, `Kconfig` | Only when needed: DTC warning flags, SoC defaults, extra heap | +| `.yaml` | Twister metadata, optional | +| `battery_curve.c` | Optional per-board battery discharge curve, compiled automatically when present | + +With a custom `.dts` the hardware can be described there entirely, and `board.overlay` is then optional. + +References: `nrf52840/ikoka_nano_30dbm` (minimal, radio only), `nrf52840/thinknode_m1` (e-paper, GPS, QSPI, +buzzer, two buttons), `nrf52840/t1000_e` (LR1110), `esp32/lilygo_t3s3` (ESP32-S3), `nrf54l/me25ls02` (nRF54L15). + +Two rules hold for both patterns: + +- **The directory name is the board name**, the first component of the `west build -b` string. +- **The parent directory is the platform.** It selects `_common.conf`; there is nothing to register in + `CMakeLists.txt`. A board name may exist under one platform directory only (the build stops otherwise). + +| Directory | Platform conf | SoCs | +|---|---|---| +| `boards/nrf52840/` | `nrf52_common.conf` | nRF52840 with the Adafruit UF2 bootloader | +| `boards/nrf54l/` | `nrf54l_common.conf` | nRF54L15, nRF54LM20A | +| `boards/esp32/` | `esp32_common.conf` (+ `esp32c3s3_common.conf` on S3 and C3) | ESP32, ESP32-S3, ESP32-C3, ESP32-C6 | +| `boards/mg24/` | `mg24_common.conf` | EFR32MG24 | +| `boards/stm32wl/` | `stm32wl_common.conf` | STM32WL | + +## 2. Steps + +1. **Collect the facts first:** the schematic, and the board's Arduino MeshCore variant (`variants//`) + if there is one. The variant gives the pin map, the SoftDevice version (nRF52) and the name MeshCore uses. + Where a vendor annotation and the pin map disagree, trust the pin map. +2. **Create `boards///`** and copy the template files into it. +3. **Edit `board.overlay`:** keep the examples for your platform, remove the `#if 0` / `#endif` lines around + them, delete the rest, and change every pin. The minimum is a radio node, a flash layout and a console. +4. **Edit `board.conf`:** the two name strings, plus `CONFIG_ZEPHCORE_SD_FWID` on nRF52 and the radio type if + the radio is not an SX126x. +5. **Edit `zephcore.yml`:** set `target`, and leave `release:` out for now. +6. **Build** the companion, then the repeater. Always `--pristine` when changing board or role. + + ```bash + west build -b zephcore --pristine + ``` + + ```bash + west build -b zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf" + ``` + + nRF54L adds `--no-sysbuild`. An ESP32-S3 / C3 / C6 **repeater** adds `--sysbuild` (section 5). ESP32 and + MG24 need their radio blobs once: `west blobs fetch hal_espressif` / `west blobs fetch hal_silabs`. +7. **Read back what was built.** CMake prints a `ZephCore config hierarchy` block with the board directory, + platform, and the final conf and overlay lists: check that your files are in it. Then look at + `build/zephyr/zephyr.dts` for the radio node, the `chosen` entries and the partition map. +8. **Validate on hardware**, then register the board for release (section 6). + +## 3. `board.conf` + +Required on every board: + +| Symbol | Meaning | +|---|---| +| `CONFIG_ZEPHCORE_BOARD_NAME` | Name shown in the app and by the `board` command; 39 characters at most | +| `CONFIG_BT_DIS_MODEL_NUMBER_STR` | BLE Device Information model string | +| `CONFIG_ZEPHCORE_SD_FWID` | nRF52 only: `0x00B6` (s140 v6) or `0x0123` (s140 v7), matching the partition include | + +Set only when the hardware calls for it (details and the values used in the tree are in the template): + +| Symbol | When | +|---|---| +| `CONFIG_ZEPHCORE_RADIO_LR1110` / `_LR2021` | LR11xx or LR2021 radio. SX126x, LLCC68 and STM32WL need nothing | +| `CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM`, `_MAX_TX_POWER_DBM` | External PA: keep the chip below the PA's maximum input | +| `CONFIG_ZEPHCORE_MAX_CONTACTS`, `_MAX_CHANNELS`, `_OFFLINE_QUEUE_SIZE` | RAM-bound boards (ESP32 without PSRAM, classic ESP32, STM32WL) | +| `CONFIG_HEAP_MEM_POOL_SIZE` | nRF with a panel larger than 128x64 | +| `CONFIG_ESPTOOLPY_FLASHSIZE_8MB` / `_16MB` | ESP32 with more than 4 MB flash | +| `CONFIG_SPIRAM_MODE_OCT` | ESP32-S3 with 8 MB octal PSRAM (R8) | +| `CONFIG_ESPTOOLPY_FLASHMODE_DIO` (+ `_QIO=n`) | Classic ESP32 PICO-D4 | + +Do not set what devicetree already decides (`ZEPHCORE_UI_DISPLAY`, `ZEPHCORE_UI_BUZZER`, `PWM`, `SPI`, +`NORDIC_QSPI_NOR`, `ESP_SPIRAM`), and do not put role or debug policy in a board: roles, logging and `BT=n` +come from `boards/common/*.conf`. `CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE` is off by default for every role and +is a user setting (`set rxduty`), not a board property. + +The chain, later wins: + +``` +prj.conf → zephcore_common.conf → _common.conf → esp32c3s3_common.conf (S3/C3) + → board.conf → auto-included feature confs → your EXTRA_CONF_FILE +``` + +Overlays follow the same order: `board.overlay` first, overlays paired with a conf after it, `partitions.overlay` +last ([ADR 0003](../../../docs/adr/0003-devicetree-overlay-precedence.md)). A `foo.overlay` next to a `foo.conf` +is paired automatically, which is how a board variant changes Kconfig and devicetree together +(`heltec_t114/no_display.conf` + `no_display.overlay`). + +## 4. What the firmware reads from devicetree + +Only `lora0` is required. Everything else enables its feature by being present. + +| Devicetree | Effect | +|---|---| +| alias `lora0` | The radio, for example `semtech,sx1262`, `semtech,lr1110` or `semtech,lr2021` | +| `lfs_partition` + `filesystem.dtsi` | `/lfs`: identity, prefs, contacts, channels | +| chosen `zephyr,settings-partition` | NVS for BLE bonds (nRF52, ESP32). nRF54L and MG24 keep bonds in a file on `/lfs` | +| aliases `led0`, `led1`, `lora-tx-led` | Heartbeat, unread, TX blink | +| alias `sw0` | User button; wake source from nRF System OFF and ESP32 light sleep | +| chosen `zephyr,display` | Display UI | +| node labelled `buzzer` + alias `buzzer` | PWM buzzer | +| node labelled `gnss` | GPS | +| aliases `gps-enable`, `gps-wakeup`, `gps-reset`, `gps-resetb`, `gps-vrtc-enable`, `gps-sleep-int`, `gps-rtc-int`; chosen `zephcore,gps-power` | GPS power control | +| `zephyr,user { io-channels; vbat-mv-multiplier; }` | Battery voltage; a regulator labelled `vbat_enable` is switched per read | +| `sensors-i2c.dtsi`, `rtc-i2c.dtsi` | I2C sensors (probed at runtime) and a battery-backed RTC (opt-in) | +| `nordic,qspi-nor` + `qspi-ext.dtsi` | `/ext` on external flash for contacts, channels and blobs | +| chosen `zephcore,companion-uart` | Companion protocol on a UART, for boards without Bluetooth | +| `zephcore,poweroff-gpios` | Pins released at shutdown on boards with a latched supply rail | + +`board.overlay` has a worked example for each row. + +## 5. Platform notes and traps ### nRF52840 -| Board | Build string | Flash | -|----------------------|-------------------------------------------|-------------------------------| -| RAK4631 | `west build -b rak4631 zephcore` | UF2 drag-drop or `west flash` | -| RAK3401 1W | `west build -b rak3401_1watt zephcore` | UF2 drag-drop or `west flash` | -| Wio Tracker L1 | `west build -b wio_tracker_l1 zephcore` | UF2 drag-drop or `west flash` | -| Wio Tracker L1 Pro 1W | `west build -b wio_tracker_l1_1w zephcore` | UF2 drag-drop or `west flash` | -| SenseCAP Solar | `west build -b sensecap_solar zephcore` | UF2 drag-drop or `west flash` | -| XIAO nRF52840 | `west build -b xiao_nrf52840 zephcore` | UF2 drag-drop or `west flash` | -| ProMicro SX1262 | `west build -b promicro_sx1262 zephcore` | UF2 drag-drop or `west flash` | -| T1000-E | `west build -b t1000_e zephcore` | UF2 drag-drop or `west flash` | -| SenseCAP MeshTracker X1 | `west build -b meshtracker_x1 zephcore` | UF2 drag-drop or `west flash` | -| ThinkNode M1 | `west build -b thinknode_m1 zephcore` | UF2 drag-drop or `west flash` | -| ThinkNode M3 | `west build -b thinknode_m3 zephcore` | UF2 drag-drop or `west flash` | -| ThinkNode M6 | `west build -b thinknode_m6 zephcore` | UF2 drag-drop or `west flash` | -| RAK WisMesh Tag | `west build -b rak_wismesh_tag zephcore` | UF2 drag-drop or `west flash` | -| Ikoka Nano 30dBm | `west build -b ikoka_nano_30dbm zephcore` | UF2 drag-drop | -| GAT562 30S Mesh Kit | `west build -b gat562_30s zephcore` | UF2 drag-drop or `west flash` | -| LilyGo T-Echo | `west build -b lilygo_techo zephcore` | UF2 drag-drop or `west flash` | -| LilyGo T-Impulse Plus | `west build -b lilygo_timpulse_plus zephcore` | UF2 drag-drop or `west flash` | -| Heltec T114 | `west build -b heltec_t114 zephcore` | UF2 drag-drop or `west flash` | -| Heltec Mesh Node T096 | `west build -b heltec_t096 zephcore` | UF2 drag-drop or `west flash` | -| Heltec Mesh Node T1 | `west build -b heltec_t1 zephcore` | UF2 drag-drop or `west flash` | -| muzi works R1 Neo | `west build -b muziworks_r1neo zephcore` | UF2 drag-drop or `west flash` | +- **SoftDevice version decides two things together:** the partition include (`nrf52_partitions_sdv6.dtsi` or + `_sdv7.dtsi`) and `CONFIG_ZEPHCORE_SD_FWID`. The Arduino variant's linker script tells you which. The wrong + pair puts the app at the wrong address and the board bootloops with no USB. +- **Include the shared partition file; do not write the map by hand.** It keeps `/lfs` and the bond store at + the offsets every shipped ZephCore board uses, and it sets `zephyr,sram`, which the nRF52840 SoC file does + not (without it the linker reports `region 'RAM' overflowed` on every build). +- **No 32.768 kHz crystal:** set `&lfclk { k32src = "rc"; k32src-accuracy-ppm = <250>; };` in devicetree. The + old `CONFIG_CLOCK_CONTROL_NRF_K32SRC_*` choices are silently ignored + ([ADR 0005](../../../docs/adr/0005-zephyr-main-pin.md)). +- **Wake from power-off** uses the `sw0` alias: `adapters/board/zephyr_poweroff.c` arms GPIO SENSE on that pin + before System OFF. `nrf52_wakeup.dtsi` documents the intent in devicetree; include it only when a node + labelled `buttons` exists, since a missing label is a build error. +- **QSPI flash:** declare it only if the chip is fitted, and disable an upstream `&qspi` the variant lacks. +- **SPIM0 / SPIM1 share their instance with TWIM0 / TWIM1.** Put the radio on `&spi2` or `&spi3`. +- **SSD1306 / SH1106 on a `nordic,nrf-twim` bus** needs `zephyr,concat-buf-size`, or the panel stays blank. -**Heltec T114 screenless:** append `boards/nrf52840/heltec_t114/no_display.conf` to `EXTRA_CONF_FILE` for units without the TFT module. +### nRF54L -UF2 flash: Double-tap reset button, drag `build/zephyr/zephyr.uf2` to the USB drive. -SWD flash: `west flash` (requires J-Link, pyocd, or nrfjprog connected). +- Build with `--no-sysbuild`. The SoC has no USB peripheral: no UF2 and no DFU, `zephyr.hex` over SWD. +- Enable `&xo` and `&lfclk` if the board DTS does not; the BLE controller does not build without them. +- **Port P2 has no GPIOTE.** No GPIO interrupt works there: the radio IRQ cannot be on P2, and a button on P2 + needs `polling-mode` on its `gpio-keys` node (`seeed_lr2021_evk`). ### ESP32 -| Board | Build string | Flash | -|----------------------|-----------------------------------------------------------|-----------------| -| XIAO ESP32-C3 | `west build -b xiao_esp32c3 zephcore` | `west flash` | -| XIAO ESP32-C6 | `west build -b xiao_esp32c6/esp32c6/hpcore zephcore` | `west flash` | -| LilyGo TLoRa C6 | `west build -b lilygo_tlora_c6/esp32c6/hpcore zephcore` | `west flash` | -| LilyGo T3S3 (SX1262) | `west build -b lilygo_t3s3/esp32s3/procpu zephcore` | `west flash` | -| XIAO ESP32-S3 | `west build -b xiao_esp32s3/esp32s3/procpu zephcore` | `west flash` | -| Station G2 | `west build -b station_g2/esp32s3/procpu zephcore` | `west flash` | -| Heltec V3 | `west build -b heltec_wifi_lora32_v3/esp32s3/procpu zephcore` | `west flash` | -| Heltec V4.2 (GC1109 PA) | `west build -b heltec_wifi_lora32_v4/esp32s3/procpu zephcore` | `west flash` | -| Heltec V4.3 (KCT8103L PA) | `west build -b heltec_wifi_lora32_v43/esp32s3/procpu zephcore` | `west flash` | -| Heltec V4-R8 (KCT8103L PA, 8MB octal PSRAM) | `west build -b heltec_wifi_lora32_v4_r8/esp32s3/procpu zephcore` | `west flash` | -| Heltec Wireless Tracker V1.1 | `west build -b heltec_wireless_tracker/esp32s3/procpu zephcore` | `west flash` | -| Heltec Wireless Tracker V2 | `west build -b heltec_wireless_tracker_v2/esp32s3/procpu zephcore` | `west flash` | -| LilyGo T-Beam v1.2 | `west build -b ttgo_tbeam/esp32/procpu zephcore` | `west flash` | -| ThinkNode M9 | `west build -b thinknode_m9/esp32s3/procpu zephcore` | `west flash` | -| Meshnology W12 (LR2021) | `west build -b meshnology_w12/esp32s3/procpu zephcore` | `west flash` | +- **Boot and flash layout** ([ADR 0001](../../../docs/adr/0001-esp32-boot-and-flash-layout.md)): a plain + companion build is simple boot at `0x0`. A repeater build on S3 / C3 / C6 pulls in WiFi OTA automatically + and **must be built with `--sysbuild`**; without it the board stops enumerating USB. Releases are always the + MCUboot `-merged.bin`. Classic ESP32 (`/esp32/` targets) is simple boot in every build and has no + `partitions.overlay`. +- **`partitions.overlay` is shared with MCUboot.** Read the template before changing a layout, including the + `_default_` versus `_amp_` base-table trap. +- **Flash size** defaults to 4 MB; state the real one in `board.conf` (it is copied to MCUboot). +- **ESP32-S3 native USB** ([ADR 0002](../../../docs/adr/0002-esp32s3-native-usb-companion.md)): including + `esp32s3_usb_otg.dtsi` from `board.overlay` gives companion builds a USB transport. esptool then cannot + auto-reset the board; use `start dfu` or a 1200-baud touch. +- **PSRAM** is enabled from devicetree when the board DTS uses an R-suffix module file. Only the mode needs + stating, because the upstream default cannot be overridden conditionally: -**Heltec V3 console:** ZephCore routes console/shell to `uart0` on V3. Use the UART serial port for boot logs and CLI. + | Part | PSRAM | `board.conf` | + |---|---|---| + | `R2` | 2 MB quad | nothing | + | `R8` | 8 MB octal | `CONFIG_SPIRAM_MODE_OCT=y` | -**Meshnology W12:** the only board here with an LR2021 *and* an external PA, and -the only ESP32 board whose TX power is capped well below the chip maximum — -4 dBm at the chip is ~30 dBm at the antenna through the GC1109 front end. It has -no USB-UART bridge, so esptool cannot auto-reset a companion build into download -mode; use `start dfu`, a 1200-baud touch, or hold BOOT. Full port notes and the -vendor schematic live in `devdocs/w12/`. +- **Manifest capabilities:** `wifi: true` gives the companion a WiFi transport and belongs only on boards with + the RAM for WiFi and BLE together ([ADR 0009](../../../docs/adr/0009-wifi-companion-boards.md)). + `light_sleep: true` makes repeaters light-sleep; the requirements are in `boards/common/pm_esp32.conf`, and + it is declared only after it was validated on the hardware. Either way, give NSS, RESET and every MCU-driven + RF switch or FEM line `ESP32_GPIO_SLEEP_HOLD_EN` from the start. +- **RAM, not flash, limits the companion.** Boards without PSRAM lower `MAX_CONTACTS`; the classic ESP32 also + trims channels and the offline queue. +- **Classic ESP32 PICO-D4** bootloops in QIO flash mode: `CONFIG_ESPTOOLPY_FLASHMODE_DIO=y`. Console and CLI + are on `uart0`. +- GPIO32 and up are on `&gpio1`: GPIO41 is `<&gpio1 9 ...>`. -**Heltec V4.2 vs V4.3:** The hardware revision is printed on the PCB silkscreen. If -unclear, check GPIO2's default pull: the V4.2 GC1109 PA has an internal pull-down -(GPIO2 reads LOW at boot), while the V4.3 KCT8103L PA has an internal pull-up -(GPIO2 reads HIGH). Only difference in firmware: TX control pin GPIO46→GPIO5. +### MG24 -**Heltec Wireless Tracker V1.1 vs V2:** use `heltec_wireless_tracker` for the -upstream Zephyr V1.1 target and `heltec_wireless_tracker_v2` for the ESP32-S3FN8 -V2 board with KCT8103L PA/FEM. The V2 pin map is documented in -`boards/esp32/heltec_wireless_tracker_v2/README.md`. +- Erase blocks are 8 KB: partition sizes are multiples of 8 KB. +- Enable `&se` (secure element: crypto and the TRNG). +- On the XIAO the default I2C pins are the radio's NSS / RXEN; disable `&i2c0`. -**LilyGo T-Beam v1.2:** Classic ESP32 (PICO-D4) board — several caveats apply: -- Upstream Zephyr DTS models the SX1276 variant; `board.overlay` overrides the radio node to SX1262 on the same SPI3 wiring. -- PICO-D4 rev 1.0 bootloops when the bootloader tries to enable QIO flash mode. `board.conf` forces DIO (`CONFIG_ESPTOOLPY_FLASHMODE_DIO=y`). Any new classic ESP32 board with PICO-D4 needs this. -- Classic ESP32 DRAM is much smaller than ESP32-S3. `board.conf` shrinks contacts/channels/queue (`MAX_CONTACTS=160`, `MAX_CHANNELS=8`, `OFFLINE_QUEUE_SIZE=128`) to fit. -- AXP2101 PMU manages LoRa and GPS power rails via Zephyr regulator + fuel-gauge drivers (auto-enabled from the upstream DTS PMU nodes). Add `CONFIG_FUEL_GAUGE=y` to boards that expose battery state-of-charge over I2C. -- Console/CLI are on `uart0` (onboard USB-UART). No native USB on classic ESP32. +### STM32WL -ESP32 flash uses esptool over USB via `west flash`. Hold BOOT button if the device -doesn't enter download mode automatically. +- No Bluetooth and no USB device. The companion protocol and the CLI both run on USART1; the board selects it + with the `zephcore,companion-uart` chosen node. +- 64 KB SRAM: the companion tables are cut hard in `board.conf`. +- `boards/stm32wl/lora_e5_mini/` is the only reference. -Zephyr's `CONFIG_ESP_SIMPLE_BOOT` is active by default (no MCUBoot). The build -produces a self-contained `zephyr.bin` that the ESP32 ROM bootloader loads directly -from `0x0` — no special first-flash procedure, works on a bare chip. +### SX127x + +Source-only and unsupported: a separate path through Zephyr's loramac-node backend with no RX duty cycle and +no RX boost. `boards/esp32/ttgo_lora32/` is the one board on it, and its `board.conf` shows the overrides. + +## 6. Registering the board + +`zephcore.yml` is the only registry. `build.sh` (and so CI and the release), the Mesh America catalog, +[docs/supported_boards.md](../../../docs/supported_boards.md) and the board count in the root README are all +generated from it. The schema is documented at the top of `zephcore/scripts/board_manifest.py`. + +- **Bring-up:** `target` only. The board builds locally and is listed as source-only. +- **Validated:** add `release:` with the roles to publish. +- **In the configurator:** add `catalog:`. `device` must be MeshCore's exact device name for the board to fold + into their tile ([docs/PROVIDER_CATALOG.md](../../../docs/PROVIDER_CATALOG.md)). + +After every manifest change: -**Exception — WiFi AP OTA** (`boards/common/wifi_ota.conf`): the HTTP OTA updater -writes firmware to MCUBoot slot1 and requires MCUBoot. Add `--sysbuild` when building -with `wifi_ota.conf`, and run `west flash` once to seat MCUBoot before OTA works: ```bash -west build -b /esp32s3/procpu zephcore --pristine --sysbuild -- \ - -DEXTRA_CONF_FILE="boards/common/wifi_ota.conf" -west flash --esp-device COMX +python zephcore/scripts/board_manifest.py check ``` -**GitHub Release downloads for S3/C-series boards are always MCUboot-based**, unlike -the plain-build default above: `build.sh` builds those boards with `--sysbuild` -unconditionally, so the release always has MCUboot @ `0x0` + signed app @ `0x20000`. -Only `-merged.bin` is published for them — flash that one, at offset `0x0`, for both -first flash and updates. It does not touch `storage_partition`/`lfs_partition` -(identity, prefs, contacts, BLE bonds), so routine updates preserve device state. -Classic ESP32 boards (T-Beam, PICO-D4) still publish a self-contained plain `.bin` -for `0x1000`, since they use simple-boot in the release build too. - -### SX127x Boards (loramac-node backend) - -ZephCore can drive SX1272/SX1276/SX1278 via the loramac-node backend — a separate radio path from the native SX126x driver used by all other boards. The TTGO LoRa32 is the only board exercising it. - -**This path is source-only and unsupported.** No release publishes firmware for it, it is not in `build.sh` or the Mesh America catalog, and it has no RX duty cycle and no RX gain boost. Treat it as a starting point to maintain yourself, not as a supported target. - -| Board | Build string | Flash | -|----------------|------------------------------------------------|--------------| -| TTGO LoRa32 | `west build -b ttgo_lora32/esp32/procpu zephcore` | `west flash` | - -SX127x boards require these `board.conf` overrides (the `zephcore_common.conf` default enables the native SX126x path): - -```kconfig -CONFIG_LORA_MODULE_BACKEND_NATIVE=n -CONFIG_LORA_MODULE_BACKEND_LORAMAC_NODE=y -CONFIG_ZEPHCORE_RADIO_SX127X=y -CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE=n # lora_recv_duty_cycle_async not implemented for SX127x -CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=17 # PA_BOOST max without external PA +```bash +python zephcore/scripts/board_manifest.py docs ``` -Classic ESP32 + PICO-D4 also need the DIO flash mode fix (see T-Beam note above). +The notes under each list in `docs/supported_boards.md` are written by hand; add one if the board has +something a user must know before flashing. -**One-time setup required (all ESP32 boards):** +## Quick reference: Wio-SX1262 on a XIAO -``` -# Download Espressif BLE controller blobs (closed-source, required for BLE) -west blobs fetch hal_espressif -``` +Every XIAO carries the Wio-SX1262 on the same D pins: -Run once after `west init`/`west update`. Without it, CMake aborts with a blob -validation error. Re-run after any `west update` that bumps the `hal_espressif` -revision. +| Signal | XIAO pin | nRF52840 | nRF54L15 | MG24 | ESP32-C3 | ESP32-C6 | ESP32-S3 | +|---|---|---|---|---|---|---|---| +| DIO1 | D1 | P0.03 | P1.05 | PC01 | GPIO3 | GPIO1 | GPIO39 | +| RESET | D2 | P0.28 | P1.06 | PC02 | GPIO4 | GPIO2 | GPIO42 | +| BUSY | D3 | P0.05 | P1.07 | PC03 | GPIO5 | GPIO21 | GPIO40 | +| NSS | D4 | P0.04 | P1.10 | PC04 | GPIO6 | GPIO22 | GPIO41 | +| RXEN | D5 | P0.29 | P1.11 | PC05 | GPIO7 | GPIO23 | GPIO38 | +| SCK | D8 | P1.13 | P2.01 | PA03 | GPIO8 | GPIO19 | GPIO7 | +| MISO | D9 | P1.14 | P2.04 | PA04 | GPIO9 | GPIO20 | GPIO8 | +| MOSI | D10 | P1.15 | P2.02 | PA05 | GPIO10 | GPIO18 | GPIO9 | -### nRF54L15 - -| Board | Build string | Flash | -|----------------------|------------------------------------------------------------------------|-----------------| -| XIAO nRF54L15 | `west build -b xiao_nrf54l15/nrf54l15/cpuapp zephcore --no-sysbuild` | `west flash` | -| MinewSemi ME25LS02 | `west build -b me25ls02/nrf54l15/cpuapp zephcore --no-sysbuild` | SWD (`west flash`) | -| Semtech LR2021 LoRa Plus EVK | `west build -b seeed_lr2021_evk/nrf54l15/cpuapp zephcore --no-sysbuild` | `west flash` | - -Requires J-Link or CMSIS-DAP (built into XIAO board via SAMD11 bridge). -The `--no-sysbuild` flag is required (no MCUboot support yet). - -The LR2021 EVK is the Seeed/Semtech kit (SKU 100039980): a XIAO nRF54L15 plus the -LoRa Plus Expansion Board plus a Wio-LR2021 module, all in XIAO sockets sharing one -set of D0..D10 nets. It is a distinct board from `xiao_nrf54l15` above, which is the -same MCU on a Wio-SX1262 carrier with incompatible wiring. Two hardware gotchas -before first boot: the **IDCC jumper** must be fitted or the radio has no power, and -the **LF U.FL pigtail** must be connected before transmitting. The board's `.dts` -header carries the full pin map and the reasoning behind each choice. - -The SoC has no USB peripheral at all, so none of these boards has a UF2 or DFU path — `zephyr.hex` -links at RRAM base 0x0 and is the complete image, written over SWD. On the ME25LS02's MX25LE02 -carrier the USB-C port is a CH340x UART bridge (console only), so it needs an external probe. - -### MG24 (Silicon Labs) - -| Board | Build string | Flash | -|----------------------|-------------------------------------------|-----------------| -| XIAO MG24 | `west build -b xiao_mg24 zephcore` | `west flash` | - -**First-time setup required:** - -``` -# Download Silicon Labs BLE controller blob -west blobs fetch hal_silabs - -# Install pyocd + Silicon Labs device pack (if using pyocd) -pip install pyocd -pyocd pack install EFR32MG24B220F1536IM48 -``` - -Flash with: `west flash --runner pyocd` - -### STM32WL (Seeed LoRa-E5) - -| Board | Build string | Flash | -|----------------|---------------------------------------|--------------| -| LoRa-E5 mini | `west build -b lora_e5_mini zephcore` | `west flash` | - -The STM32WLE5JC integrates an SX1262-class sub-GHz radio (driven by the native -SX126x driver via its STM32WL HAL over the internal `subghzspi` bus). RF -front-end, RF switch (PA4/PA5), TCXO and the +22 dBm RFO_HP PA are described in -upstream `zephyr/dts/arm/seeed_studio/lora-e5.dtsi`. - -STM32WL caveats — different from every other ZephCore platform: -- **No Bluetooth and no USB device.** `boards/common/stm32wl_common.conf` forces - `CONFIG_BT=n`. The console/CLI and the companion protocol both run over - **USART1**, bridged to USB-C by the onboard USB-UART chip. -- **Repeater** uses the USART CLI (add `repeater.conf`). The **companion** speaks - MeshCore serial framing over the same UART through the wired companion - transport (`ZephyrCompanionUSB.cpp`, UART backend: the board's - `zephcore,companion-uart` chosen node selects `CONFIG_ZEPHCORE_COMPANION_SERIAL` - when there is no Bluetooth) — no BLE pairing, the official serial client - connects directly, and a terminal gets the text CLI. -- **RAM-bound, not flash-bound:** 64KB SRAM. The companion's contact/queue - arrays are capped hard in `board.conf` (`MAX_CONTACTS=24`, `OFFLINE_QUEUE_SIZE=8`). - AES tables live in ROM (`MBEDTLS_AES_ROM_TABLES`) to reclaim ~8KB SRAM. -- **TRNG only (no HW CSPRNG):** the STM32 TRNG is enabled as the entropy source - and `CSPRNG_ENABLED` auto-resolves on top; `ZephyrRNG` further conditions - identity seeds with AES-CTR (the timing stages are skipped — SysTick has no - independent slow clock — so the TRNG-fed CSPRNG stages carry the seed). -- **No MCUboot / UF2:** single app partition at flash origin + a LittleFS volume - (see `board.overlay`). Flash over SWD/ST-Link with `west flash` (OpenOCD). - -Build the repeater: -``` -west build -b lora_e5_mini zephcore -- -DEXTRA_CONF_FILE="boards/common/repeater.conf" -``` - -### Building for Repeater Role - -Append `-- -DEXTRA_CONF_FILE="boards/common/repeater.conf"` to any build command: - -``` -west build -b rak4631 zephcore -- -DEXTRA_CONF_FILE="boards/common/repeater.conf" -``` - -### Building for Room Server Role - -Append `-- -DEXTRA_CONF_FILE="boards/common/room_server.conf"` to any build command: - -``` -west build -b rak4631 zephcore -- -DEXTRA_CONF_FILE="boards/common/room_server.conf" -``` - -### Production vs Debug Builds - -Production (no logging, no asserts, reboot-on-fatal) is the **default** — no -extra conf needed. To enable logging, add the debug overlay: - -``` -west build -b rak4631 zephcore -- -DEXTRA_CONF_FILE="boards/common/debug.conf" -``` - -### Repeater + Debug - -``` -west build -b rak4631 zephcore -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/debug.conf" -``` - -All build commands should include `--pristine` when switching between roles or boards. - - -Adding a New Board ------------------- - -There are TWO ways to add a board, depending on whether Zephyr -already has a board definition for your hardware. - - -### PATTERN 1: Existing Zephyr Board (overlay only) - -Use this when your board already exists in Zephyr's tree. -You only need TWO files: board.conf + board.overlay - -Examples: XIAO nRF54L15, XIAO MG24, XIAO ESP32-C3, RAK4631 - -Directory structure: - - zephcore/boards/// - board.conf — Kconfig (name, radio type) - board.overlay — DT overlay (LoRa SPI, partitions, peripherals) - -Steps: - 1. Create directory: `boards///` - Platform folders: nrf52840, nrf54l, mg24, esp32, stm32wl - 2. Copy board.conf and board.overlay from THIS directory - 3. Uncomment the sections matching your platform - 4. Fill in YOUR pin numbers and partition layout - 5. Add a zephcore.yml manifest (copy one from a similar board; schema in - zephcore/scripts/board_manifest.py). No CMake edit is needed: the - boards// directory the board lives in selects its platform. - Leave out `release:` until the board is validated on hardware, then run - `python zephcore/scripts/board_manifest.py check`. - 6. Build and iterate! - - -### PATTERN 2: Fully Custom Board (new DTS) - -Use this when your board does NOT exist in Zephyr's tree. -You need a full board definition: .dts, pinctrl, Kconfig, etc. - -Examples: Ikoka Nano 30dBm, ThinkNode M1 (custom nRF52840 designs) - -Look at existing custom boards as templates: - - zephcore/boards/nrf52840/ikoka_nano_30dbm/ — minimal (LoRa only) - zephcore/boards/nrf52840/thinknode_m1/ — full-featured (EPD, GPS, QSPI, buzzer) - -A full custom board includes: - - board.conf — Kconfig (name, radio, overrides) - board.overlay — DT overlay (usually empty if DTS is complete) - _.dts — Full device tree - _-pinctrl.dtsi — Pin control definitions - board.yml — Board metadata (name, arch, SoC) - Kconfig. — SoC selection - __defconfig — Minimal defconfig - board.cmake — Flash runner config - -**Critical: `zephyr,sram` in the chosen node (nRF52840 only)** - -The nRF52840 SoC DTSI defines `sram0` but does NOT set `zephyr,sram` in -the chosen node. Without it, the linker gets `RAM size = 0` and every -build fails with "region 'RAM' overflowed" regardless of actual RAM use. - -Boards that include `` get this for free. -Boards that use `nrf52_partitions_sdv6.dtsi` or `nrf52_partitions_sdv7.dtsi` -directly (without the upstream include) also get it now — those DTSIs set -`zephyr,sram = &sram0` themselves. - -If you write a fully custom DTS that includes neither, add it yourself: - - chosen { - zephyr,sram = &sram0; - zephyr,code-partition = &code_partition; - ... - }; - - -**nRF52 boards with a user button: button wakeup from System OFF** - -`sys_poweroff()` on nRF52840 enters System OFF (~1µA). To allow waking via -button press (instead of only via USB/charger), GPIO SENSE bits must be set -before poweroff. This is done via the `wakeup-source` property on the -`gpio-keys` node, which the nRF52 GPIO driver handles automatically. - -Boards that define a `buttons:` gpio-keys node must add at the END of their DTS: - - #include "../../common/nrf52_wakeup.dtsi" - -Boards WITHOUT a `buttons` label (ikoka_nano) must NOT include it — -referencing an undefined `&buttons` label is a hard build error. - -**RAK4631 user button:** `gpio-keys` on **P0.09** (NFC1). Short → next page (after -tap window), double-tap → previous page, long press (≥1s) → enter / activate. - - -What Goes in board.conf ------------------------ - -Most hardware features are auto-detected from devicetree. board.conf -should ONLY contain settings that can't be inferred from hardware: - - REQUIRED (all boards): - CONFIG_ZEPHCORE_BOARD_NAME Human-readable name - CONFIG_BT_DIS_MODEL_NUMBER_STR BLE Device Information model - - REQUIRED (nRF52 only): - CONFIG_ZEPHCORE_SD_FWID SoftDevice firmware ID (0x00B6 or 0x0123) - - OPTIONAL (only if needed): - CONFIG_ZEPHCORE_RADIO_LR1110 LR1110 radio (auto-selects SPI) - CONFIG_ZEPHCORE_MAX_CONTACTS Override for RAM-limited boards - CONFIG_HEAP_MEM_POOL_SIZE Override for large displays (>128x64) - CONFIG_SEGGER_RTT_BUFFER_SIZE_UP Shrink RTT on RAM-tight boards - CONFIG_ESPTOOLPY_FLASHSIZE_16MB ESP32 boards with 16MB flash - CONFIG_ESPTOOLPY_FLASHMODE_DIO=y Classic ESP32 PICO-D4 boards (bootloops otherwise) - CONFIG_ESPTOOLPY_FLASHMODE_QIO=n Companion to the DIO override above - CONFIG_FUEL_GAUGE=y Boards with AXP2101 or other I2C fuel gauge - CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM Boards with external PA - CONFIG_ZEPHCORE_MAX_TX_POWER_DBM Boards with external PA - - AUTO-DETECTED (do NOT set in board.conf): - CONFIG_PWM Auto from DT buzzer nodelabel - CONFIG_ZEPHCORE_UI_BUZZER Auto from DT buzzer nodelabel - CONFIG_ZEPHCORE_UI_DISPLAY Auto from DT zephyr,display chosen - CONFIG_SPI Auto from ZEPHCORE_RADIO_LR1110 - CONFIG_NORDIC_QSPI_NOR Auto from DT nordic,qspi-nor node - CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE OFF by default for all roles (boot default only; - runtime toggle via CLI "set rxduty on/off") - CONFIG_ESP_SPIRAM Auto: ON when DT psram0 size > 0 (ESP32-S/C5) - CONFIG_ESP_SPIRAM_SIZE Auto: read from DT psram0 size by upstream Kconfig - - NOT auto-detected (must set in board.conf for ESP32-S3 R8 boards): - CONFIG_SPIRAM_MODE_OCT R8 chips (8 MB OPI octal) need this set - to "y" in board.conf. R2 chips (2 MB QSPI - quad) need nothing — QUAD is the upstream - default. See "ESP32 PSRAM" section below. - - -ESP32 PSRAM (auto-enable, per-board mode on S3 R8) --------------------------------------------------- - -`CONFIG_ESP_SPIRAM=y` is enabled automatically from devicetree by -[zephcore/Kconfig.psram](../../Kconfig.psram). As long as a board's DTS -includes a Zephyr WROOM dtsi with an R-suffix (e.g. `esp32s3_wroom_n16r2.dtsi`, -`esp32s3_wroom_n8r8.dtsi`), PSRAM lights up — no `board.conf` entry needed -for the basics. Boards without PSRAM use a non-R-suffix dtsi (e.g. -`esp32s3_wroom_n8`), which leaves `psram0` at size = 0, so PSRAM stays off. -ESP32-C3/C6 are outside Espressif's PSRAM Kconfig gate entirely — no effect. - -**Mode selection is silicon-strapped, but not auto-pickable here:** upstream -Kconfig.spiram sets an unconditional `default SPIRAM_MODE_QUAD` on the choice, -which wins over any conditional override we'd add downstream. So: - -| Chip suffix | PSRAM size | Mode | board.conf needs | -|-------------|-----------|-----------|----------------------------| -| `R2` | 2 MB | QSPI quad | nothing — upstream default | -| `R8` | 8 MB | OPI octal | `CONFIG_SPIRAM_MODE_OCT=y` | - -If you add a new ESP32-S3 **R8** board (e.g. `esp32s3_wroom_n*r8.dtsi`), -add a one-liner to its board.conf: - -```kconfig -# PSRAM mode — ESP32-S3R8 is silicon-strapped OPI octal. -# Enable is auto-detected from DTS by Kconfig.psram; only the mode is set here. -CONFIG_SPIRAM_MODE_OCT=y -``` - -For aggressive DRAM relief beyond the heap rebalance, additional Kconfigs -`CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y` and `CONFIG_SPIRAM_RODATA=y` move -text/rodata into PSRAM at boot, but introduce PSRAM-cache-miss timing -variability — only enable per-board after verifying radio and BLE paths -still meet their timing budgets. - - -Config Inheritance ------------------- - - prj.conf Always loaded first - | - zephcore_common.conf BLE, storage, input, LoRa, crypto, sensors - | - _common.conf Platform-specific overrides only - | nrf52_common.conf — UF2, USB CDC, DLE, RTT - | nrf54l_common.conf — DLE, RTT - | mg24_common.conf — SiLabs blob stacks, heap - | esp32_common.conf — Espressif blob stacks, heap - | stm32wl_common.conf — BT off, UART companion/CLI - | - board.conf Board name, radio type, board-specific - -DO NOT duplicate settings from parent configs in board.conf! - - -Quick Reference: Wio-SX1262 XIAO Pin Mapping ---------------------------------------------- - -All XIAO boards use the same D-pin assignment for Wio-SX1262: - - Signal | XIAO Pin | nRF52840 | nRF54L15 | MG24 | ESP32-C3 | ESP32-C6 | ESP32-S3 - -------|----------|-----------|-----------|-----------|-----------|-----------|---------- - DIO1 | D1 | P0.03 | P1.05 | PC01 | GPIO3 | GPIO1 | GPIO39 - RESET | D2 | P0.28 | P1.06 | PC02 | GPIO4 | GPIO2 | GPIO42 - BUSY | D3 | P0.05 | P1.07 | PC03 | GPIO5 | GPIO21 | GPIO40 - NSS | D4 | P0.04 | P1.10 | PC04 | GPIO6 | GPIO22 | GPIO41 - RXEN | D5 | P0.29 | P1.11 | PC05 | GPIO7 | GPIO23 | GPIO38 - SCK | D8 | P1.13 | P2.01 | PA03 | GPIO8 | GPIO19 | GPIO7 - MISO | D9 | P1.14 | P2.04 | PA04 | GPIO9 | GPIO20 | GPIO8 - MOSI | D10 | P1.15 | P2.02 | PA05 | GPIO10 | GPIO18 | GPIO9 - -Note: nRF52840 D-pin mapping varies by board (XIAO nRF52840 shown). -RAK4631 has SX1262 integrated — different pinout entirely. - - -RAK4631 LEDs and overlay (RAK4631) ------------------------------------ - -- **Polarity:** Stock Zephyr RAK4631 DTS uses `GPIO_ACTIVE_LOW` for the two user - LEDs (P1.3 / P1.4). On hardware here they are **active-high** — the overlay - overrides `green_led` / `blue_led` to `GPIO_ACTIVE_HIGH`. -- **Aliases:** Overlay sets `led0`→green (heartbeat), `led1`→blue (unread + - LoRa TX blink via `lora-tx-led`). On repeaters, firmware does not drive the - second LED as “unread”; blue stays TX-only. +RXEN and DIO2 together drive the module's RF switch, and DIO3 powers its TCXO at 1.8 V. diff --git a/zephcore/boards/example_board/board.conf b/zephcore/boards/example_board/board.conf index 53223d2..9e1848d 100644 --- a/zephcore/boards/example_board/board.conf +++ b/zephcore/boards/example_board/board.conf @@ -1,110 +1,141 @@ # ============================================================================ -# ZephCore — Universal board.conf Template +# ZephCore — board.conf template # ============================================================================ # -# This file is ONLY for settings that cannot be auto-detected from devicetree -# or Kconfig conditionals. Most hardware features are auto-enabled: +# Only for settings that cannot be derived from devicetree. Most hardware +# features switch themselves on: # -# Feature Auto-detected from +# Feature Enabled by # ─────────────── ────────────────────────────────────────────────── -# Display DT chosen node: zephyr,display -# Buzzer + PWM DT nodelabel: buzzer (compatible "pwm-leds") +# Display DT chosen: zephyr,display +# Buzzer + PWM DT nodelabel: buzzer (child of a "pwm-leds" node) # QSPI flash DT node: compatible "nordic,qspi-nor" -# SPI (LR1110) Kconfig: ZEPHCORE_RADIO_LR1110 selects SPI -# RX duty cycle Kconfig: auto-enabled for companion + SX1262/LLCC68 -# Sensors DT: I2C scan at runtime -# GPS DT: GNSS compatible string on UART +# GPS DT nodelabel: gnss (a GNSS driver on a UART) +# I2C sensors DT: boards/common/sensors-i2c.dtsi, probed at runtime +# Hardware RTC DT: boards/common/rtc-i2c.dtsi, probed at boot +# ESP32 PSRAM DT: psram0 size > 0 (zephcore/Kconfig.psram) # -# Config chain (later files override earlier): -# prj.conf → zephcore_common.conf → _common.conf → THIS FILE +# Config chain (later files override earlier ones): +# prj.conf → zephcore_common.conf → _common.conf +# → esp32c3s3_common.conf (ESP32-S3/C3 only) → THIS FILE +# → auto-included feature confs → your -DEXTRA_CONF_FILE # -# Platform configs are auto-selected in CMakeLists.txt based on BOARD name: -# nrf52 → nrf52_common.conf (UF2, USB CDC, DLE, RTT) -# nrf54l → nrf54l_common.conf (DLE, RTT) -# mg24 → mg24_common.conf (SiLabs blob stacks, larger heap) -# esp32 → esp32_common.conf (Espressif blob stacks, larger heap) +# The platform conf is chosen by the directory this board lives in +# (boards/nrf52840/, nrf54l/, esp32/, mg24/, stm32wl/). There is nothing to +# register in CMakeLists.txt. # -# DO NOT duplicate settings from the common configs! -# -# IMPORTANT: Register your board in CMakeLists.txt! -# Add "OR BOARD MATCHES \"your_board\"" to the correct platform detection -# line (~line 60-75) so the right _common.conf is auto-included. +# DO NOT duplicate settings from the common confs. # # ============================================================================ # ========== REQUIRED: Board Identity ========== -# Name shown on phone app and OLED top bar +# Name reported to the phone app and by the `board` CLI command. 39 characters +# at most (static_assert in app/CompanionMesh.cpp). Match the Arduino MeshCore +# variant's name where one exists. CONFIG_ZEPHCORE_BOARD_NAME="My Board" # BLE Device Information Service model name (shown in phone settings) CONFIG_BT_DIS_MODEL_NUMBER_STR="My Board v1" # ========== REQUIRED (nRF52 only): SoftDevice Firmware ID ========== -# Determines the app start address — MUST match your bootloader's SoftDevice. -# Wrong value = bootloop / bricked device with no USB/BLE/logs. +# Written into the DFU .zip (adafruit-nrfutil --sd-req). It MUST match the +# SoftDevice the board's Adafruit bootloader was flashed with, and the +# partition include in the DTS must be the matching one: # -# Check the Arduino variant's platformio.ini for which linker script it uses: -# nrf52840_s140_v6*.ld → 0x00B6 (app@0x26000) -# nrf52840_s140_v7*.ld → 0x0123 (app@0x27000) +# Arduino variant linker script FWID DTS include +# nrf52840_s140_v6*.ld (app@0x26000) 0x00B6 nrf52_partitions_sdv6.dtsi +# nrf52840_s140_v7*.ld (app@0x27000) 0x0123 nrf52_partitions_sdv7.dtsi # -# CONFIG_ZEPHCORE_SD_FWID=0x00B6 # s140 v6.1.1 (RAK4631, ThinkNode M1/M3) -# CONFIG_ZEPHCORE_SD_FWID=0x0123 # s140 v7.3.0 (XIAO nRF52, Wio Tracker, T1000-E) +# The wrong pair puts the app at the wrong address: bootloop, no USB, no logs. +# +# CONFIG_ZEPHCORE_SD_FWID=0x00B6 # s140 v6.1.1 (RAK4631, ThinkNode M1/M3/M6, Heltec T114) +# CONFIG_ZEPHCORE_SD_FWID=0x0123 # s140 v7.3.0 (XIAO nRF52840, Wio Tracker L1, T1000-E) # ========== OPTIONAL: Radio Type ========== -# Default: RADIO_NATIVE (SX126x, SX127x, LLCC68, STM32WL) — no setting needed. -# Override ONLY for LR11xx boards (SPI is auto-selected): -# CONFIG_ZEPHCORE_RADIO_LR1110=y - -# ========== OPTIONAL: Max Contacts Override ========== -# Default is 350 (Kconfig). Override for RAM-constrained boards: -# nRF52840 + QSPI: 510 (ample storage) -# nRF52840: 350 (default, no override needed) -# ESP32-C6: 350 (default, no override needed) -# nRF54L15: 450 (FLPR core reserves 68KB SRAM) -# ESP32-C3: 300 (limited DRAM) -# MG24: 350 (default, Silabs blob uses 32KB heap) -# CONFIG_ZEPHCORE_MAX_CONTACTS=450 - -# ========== OPTIONAL: Display Heap Override ========== -# The CFB framebuffer is allocated via k_malloc at boot. -# Default heap (2048 in zephcore_common.conf) fits 128x64 OLEDs. -# Larger displays WILL fail silently (-ENOMEM, blank screen). +# Default: ZEPHCORE_RADIO_NATIVE (Zephyr's SX126x driver: SX1261/62/68, LLCC68, +# STM32WL). Nothing to set for those. Otherwise pick the one that matches the +# lora node's compatible: +# CONFIG_ZEPHCORE_RADIO_LR1110=y # semtech,lr1110 (LR1110/LR1120/LR1121) +# CONFIG_ZEPHCORE_RADIO_LR2021=y # semtech,lr2021 # -# Display Resolution Framebuffer Heap needed -# SSD1306/SH1106 128x64 1024 bytes 2048 (default) -# SSD1681 200x200 5000 bytes 6144+ -# SSD1680 296x128 4736 bytes 6144+ +# SX127x goes through the loramac-node backend and needs the whole group +# (source-only path, see boards/esp32/ttgo_lora32/): +# CONFIG_LORA_MODULE_BACKEND_NATIVE=n +# CONFIG_LORA_MODULE_BACKEND_LORAMAC_NODE=y +# CONFIG_ZEPHCORE_RADIO_SX127X=y +# CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=17 + +# ========== OPTIONAL: TX Power (boards with an external PA / FEM) ========== +# Both default to 22 dBm, the SX1262 maximum. With a PA after the chip, the +# chip must be held below the PA's maximum input: +# CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=14 +# CONFIG_ZEPHCORE_MAX_TX_POWER_DBM=19 # hard cap, enforced by the radio adapter + +# ========== OPTIONAL: Companion table sizes (RAM-bound boards) ========== +# Defaults: 350 contacts, 40 channels, 256 queued offline messages. The +# companion is limited by RAM, not flash, so the boards that override these +# lower them. Values in the tree today: +# nRF52840 350 (default) +# nRF54L15 450 (nRF54LM20A: 510) +# ESP32-C3 300 +# ESP32-C6 280 +# ESP32-S3 without PSRAM 130-140 (Heltec V3, Wireless Tracker) +# Classic ESP32 50-140, with MAX_CHANNELS / OFFLINE_QUEUE_SIZE trimmed too +# STM32WL (64 KB SRAM) 12 +# Raise it only after reading the RAM figure in the build's memory report. +# CONFIG_ZEPHCORE_MAX_CONTACTS=300 +# CONFIG_ZEPHCORE_MAX_CHANNELS=8 +# CONFIG_ZEPHCORE_OFFLINE_QUEUE_SIZE=128 + +# ========== OPTIONAL: Display heap (nRF52 / nRF54L with a large panel) ========== +# The CFB framebuffer is k_malloc'd at boot. The 2048-byte default in +# zephcore_common.conf fits a 128x64 OLED (1024 bytes). A larger panel fails +# with -ENOMEM and stays blank, with no other symptom. # +# Panel Resolution Framebuffer Heap in the tree +# SSD1306/SH1106 128x64 1024 bytes 2048 (default) +# ST7735 mono TFT 160x80 1600 bytes 4096 +# SSD1681 e-paper 200x200 5000 bytes 6144 +# +# ESP32 and MG24 already have a heap of tens of KB and need no override. # CONFIG_HEAP_MEM_POOL_SIZE=6144 -# -# On RAM-constrained MCUs (nRF52840 + large display), you may also need: -# CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=1024 # Shrink RTT buffer (saves 3KB) -# ========== OPTIONAL: ESP32 Flash Size Override ========== -# esp32_common.conf defaults to 4MB flash. Override for boards with more: +# ========== OPTIONAL: UI ========== +# Boards whose long press emits KEY_ENTER: two confirms do not fit in the +# default 500 ms window. +# CONFIG_ZEPHCORE_UI_CONFIRM_WINDOW_MS=3000 +# Larger built-in font for high-resolution panels (e-paper). +# CONFIG_ZEPHCORE_DISPLAY_LARGE_FONT=y + +# ========== OPTIONAL: ESP32 ========== +# esp32_common.conf assumes 4 MB flash. Set the real size; sysbuild copies it +# to the MCUboot image (sysbuild/CMakeLists.txt). A mismatch hangs MCUboot at +# "spi_flash: detected chip:". +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +# +# PSRAM is enabled from devicetree. Only the mode of an 8 MB octal part +# (ESP32-S3R8) has to be stated; 2 MB quad (R2) is the upstream default. +# CONFIG_SPIRAM_MODE_OCT=y +# +# Classic ESP32 PICO-D4 bootloops when the bootloader enables QIO flash mode. +# CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_QIO=n +# +# Boards with an I2C fuel gauge / PMU that reports state of charge (AXP2101). +# CONFIG_FUEL_GAUGE=y -# ========== OPTIONAL: TX Power Override (boards with external PA) ========== -# Default: 22 dBm (SX1262 max). Override for boards with external PA -# to prevent overdriving: -# CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=15 # e.g., Station G2 (20dB PA) -# CONFIG_ZEPHCORE_MAX_TX_POWER_DBM=19 # Hard cap - -# ========== OPTIONAL: UART Async API (nRF52840 GPS boards) ========== -# Enable if your board has a GPS module on UART with deferred-init -# (i.e., GPS power is controlled by external GPIOs, not the GNSS driver). -# -# The nRF52840 UARTE is DMA-based. The default ISR modem backend -# emulates byte-by-byte interrupts on top of DMA, which breaks when GPS -# data arrives before the ISR handler is enabled. -# -# WHEN TO ENABLE: -# - Your board uses quectel,lc76g with zephyr,deferred-init -# - GPS module starts transmitting before modem_pipe_open() is called -# -# WHEN NOT NEEDED: -# - luatos,air530z driver (manages power internally via on-off-gpios) -# - GPS at 9600 baud (slow enough for ISR backend) -# - Boards where the GNSS driver controls power (no deferred-init) -# -# CONFIG_UART_ASYNC_API=y +# ========== DO NOT SET HERE ========== +# CONFIG_ZEPHCORE_UI_DISPLAY / _UI_BUZZER / CONFIG_PWM / CONFIG_DISPLAY +# follow devicetree (exception: a board whose panel is an optional +# plug-in forces UI_DISPLAY=y, see boards/nrf52840/rak4631/) +# CONFIG_SPI, CONFIG_LORA selected by the radio type +# CONFIG_NORDIC_QSPI_NOR follows the nordic,qspi-nor node +# CONFIG_ESP_SPIRAM / _SIZE follow the psram0 node +# CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE +# boot default is off for every role; users switch it with `set rxduty` +# CONFIG_ZEPHCORE_ROLE_*, CONFIG_LOG*, CONFIG_BT=n +# role and debug policy come from boards/common/*.conf, never from a board +# CONFIG_CLOCK_CONTROL_NRF_K32SRC_* +# ignored since the Zephyr main pin; the 32.768 kHz source is devicetree +# (&lfclk { k32src = "rc"; }, see board.overlay and docs/adr/0005) diff --git a/zephcore/boards/example_board/board.overlay b/zephcore/boards/example_board/board.overlay index 6605064..6c78c04 100644 --- a/zephcore/boards/example_board/board.overlay +++ b/zephcore/boards/example_board/board.overlay @@ -1,887 +1,1034 @@ /* * ============================================================================ - * ZephCore — Universal board.overlay Template + * ZephCore — board.overlay template * ============================================================================ * - * This overlay adds ZephCore-specific nodes ON TOP of an existing Zephyr - * board definition. Use this when your board already exists in Zephyr's tree - * (e.g., xiao_nrf54l15, xiao_mg24, xiao_esp32c3, rak4631). + * A board.overlay adds ZephCore's nodes on top of a board definition: + * - a board that already exists in Zephyr (rak4631, xiao_nrf54l15, xiao_mg24, + * xiao_esp32c3, heltec_wifi_lora32_v3): this file carries everything + * - a custom board with its own .dts in this directory: the same nodes can + * live in the .dts instead, and board.overlay may be absent (ikoka_nano_30dbm, + * gat562_30s) or hold only the peripherals (lilygo_t3s3) * - * If you're creating a FULLY CUSTOM board (no upstream Zephyr DTS), look at - * boards/nrf52840/ikoka_nano_30dbm/ as a reference — it includes a complete - * .dts, pinctrl, Kconfig, and board.cmake. + * HOW TO USE THIS FILE + * Every example is inside an #if 0 ... #endif pair, so the examples can + * carry real comments and real indentation. Keep the examples for your + * platform, delete the #if 0 and #endif lines around them, delete the rest, + * and CHANGE EVERY PIN. Each example names the board it was taken from: when + * in doubt, that board's files are the reference, not this one. * - * ┌──────────────────────────────────────────────────────────────────────┐ - * │ WHAT THIS FILE DOES │ - * │ │ - * │ 1. Adds LoRa radio on the board's SPI bus │ - * │ 2. Sets flash partitions for NVS + LittleFS │ - * │ 3. Optionally disables conflicting peripherals │ - * │ 4. Adds aliases and chosen nodes for ZephCore │ - * │ │ - * │ PLATFORM DIFFERENCES (see each section): │ - * │ │ - * │ │ Feature │ nRF52840 │ nRF54L15 │ MG24 │ ESP32 │ │ - * │ │──────────────│────────────│───────────│───────────│───────────│ │ - * │ │ SPI bus │ &spi1 │ &spi00 │ &eusart0 │ &spi2 │ │ - * │ │ GPIO prefix │ &gpio0/1 │ &gpio1/2 │ &gpioa-d │ &gpio0 │ │ - * │ │ Flash node │ &flash0 │ cpuapp_rram│ &flash0 │ &flash0 │ │ - * │ │ Erase block │ 4KB │ 4KB │ 8KB │ 4KB │ │ - * │ │ Console │ USB CDC │ &uart20* │ &usart0* │ usb_serial│ │ - * │ │ Pinctrl │ NRF_PSEL() │ NRF_PSEL()│ in DTS │ in DTS │ │ - * │ │ Boot region │ SoftDevice │ MCUboot │ MCUboot │ image hdr │ │ - * │ │ │ - * │ * = already set in Zephyr's board DTS, no override needed │ - * └──────────────────────────────────────────────────────────────────────┘ + * WHAT A BOARD HAS TO PROVIDE + * 1. the lora0 alias and a radio node (sections 1, 2) + * 2. a flash layout with an lfs_partition, mounted at /lfs (section 3) + * 3. a console (section 4) + * everything else is optional (sections 5, 6) + * + * PLATFORM CHEAT SHEET + * + * nRF52840 nRF54L15 MG24 ESP32-C3/C6 ESP32-S3 + * SPI bus &spi2/&spi3 &spi00 &eusart0 &spi2 &spi2 + * GPIO &gpio0/1 &gpio0/1/2 &gpioa-d &gpio0 &gpio0, &gpio1 = GPIO32+ + * Flash node &flash0 &cpuapp_rram &flash0 &flash0 &flash0 + * Erase block 4 KB 4 KB 8 KB 4 KB 4 KB + * BLE bonds NVS partition file in /lfs file in /lfs NVS partition NVS partition + * Console USB CDC ACM UART (bridge) USART0 USB-Serial/JTAG USB-Serial/JTAG + * Flash map common .dtsi here here partitions.overlay + * + * nRF52840: SPIM0 and SPIM1 share their instance with TWIM0 / TWIM1. Put the + * radio on &spi2 or &spi3 if the board uses I2C at all. + * nRF54L15: port P2 has no GPIOTE, so no GPIO interrupt works there. A radio + * IRQ line cannot be on P2, and a button on P2 needs gpio-keys polling-mode. * * SPDX-License-Identifier: MIT */ +/* dt-bindings headers; keep the ones your nodes use. + * lora/sx126x.h SX126X_DIO3_TCXO_* + * input/input-event-codes.h INPUT_KEY_* (buttons) + * adc/nrf-saadc.h NRF_SAADC_AIN* (nRF battery ADC) + * gpio/espressif-esp32-gpio.h ESP32_GPIO_SLEEP_HOLD_EN (ESP32 light sleep) */ +#if 0 +#include +#include +#include +#include +#endif + /* ================================================================== - * SECTION 1: ALIASES AND CHOSEN (REQUIRED) + * SECTION 1: ALIASES AND CHOSEN * - * Every board MUST define: - * lora0 alias — points to LoRa radio node (required) - * settings-partition — NVS for BLE bonds (required) + * What the firmware looks up. Only lora0 is required. * - * Console is ONLY needed if the board doesn't already set it: - * nRF52840: needs zephyr,console = &cdc_acm_uart (USB CDC) - * nRF54L15: already set to &uart20 in board DTS — skip - * MG24: already set to &usart0 in board DTS — skip - * ESP32: needs zephyr,console = &usb_serial + * aliases + * lora0 the radio node REQUIRED + * led0 heartbeat LED + * led1 second LED (unread messages on a companion) + * lora-tx-led blinks while transmitting + * sw0 user button; also the wake source from nRF System OFF + * and from ESP32 light sleep + * buzzer PWM buzzer (the node must also be LABELLED buzzer) + * gps-* GPS control pins, see section 6 + * + * chosen + * zephyr,display the panel; turns the display UI on + * zephyr,settings-partition NVS partition for BLE bonds. nRF52 gets it + * from the partition .dtsi, ESP32 sets it in + * partitions.overlay. nRF54L and MG24 keep bonds + * in a file on /lfs and do NOT set it + * zephyr,code-partition nRF52: &code_partition (section 3) + * zephyr,console / zephyr,shell-uart section 4 + * zephcore,gps-power regulator that powers the GPS (section 6) + * zephcore,companion-uart companion protocol on a plain UART, for + * boards without Bluetooth (lora_e5_mini) * ================================================================== */ / { aliases { lora0 = &lora; - - /* Optional aliases (uncomment if your board has these): - * led0 = &user_led; heartbeat LED - * sw0 = &user_button; primary button - * buzzer = &buzzer; PWM buzzer - * gps-enable = &gps_enable; GPS power control - */ - }; - - chosen { - zephyr,settings-partition = &storage_partition; - - /* --- Console (only if board DTS doesn't already set it) --- - * - * nRF52840 (USB CDC): - * zephyr,console = &cdc_acm_uart; - * zephyr,shell-uart = &cdc_acm_uart; - * zephyr,code-partition = &slot0_partition; - * - * ESP32 (USB Serial/JTAG): - * zephyr,console = &usb_serial; - * zephyr,shell-uart = &usb_serial; - * - * nRF54L15: skip (uart20 already set) - * MG24: skip (usart0 already set) - */ - - /* --- Display (uncomment if board has OLED/LCD/e-paper) --- - * Setting zephyr,display auto-enables CONFIG_DISPLAY, - * CONFIG_CHARACTER_FRAMEBUFFER, and the UI page system. - * No board.conf changes needed. - * HEAP: 128x64 OLEDs fit in the default 2KB heap. - * Larger displays need CONFIG_HEAP_MEM_POOL_SIZE - * override in board.conf (e.g. 6144 for 200x200 EPD). - * - * zephyr,display = &oled; I2C OLED (SH1106, SSD1306) - * zephyr,display = &epd; SPI e-paper (SSD1681, etc.) - */ }; }; /* ================================================================== - * SECTION 2: PERIPHERAL CONFLICTS (platform-specific) + * SECTION 2: LORA RADIO (REQUIRED) * - * Many XIAO boards have I2C default pins that conflict with LoRa - * control signals. You MUST disable the conflicting peripheral. + * Without it there is no mesh. Label the node `lora`. * - * Check your board's DTS for I2C pin assignments and compare with - * your LoRa wiring. Common conflicts: + * SX1261 / SX1262 / SX1268 / LLCC68 (Zephyr's driver, patched in-tree): + * reset-gpios ACTIVE_LOW + * busy-gpios ACTIVE_HIGH + * dio1-gpios interrupt, ACTIVE_HIGH with PULL_DOWN + * dio3-tcxo-voltage only if DIO3 powers a TCXO (SX126X_DIO3_TCXO_1V8...); + * a module with a plain crystal must NOT set it + * tcxo-power-startup-delay-ms + * rx-boosted boosted RX gain (better sensitivity, a little current) + * regulator-ldo only if the module is wired for LDO; DC-DC is the default * - * Board │ Conflict │ Fix - * ───────────────│──────────────────────│──────────────────── - * XIAO nRF54L15 │ i2c22 on P1.10/P1.11│ Disable &i2c22 - * XIAO MG24 │ i2c0 on PC4/PC5 │ Disable &i2c0 - * XIAO ESP32-C3 │ (none) │ No action needed - * XIAO nRF52840 │ (none) │ No action needed - * RAK4631 │ (none) │ No action needed + * RF switch and front end. Read the schematic: what a vendor calls RXEN or + * TXEN is sometimes a module power enable or a FEM control pin. + * dio2-tx-enable DIO2 drives the TX side of the switch (most modules) + * rx-enable-gpios separate RXEN line (Wio-SX1262 needs it AND DIO2) + * tx-enable-gpios separate TXEN line + * antenna-enable-gpios switch or FEM supply that must be on for RX and TX + * lna-bypass-gpios FEM receive-path select (KCT8103L CTX). It enables + * nothing: binding it as a TX enable costs ~69 dB of + * RX. Reference: boards/esp32/heltec_wifi_lora32_v43/ + * A pin that is really a module POWER enable is not a radio property at all: + * delete it from the node and hog it high (boards/nrf52840/rak4631/). + * + * External PA: also cap the chip's output in board.conf + * (CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM / _MAX_TX_POWER_DBM). + * + * ESP32 repeaters that light-sleep: NSS, RESET and every MCU-driven RF switch + * or FEM line need ESP32_GPIO_SLEEP_HOLD_EN, or they float during each sleep + * while the radio keeps receiving (boards/common/pm_esp32.conf). The flag is + * inert without PM, so set it from the start. * ================================================================== */ -/* --- nRF54L15 example: disable I2C that conflicts with NSS/RXEN --- - * - * &i2c22 { - * status = "disabled"; - * }; - */ +#if 0 /* ---- 2A: nRF52840, SX1262, custom board (wio_tracker_l1) ---- + * Pins of a custom board come from -pinctrl.dtsi. */ +&spi2 { + compatible = "nordic,nrf-spim"; + status = "okay"; + cs-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi2_default>; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; -/* --- MG24 example: disable I2C that conflicts with NSS/RXEN --- - * - * &i2c0 { - * status = "disabled"; - * }; - */ + lora: lora@0 { + compatible = "semtech,sx1262"; + reg = <0>; + spi-max-frequency = <8000000>; + reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + dio1-gpios = <&gpio0 7 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + rx-enable-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; + dio2-tx-enable; + dio3-tcxo-voltage = ; + tcxo-power-startup-delay-ms = <5>; + rx-boosted; + }; +}; +#endif + +#if 0 /* ---- 2B: nRF54L15, Wio-SX1262 on the XIAO header (xiao_nrf54l15) ---- */ +/* The XIAO's default I2C pins are the kit's NSS / RXEN. */ +&i2c22 { + status = "disabled"; +}; + +/* SCK / MISO / MOSI are already in the board's pinctrl. */ +&spi00 { + cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + + lora: lora@0 { + compatible = "semtech,sx1262"; + reg = <0>; + spi-max-frequency = <8000000>; + reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + dio1-gpios = <&gpio1 5 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + rx-enable-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; + dio2-tx-enable; + dio3-tcxo-voltage = ; + tcxo-power-startup-delay-ms = <10>; + rx-boosted; + }; +}; +#endif + +#if 0 /* ---- 2C: MG24, Wio-SX1262 on the XIAO header (xiao_mg24) ---- */ +/* Secure element: hardware crypto and the TRNG that feeds mbedTLS / BLE. */ +&se { + status = "okay"; +}; + +/* The XIAO's default I2C pins (PC4 / PC5) are the kit's NSS / RXEN. */ +&i2c0 { + status = "disabled"; +}; + +&eusart0 { + status = "okay"; + cs-gpios = <&gpioc 4 GPIO_ACTIVE_LOW>; /* overrides the default PC7 */ + + lora: lora@0 { + compatible = "semtech,sx1262"; + reg = <0>; + spi-max-frequency = <8000000>; + reset-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpioc 3 GPIO_ACTIVE_HIGH>; + dio1-gpios = <&gpioc 1 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + rx-enable-gpios = <&gpioc 5 GPIO_ACTIVE_HIGH>; + dio2-tx-enable; + dio3-tcxo-voltage = ; + tcxo-power-startup-delay-ms = <10>; + rx-boosted; + }; +}; +#endif + +#if 0 /* ---- 2D: ESP32-C3 / C6, SX1262 (xiao_esp32c3) ---- */ +&spi2 { + cs-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + + lora: lora@0 { + compatible = "semtech,sx1262"; + reg = <0>; + spi-max-frequency = <8000000>; + reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; + dio1-gpios = <&gpio0 3 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + rx-enable-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; + dio2-tx-enable; + dio3-tcxo-voltage = ; + tcxo-power-startup-delay-ms = <10>; + rx-boosted; + }; +}; +#endif + +#if 0 /* ---- 2E: ESP32-S3, SX1262, sleep-hold on the driven lines (xiao_esp32s3) ---- + * GPIO32 and up are on &gpio1: GPIO41 is <&gpio1 9>. */ +&spi2 { + cs-gpios = <&gpio1 9 (GPIO_ACTIVE_LOW | ESP32_GPIO_SLEEP_HOLD_EN)>; /* GPIO41 */ + + lora: lora@0 { + compatible = "semtech,sx1262"; + reg = <0>; + spi-max-frequency = <8000000>; + reset-gpios = <&gpio1 10 (GPIO_ACTIVE_LOW | ESP32_GPIO_SLEEP_HOLD_EN)>; /* GPIO42 */ + busy-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; /* GPIO40 */ + dio1-gpios = <&gpio1 7 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; /* GPIO39 */ + rx-enable-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | ESP32_GPIO_SLEEP_HOLD_EN)>; /* GPIO38 */ + dio2-tx-enable; + dio3-tcxo-voltage = ; + tcxo-power-startup-delay-ms = <10>; + rx-boosted; + }; +}; +#endif + +#if 0 /* ---- 2F: LR1110 / LR1120 / LR1121 (t1000_e) ---- + * ZephCore's own driver. board.conf: CONFIG_ZEPHCORE_RADIO_LR1110=y + * The interrupt is irq-gpios, the TCXO is tcxo-voltage in mV, and the RF + * switch is a set of DIO bitmasks (bit0 = DIO5 ... bit3 = DIO8) taken + * from the module's reference design. */ +&spi1 { + compatible = "nordic,nrf-spim"; + status = "okay"; + cs-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi1_default>; + pinctrl-1 = <&spi1_sleep>; + pinctrl-names = "default", "sleep"; + + lora: lora@0 { + compatible = "semtech,lr1110"; + reg = <0>; + spi-max-frequency = <8000000>; + reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; + irq-gpios = <&gpio1 1 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + tcxo-voltage = <1600>; + tcxo-power-startup-delay-ms = <5>; + rx-boosted; + rfsw-enable = <0x0F>; + rfsw-standby = <0x00>; + rfsw-rx = <0x09>; + rfsw-tx = <0x0B>; + rfsw-tx-hp = <0x0A>; + rfsw-gnss = <0x04>; + }; +}; +#endif + +#if 0 /* ---- 2G: LR2021 (promicro_lr2021) ---- + * ZephCore's own driver. board.conf: CONFIG_ZEPHCORE_RADIO_LR2021=y + * irq-dio names the chip DIO that is wired to irq-gpios. Leave + * tcxo-voltage out on a module without a TCXO: setting it there wedges + * the chip at start-up (BUSY stuck high). Absolute maximum VBAT is about + * 3.7 V; a 5 V feed destroys the module. */ +&spi2 { + compatible = "nordic,nrf-spim"; + status = "okay"; + cs-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi2_default>; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; + /* 0x00 is the LR2021's NOP; Nordic's 0xff default reads as a bogus opcode. */ + overrun-character = <0x00>; + + lora: lora@0 { + compatible = "semtech,lr2021"; + reg = <0>; + spi-max-frequency = <8000000>; + reset-gpios = <&gpio0 9 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>; + irq-gpios = <&gpio0 10 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + irq-dio = <9>; + rx-boosted; + rfsw-enable = <0x03>; + rfsw-standby = <0x00>; + rfsw-rx = <0x03>; + rfsw-tx = <0x02>; + rfsw-tx-hp = <0x03>; + }; +}; +#endif /* ================================================================== - * SECTION 3: LORA RADIO ON SPI (REQUIRED) + * SECTION 3: FLASH LAYOUT AND /lfs (REQUIRED) * - * This is the most important section — without it, no mesh. + * ZephCore keeps identity, prefs, contacts and channels in a LittleFS volume + * on lfs_partition, mounted at /lfs by boards/common/filesystem.dtsi. * - * Pick your SPI bus based on platform: - * nRF52840: &spi1 (or &spi2, &spi3 — avoid &spi0 if using I2C0) - * nRF54L15: &spi00 (XIAO SPI bus, pinctrl already set) - * MG24: &eusart0 (XIAO SPI bus, pinctrl already set) - * ESP32-C3: &spi2 - * ESP32-C6: &spi2 + * Platform Layout + * ────────────── ───────────────────────────────────────────────────────── + * nRF52840 SD v6 SoftDevice 152K | app 680K | NVS 16K | lfs 128K | UF2 48K + * nRF52840 SD v7 SoftDevice 156K | app 676K | NVS 16K | lfs 128K | UF2 48K + * nRF54L15 MCUboot 64K (reserved) | app 1272K | lfs 92K + * MG24 MCUboot 48K (reserved) | app 1344K | lfs 144K + * ESP32-S3 MCUboot 64K | slot0 | slot1 | NVS 16K | lfs 384K + * ESP32-C3/C6 MCUboot 64K | slot0 | slot1 | lpcore | lfs 192K | NVS 16K + * Classic ESP32 stock app slots | lfs 192K | NVS 16K + * STM32WL app at flash origin | lfs (boards/stm32wl/lora_e5_mini/) * - * GPIO syntax differs per MCU: - * nRF52840: <&gpio0 PIN FLAGS> or <&gpio1 PIN FLAGS> - * nRF54L15: <&gpio1 PIN FLAGS> or <&gpio2 PIN FLAGS> - * MG24: <&gpioa PIN FLAGS>, <&gpioc PIN FLAGS>, etc. - * ESP32: <&gpio0 PIN FLAGS> - * - * SX1262 pin connections: - * cs-gpios — SPI chip select (NSS), ACTIVE_LOW - * reset-gpios — Radio reset, ACTIVE_LOW - * busy-gpios — Busy indicator, ACTIVE_HIGH - * dio1-gpios — Interrupt (RX/TX done), ACTIVE_HIGH with PULL_DOWN - * - * RF switch (pick what your module needs): - * dio2-tx-enable — DIO2 controls TX (most modules) - * rx-enable-gpios — Separate RXEN pin (Wio-SX1262 needs both) - * - * TCXO (if your module has one): - * dio3-tcxo-voltage — DIO3 provides TCXO power (use 1.8V) - * Use: SX126X_DIO3_TCXO_1V8 (from sx126x.h) - * - * rx-boosted — Better RX sensitivity (slight power increase) - * - * CHANGE ALL PINS to match your board! + * Every partition node written by hand needs + * compatible = "zephyr,mapped-partition"; + * (Zephyr main). Without it the code that opens the partition does not + * compile: "..._PARTITION_ID undeclared". The nRF52 .dtsi files below already + * carry what they need. * + * Once a board has shipped, lfs and NVS must keep their offset and size: a + * firmware that moves them erases every user's identity, contacts and bonds. * ================================================================== */ -/* ┌─────────────────────────────────────────┐ - * │ EXAMPLE A: nRF52840 — custom SPI pins │ - * │ (pinctrl defined in separate .dtsi) │ - * └─────────────────────────────────────────┘ - * - * &spi1 { - * compatible = "nordic,nrf-spim"; - * status = "okay"; - * cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; - * pinctrl-0 = <&spi1_default>; - * pinctrl-1 = <&spi1_sleep>; - * pinctrl-names = "default", "sleep"; - * - * lora: lora@0 { - * compatible = "semtech,sx1262"; - * reg = <0>; - * spi-max-frequency = <8000000>; - * reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; - * busy-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; - * dio1-gpios = <&gpio0 7 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; - * dio2-tx-enable; - * dio3-tcxo-voltage = ; - * tcxo-power-startup-delay-ms = <5>; - * }; - * }; - */ +#if 0 /* ---- 3A: nRF52840 (rak4631, wio_tracker_l1) ---- + * + * Do not write this map by hand. Include the shared layout that matches + * the SoftDevice the board's Adafruit bootloader carries; the same + * choice sets CONFIG_ZEPHCORE_SD_FWID in board.conf: + * + * nrf52_partitions_sdv6.dtsi s140 v6, app at 0x26000, FWID 0x00B6 + * nrf52_partitions_sdv7.dtsi s140 v7, app at 0x27000, FWID 0x0123 + * + * The Arduino MeshCore variant's platformio.ini names the linker script + * (nrf52840_s140_v6*.ld or _v7*.ld). The wrong one places the app at the + * wrong address: the board bootloops with no USB, BLE or logs. + * + * The include defines code_partition, storage_partition (NVS), + * lfs_partition and the reserved regions, sets zephyr,sram and + * zephyr,settings-partition, and mounts /lfs. Put it at the END of the + * file. The delete lines apply when the base DTS includes + * , as upstream boards do. */ +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &storage_partition; -/* ┌─────────────────────────────────────────┐ - * │ EXAMPLE B: nRF54L15 (XIAO) — spi00 │ - * │ SCK/MISO/MOSI already in board pinctrl │ - * └─────────────────────────────────────────┘ - * - * &spi00 { - * cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; - * - * lora: lora@0 { - * compatible = "semtech,sx1262"; - * reg = <0>; - * spi-max-frequency = <8000000>; - * reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; - * busy-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; - * dio1-gpios = <&gpio1 5 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; - * rx-enable-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; - * dio2-tx-enable; - * dio3-tcxo-voltage = ; - * tcxo-power-startup-delay-ms = <10>; - * rx-boosted; - * }; - * }; - */ +/ { + chosen { + zephyr,code-partition = &code_partition; + }; +}; -/* ┌─────────────────────────────────────────┐ - * │ EXAMPLE C: MG24 (XIAO) — eusart0 │ - * │ SCK/MISO/MOSI already in board pinctrl │ - * │ Default CS is PC7 — override to PC4 │ - * └─────────────────────────────────────────┘ - * - * &eusart0 { - * status = "okay"; - * cs-gpios = <&gpioc 4 GPIO_ACTIVE_LOW>; - * - * lora: lora@0 { - * compatible = "semtech,sx1262"; - * reg = <0>; - * spi-max-frequency = <8000000>; - * reset-gpios = <&gpioc 2 GPIO_ACTIVE_LOW>; - * busy-gpios = <&gpioc 3 GPIO_ACTIVE_HIGH>; - * dio1-gpios = <&gpioc 1 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; - * rx-enable-gpios = <&gpioc 5 GPIO_ACTIVE_HIGH>; - * dio2-tx-enable; - * dio3-tcxo-voltage = ; - * tcxo-power-startup-delay-ms = <10>; - * rx-boosted; - * }; - * }; - */ +#include "../../common/nrf52_partitions_sdv7.dtsi" +#endif -/* ┌─────────────────────────────────────────┐ - * │ EXAMPLE D: ESP32-C3/C6 — spi2 │ - * │ SCK/MISO/MOSI already in board pinctrl │ - * └─────────────────────────────────────────┘ - * - * &spi2 { - * cs-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; - * - * lora: lora@0 { - * compatible = "semtech,sx1262"; - * reg = <0>; - * spi-max-frequency = <8000000>; - * reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>; - * busy-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; - * dio1-gpios = <&gpio0 3 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; - * rx-enable-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; - * dio2-tx-enable; - * dio3-tcxo-voltage = ; - * tcxo-power-startup-delay-ms = <10>; - * rx-boosted; - * }; - * }; - */ +#if 0 /* ---- 3B: nRF54L15, 1428 KB RRAM (xiao_nrf54l15) ---- + * The 64 KB MCUboot region stays reserved although builds are + * --no-sysbuild; slot0 grows into slot1. No NVS partition: bonds are a + * settings file on /lfs. */ +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &storage_partition; + +&cpuapp_rram { + partitions { + slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; + label = "image-0"; + reg = <0x10000 0x13E000>; /* 1272 KB */ + }; + + lfs_partition: partition@14e000 { + compatible = "zephyr,mapped-partition"; + label = "lfs"; + reg = <0x14E000 0x17000>; /* 92 KB */ + }; + }; +}; + +#include "../../common/filesystem.dtsi" +#endif + +#if 0 /* ---- 3C: MG24, 1536 KB flash, 8 KB erase blocks (xiao_mg24) ---- + * Partition sizes must be multiples of 8 KB. No NVS partition: bonds + * are a settings file on /lfs. */ +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &storage_partition; + +&flash0 { + partitions { + slot0_partition: partition@c000 { + compatible = "zephyr,mapped-partition"; + label = "image-0"; + reg = <0x0C000 0x150000>; /* 1344 KB */ + }; + + lfs_partition: partition@15c000 { + compatible = "zephyr,mapped-partition"; + label = "lfs"; + reg = <0x15C000 0x24000>; /* 144 KB */ + }; + }; +}; + +#include "../../common/filesystem.dtsi" +#endif + +#if 0 /* ---- 3D: ESP32-S3 / C3 / C6 (xiao_esp32s3, xiao_esp32c3) ---- + * The map is NOT here. It is in partitions.overlay beside this file, + * which is also fed to MCUboot (see the template). This file only + * mounts the volume. */ +#include "../../common/filesystem.dtsi" +#endif + +#if 0 /* ---- 3E: classic ESP32, 4 MB (ttgo_tbeam) ---- + * Simple boot only, no MCUboot and no partitions.overlay: the stock app + * slots stay, and the stock 192 KB storage_partition becomes lfs with a + * new 16 KB NVS above it. */ +/delete-node/ &storage_partition; + +/ { + chosen { + zephyr,settings-partition = &storage_partition; + }; +}; + +&flash0 { + partitions { + lfs_partition: partition@3b0000 { + compatible = "zephyr,mapped-partition"; + label = "lfs"; + reg = <0x3B0000 0x30000>; + }; + + storage_partition: partition@3e0000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x3E0000 0x4000>; + }; + }; +}; + +#include "../../common/filesystem.dtsi" +#endif /* ================================================================== - * SECTION 4: FLASH PARTITIONS (REQUIRED) + * SECTION 4: CONSOLE AND USB * - * ZephCore needs three partitions: - * slot0_partition — Application code - * storage_partition — NVS for BLE bonds + settings - * lfs_partition — LittleFS for identity, contacts, channels - * - * Most boards also have a boot region (SoftDevice, MCUboot, or image - * header) that should NOT be touched. - * - * CRITICAL: Erase block size determines minimum NVS size: - * 4KB erase (nRF52, nRF54L, ESP32): 2 × 4KB = 8KB minimum - * 8KB erase (MG24): 2 × 8KB = 16KB minimum - * - * General approach: - * 1. Delete default partitions (slot0, slot1, storage) - * 2. Expand slot0 into slot1 space (no DFU for companion device) - * 3. Add NVS + LittleFS at the end + * Check build/zephyr/zephyr.dts first: many board definitions already choose + * the right console. * + * nRF52840 USB CDC ACM. Upstream boards that include Zephyr's + * cdc_acm_serial.dtsi (rak4631) already have it. A custom board + * declares it itself (4A). + * nRF54L15 no USB peripheral at all. Console is the UART the board DTS + * chooses (a debug-probe or CH340 bridge). + * MG24 USART0, set by the board DTS. + * ESP32-C3 / C6 USB-Serial/JTAG (4B). + * ESP32-S3 USB-Serial/JTAG for the console, and the same port becomes a + * native USB CDC companion transport in companion builds (4C). + * Classic ESP32, Heltec V3 uart0 through the on-board USB-UART bridge. + * STM32WL USART1; also carries the companion protocol (no Bluetooth). * ================================================================== */ -/* ┌─────────────────────────────────────────────────────────────────────┐ - * │ EXAMPLE A: nRF52840 (1MB flash, 4KB erase, SoftDevice + UF2) │ - * │ │ - * │ CRITICAL: You MUST match the SoftDevice version that your board's │ - * │ bootloader was flashed with. Getting this wrong means the app is │ - * │ placed at the wrong address and the board will bootloop / brick. │ - * │ │ - * │ Check the Arduino variant's platformio.ini for your board: │ - * │ s140_v6 linker script → app starts at 0x26000 (SD is 152KB) │ - * │ s140_v7 linker script → app starts at 0x27000 (SD is 156KB) │ - * │ │ - * │ The bootloader address also varies. Parse the bootloader hex to │ - * │ find it (highest address region), or read UICR 0x10001014 via │ - * │ J-Link. Common values: 0xF4000 (v6 boards), 0xED000 (v7 boards). │ - * │ │ - * │ s140 v6 boards (app@0x26000): │ - * │ RAK4631, heltec_t114, heltec_mesh_solar, lilygo_techo, │ - * │ lilygo_techo_lite, mesh_pocket, meshtiny, nano_g2_ultra, │ - * │ promicro, rak3401, rak_wismesh_tag, keepteen_lt1, │ - * │ thinknode_m1/m3/m6 │ - * │ │ - * │ s140 v7 boards (app@0x27000): │ - * │ xiao_nrf52, wio_wm1110, wio-tracker-l1, wio-tracker-l1-eink, │ - * │ sensecap_solar, t1000-e, minewsemi_me25ls01, │ - * │ ikoka_nano_nrf, ikoka_stick_nrf, ikoka_handheld_nrf │ - * │ │ - * │ Example: s140 v6 (RAK4631, bootloader@0xF4000): │ - * │ 0x00000 - 0x26000 (152KB) SoftDevice s140 v6.1.1 (reserved) │ - * │ 0x26000 - 0xD4000 (696KB) Application │ - * │ 0xD4000 - 0xD6000 (8KB) NVS (2 × 4KB) │ - * │ 0xD6000 - 0xF4000 (120KB) LittleFS │ - * │ 0xF4000 - 0x100000 (48KB) UF2 bootloader (reserved) │ - * │ │ - * │ Example: s140 v7 (xiao_nrf52, bootloader@0xED000): │ - * │ 0x00000 - 0x27000 (156KB) SoftDevice s140 v7.3.0 (reserved) │ - * │ 0x27000 - 0xD4000 (692KB) Application │ - * │ 0xD4000 - 0xD6000 (8KB) NVS (2 × 4KB) │ - * │ 0xD6000 - 0xED000 (92KB) LittleFS │ - * │ 0xED000 - 0x100000 (76KB) UF2 bootloader (reserved) │ - * └─────────────────────────────────────────────────────────────────────┘ - * - * NOTE (Zephyr main, since 2026-09-19): every partition node in the examples - * below also needs compatible = "zephyr,mapped-partition"; (omitted from - * each one for brevity). Without it, code that opens the partition fails to - * compile with "..._PARTITION_ID undeclared" — see any - * boards/esp32//partitions.overlay for the full form. - * - * /delete-node/ &boot_partition; - * /delete-node/ &slot0_partition; - * /delete-node/ &slot1_partition; - * /delete-node/ &storage_partition; - * - * --- s140 v6 variant (app@0x26000) --- - * - * &flash0 { - * partitions { - * compatible = "fixed-partitions"; - * #address-cells = <1>; - * #size-cells = <1>; - * - * boot_partition: partition@0 { - * label = "softdevice"; - * read-only; - * reg = <0x00000 0x26000>; - * }; - * slot0_partition: partition@26000 { - * label = "Application"; - * reg = <0x26000 0xAE000>; - * }; - * storage_partition: partition@d4000 { - * label = "storage"; - * reg = <0xD4000 0x2000>; - * }; - * lfs_partition: partition@d6000 { - * label = "lfs"; - * reg = <0xD6000 0x1E000>; - * }; - * uf2_partition: partition@f4000 { - * label = "UF2"; - * read-only; - * reg = <0xF4000 0xC000>; - * }; - * }; - * }; - * - * --- s140 v7 variant (app@0x27000) --- - * - * &flash0 { - * partitions { - * compatible = "fixed-partitions"; - * #address-cells = <1>; - * #size-cells = <1>; - * - * boot_partition: partition@0 { - * label = "softdevice"; - * read-only; - * reg = <0x00000 0x27000>; - * }; - * slot0_partition: partition@27000 { - * label = "Application"; - * reg = <0x27000 0xAD000>; - * }; - * storage_partition: partition@d4000 { - * label = "storage"; - * reg = <0xD4000 0x2000>; - * }; - * lfs_partition: partition@d6000 { - * label = "lfs"; - * reg = <0xD6000 0x17000>; - * }; - * uf2_partition: partition@ed000 { - * label = "UF2"; - * read-only; - * reg = <0xED000 0x13000>; - * }; - * }; - * }; - */ +#if 0 /* ---- 4A: nRF52840 custom board, USB CDC ACM (thinknode_m1) ---- */ +/ { + chosen { + zephyr,console = &cdc_acm_uart; + zephyr,shell-uart = &cdc_acm_uart; + }; +}; -/* ┌─────────────────────────────────────────────────────────────────────┐ - * │ EXAMPLE B: nRF54L15 (1428KB RRAM, 4KB erase, MCUboot reserved) │ - * │ │ - * │ 0x00000 - 0x10000 (64KB) MCUboot (reserved) │ - * │ 0x10000 - 0x14E000 (1272KB) Application │ - * │ 0x14E000 - 0x150000 (8KB) NVS (2 × 4KB) │ - * │ 0x150000 - 0x165000 (84KB) LittleFS │ - * │ │ - * │ NOTE: nRF54L uses RRAM not flash — node is &cpuapp_rram │ - * └─────────────────────────────────────────────────────────────────────┘ - * - * /delete-node/ &slot0_partition; - * /delete-node/ &slot1_partition; - * /delete-node/ &storage_partition; - * - * &cpuapp_rram { - * partitions { - * slot0_partition: partition@10000 { - * label = "image-0"; - * reg = <0x10000 0x13E000>; - * }; - * storage_partition: partition@14e000 { - * label = "storage"; - * reg = <0x14E000 0x2000>; - * }; - * lfs_partition: partition@150000 { - * label = "lfs"; - * reg = <0x150000 0x15000>; - * }; - * }; - * }; - */ +zephyr_udc0: &usbd { + compatible = "nordic,nrf-usbd"; + status = "okay"; -/* ┌─────────────────────────────────────────────────────────────────────┐ - * │ EXAMPLE C: MG24 (1536KB flash, 8KB erase, MCUboot reserved) │ - * │ │ - * │ 0x00000 - 0x0C000 (48KB) MCUboot (reserved) │ - * │ 0x0C000 - 0x15C000 (1344KB) Application │ - * │ 0x15C000 - 0x160000 (16KB) NVS (2 × 8KB — MG24 has 8KB erase!) │ - * │ 0x160000 - 0x180000 (128KB) LittleFS │ - * └─────────────────────────────────────────────────────────────────────┘ - * - * /delete-node/ &slot0_partition; - * /delete-node/ &slot1_partition; - * /delete-node/ &storage_partition; - * - * &flash0 { - * partitions { - * slot0_partition: partition@c000 { - * label = "image-0"; - * reg = <0x0C000 0x150000>; - * }; - * storage_partition: partition@15c000 { - * label = "storage"; - * reg = <0x15C000 0x4000>; - * }; - * lfs_partition: partition@160000 { - * label = "lfs"; - * reg = <0x160000 0x20000>; - * }; - * }; - * }; - */ + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + }; +}; +#endif -/* ┌─────────────────────────────────────────────────────────────────────┐ - * │ EXAMPLE D: ESP32 (4MB flash, 4KB erase, simple boot at 0x0) │ - * │ │ - * │ Default layout already has large storage_partition at end. │ - * │ We just split it into NVS + LittleFS: │ - * │ │ - * │ 0x3B0000 - 0x3B2000 (8KB) NVS (2 × 4KB) │ - * │ 0x3B2000 - 0x3E0000 (184KB) LittleFS │ - * │ │ - * │ NOTE: Application partition is NOT changed — just storage split. │ - * └─────────────────────────────────────────────────────────────────────┘ - * - * /delete-node/ &storage_partition; - * - * &flash0 { - * partitions { - * storage_partition: partition@3b0000 { - * label = "storage"; - * reg = <0x3B0000 0x2000>; - * }; - * lfs_partition: partition@3b2000 { - * label = "lfs"; - * reg = <0x3B2000 0x2E000>; - * }; - * }; - * }; - */ +#if 0 /* ---- 4B: ESP32-C3 / C6 (xiao_esp32c3) ---- */ +/ { + chosen { + zephyr,console = &usb_serial; + zephyr,shell-uart = &usb_serial; + }; +}; + +/* WiFi carries the repeater's OTA update. */ +&wifi { + status = "okay"; +}; +#endif + +#if 0 /* ---- 4C: ESP32-S3 with native USB (xiao_esp32s3) ---- + * + * Including esp32s3_usb_otg.dtsi is the switch: CMake looks for that + * include in board.overlay and, for companion builds, adds + * boards/common/esp32s3_usb.conf, which hands the port to USB OTG for + * the companion protocol and moves the console to uart0. Repeater, + * room-server and debug builds keep the console on USB-Serial/JTAG. + * Consequence: esptool cannot auto-reset an S3 companion into download + * mode. Use `start dfu` or a 1200-baud touch (docs/adr/0002). + * + * Do NOT include esp32s3_console_uart0.dtsi here unless the board's + * USB-C really is a UART bridge on GPIO43/44 (Heltec V3, ThinkNode M9): + * it would strand the console of every non-USB build on those pins. */ +/ { + chosen { + zephyr,console = &usb_serial; + zephyr,shell-uart = &usb_serial; + }; +}; + +#include "../../common/esp32s3_usb_otg.dtsi" +#endif /* ================================================================== - * SECTION 5: LITTLEFS MOUNT (REQUIRED) + * SECTION 5: CLOCKS (Nordic) * - * Always include at the END of the overlay (after lfs_partition is - * defined). This provides the standard /lfs mount point. + * The 32.768 kHz source is a devicetree property since the Zephyr main pin. + * The old CONFIG_CLOCK_CONTROL_NRF_K32SRC_* choices are dropped with nothing + * but a Kconfig warning, which leaves a crystal-less board waiting on a + * crystal that is not there (docs/adr/0005). * ================================================================== */ -/* #include "../../common/filesystem.dtsi" */ +#if 0 /* ---- 5A: board WITHOUT a 32.768 kHz crystal (promicro_sx1262) ---- */ +&lfclk { + k32src = "rc"; + k32src-accuracy-ppm = <250>; +}; +#endif + +#if 0 /* ---- 5B: nRF54L, clock nodes the upstream board DTS leaves off (xiao_nrf54l15) ---- + * The split clock driver builds only the clocks that are enabled; the + * BLE controller does not compile without lfclk. With no LF crystal add + * the two k32src lines of 5A as well (me25ls02). */ +&xo { + status = "okay"; +}; + +&lfclk { + status = "okay"; +}; +#endif /* ================================================================== - * SECTION 5b: QSPI EXTERNAL FLASH (nRF52840 only, OPTIONAL) + * SECTION 6: OPTIONAL PERIPHERALS * - * WARNING: The Nordic QSPI NOR driver hangs FOREVER at boot if no - * flash chip responds (k_sem_take K_FOREVER in nrf_qspi_nor.c). - * This bricks the device — no USB, no BLE, no logs. - * - * ONLY add a QSPI node if your board physically has a QSPI flash chip. - * If unsure, leave QSPI out. The DataStore falls back to internal - * flash gracefully (20 blob slots instead of 100). - * - * The QSPI driver and page size are auto-configured: - * - CONFIG_NORDIC_QSPI_NOR auto-enables from DT (Zephyr default) - * - CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 in nrf52_common.conf - * - No board.conf changes needed — just add the DT nodes below - * - * To enable QSPI: - * 1. Add the qspi_flash node below (uncommented) - * 2. Add #include "../../common/qspi-ext.dtsi" for /ext mount - * - * To disable QSPI on a board whose upstream DTS has &qspi enabled: - * Add &qspi { status = "disabled"; }; to override upstream DTS - * (e.g., RAK4631 has &qspi enabled in upstream but no chip on mini base) - * - * Boards WITH QSPI: Wio Tracker L1, ThinkNode M1 - * Boards WITHOUT: RAK4631 (mini base), most XIAO boards + * None of these is needed for a working mesh node. Each one switches its + * feature on by being present; board.conf stays untouched unless noted. * ================================================================== */ -/* --- QSPI disabled (safe default) --- - * - * &qspi { - * status = "disabled"; - * }; - */ +#if 0 /* ---- LEDs ---- */ +/ { + aliases { + led0 = &led_green; /* heartbeat */ + led1 = &led_blue; /* unread (companion) */ + lora-tx-led = &led_blue; /* TX blink */ + }; -/* --- QSPI enabled (only if chip is physically present) --- - * - * &qspi { - * status = "okay"; - * pinctrl-0 = <&qspi_default>; - * pinctrl-1 = <&qspi_sleep>; - * pinctrl-names = "default", "sleep"; - * - * qspi_flash: qspi_flash@0 { - * compatible = "nordic,qspi-nor"; - * reg = <0>; - * writeoc = "pp4io"; - * readoc = "read4io"; - * sck-frequency = <16000000>; - * jedec-id = [85 60 15]; - * sfdp-bfp = [ - * e5 20 f1 ff ff ff ff 01 44 eb 08 6b 08 3b 04 bb - * ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 - * 10 d8 00 ff - * ]; - * size = <16777216>; - * has-dpd; - * t-enter-dpd = <3000>; - * (t-exit-dpd comes from qspi-ext.dtsi: 50 us) - * quad-enable-requirements = "S2B1v1"; - * }; - * }; - * - * #include "../../common/qspi-ext.dtsi" - * - * &qspi_flash { - * partitions { - * compatible = "fixed-partitions"; - * #address-cells = <1>; - * #size-cells = <1>; - * qspi_storage_partition: partition@0 { - * label = "qspi_storage"; - * reg = <0x00000000 0x00200000>; - * }; - * }; - * }; - */ + leds { + compatible = "gpio-leds"; + led_green: led_0 { + gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; + label = "Green LED"; + }; -/* ================================================================== - * SECTION 6: USB CDC (nRF52840 only) - * - * nRF52840 boards that use USB for console need this. - * Other platforms skip this entirely: - * nRF54L15: console via UART20 (SAMD11 bridge) - * MG24: console via USART0 - * ESP32: console via built-in USB-serial - * ================================================================== */ + led_blue: led_1 { + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; + label = "Blue LED"; + }; + }; +}; +#endif -/* --- nRF52840 USB CDC --- - * - * zephyr_udc0: &usbd { - * compatible = "nordic,nrf-usbd"; - * status = "okay"; - * - * cdc_acm_uart: cdc_acm_uart { - * compatible = "zephyr,cdc-acm-uart"; - * }; - * }; - */ +#if 0 /* ---- Battery voltage (rak4631) ---- + * + * mV = raw * vbat-mv-multiplier / 4096, so the multiplier is the ADC's + * full-scale voltage in mV times the divider ratio. An individual unit + * is trimmed at runtime with `set adc.multiplier`. + * + * If the divider is switched, label its regulator-fixed node + * vbat_enable: the firmware turns it on around each read only + * (t1000_e, heltec_wifi_lora32_v3). + * + * Other platforms use the same zephyr,user node with their own ADC + * channel: xiao_nrf54l15 (gain 1/4), heltec_wifi_lora32_v3 (ESP32; mind + * that the node labelled adc0 is ADC unit 1). */ +/ { + zephyr,user { + io-channels = <&adc 3>; + vbat-mv-multiplier = <6161>; + }; +}; +&adc { + #address-cells = <1>; + #size-cells = <0>; -/* ================================================================== - * SECTION 7: OPTIONAL PERIPHERALS - * - * These are NOT required for basic mesh operation but add features. - * See boards/nrf52840/ikoka_nano_30dbm/ for a fully annotated - * real-world example with all options explained. - * ================================================================== */ + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; + zephyr,resolution = <12>; + }; +}; +#endif -/* --- Battery ADC (nRF52840 only — other platforms don't have SAADC) --- - * - * / { - * zephyr,user { - * io-channels = <&adc 7>; - * vbat-mv-multiplier = <7200>; - * }; - * }; - * - * &adc { - * status = "okay"; - * #address-cells = <1>; - * #size-cells = <0>; - * channel@7 { - * reg = <7>; - * zephyr,gain = "ADC_GAIN_1_6"; - * zephyr,reference = "ADC_REF_INTERNAL"; - * zephyr,acquisition-time = ; - * zephyr,input-positive = ; - * zephyr,resolution = <12>; - * }; - * }; - */ +#if 0 /* ---- One button, board WITHOUT a display (t1000_e) ---- + * + * gpio-keys emits KEY_0. The longpress filter turns it into KEY_A + * (short) or KEY_F / KEY_POWER (held 1 s: power off). The multi-tap + * filter counts KEY_A presses: + * 1 tap → KEY_1 next page (no-op without a display) + * 2 taps → KEY_B LED heartbeat on/off + * 3 taps → KEY_D buzzer / notification mode + * 4 taps → KEY_C GPS on/off + * 5 taps → KEY_E flood advert (last entry, fires immediately) + * + * The sw0 alias makes the button the wake source from System OFF. */ +/ { + aliases { + sw0 = &user_button; + }; -/* --- LEDs (any platform) --- - * - * / { - * leds { - * compatible = "gpio-leds"; - * user_led: led_0 { - * gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; - * label = "User LED"; - * }; - * }; - * }; - */ + buttons: buttons { + compatible = "gpio-keys"; -/* --- Buttons with input filter chain (any platform) --- - * - * / { - * buttons: buttons { - * compatible = "gpio-keys"; - * /* nRF52 only: omit wakeup-source here; use the common include below instead. - * * Other platforms: add wakeup-source; directly if the driver supports it. */ - * user_button: button_0 { - * gpios = <&gpio0 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; - * zephyr,code = ; - * }; - * }; - * - * user_btn_longpress { - * compatible = "zephyr,input-longpress"; - * input = <&buttons>; - * input-codes = ; - * short-codes = ; /* short press → multi-tap input */ - * long-codes = ; /* long press (≥1s) → deep sleep */ - * long-delay-ms = <1000>; - * }; - * - * /* Multi-tap action mapping (standard ZephCore assignment): - * * 1 tap (400ms wait) → KEY_1 = page next - * * 2 taps (400ms wait) → KEY_B = LED heartbeat toggle - * * 3 taps (400ms wait) → KEY_D = buzzer mute toggle - * * 4 taps (400ms wait) → KEY_C = GPS on/off - * * 5 taps (immediate) → KEY_E = flood advert */ - * user_btn_multitap { - * compatible = "zephcore,input-multi-tap"; - * input-codes = ; - * tap-codes = ; - * tap-delay-ms = <400>; - * }; - * }; - * - * /* nRF52 boards: add this at the END of the DTS file so the button GPIO - * * is configured for System OFF wakeup (press button to boot after shutdown). - * * Do NOT add for boards without a 'buttons' label (ikoka_nano, rak4631). */ - * #include "../../common/nrf52_wakeup.dtsi" - */ + user_button: button_0 { + gpios = <&gpio0 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + zephyr,code = ; + label = "User Button"; + }; + }; -/* --- GPS on UART (nRF52840 typically on &uart0) --- - * - * Supported: "luatos,air530z", "quectel,lc76g", "gnss-nmea-generic" - * - * Two patterns depending on who controls GPS power: - * - * PATTERN A: Driver-managed power (simple, GPS at 9600 baud) - * Driver uses on-off-gpios to control power internally. - * UART ISR backend works fine (driver enables power after ISR is ready). - * No deferred-init needed, no CONFIG_UART_ASYNC_API needed. - * Example: Wio Tracker L1 (L76K via air530z driver) - * - * &uart0 { - * current-speed = <9600>; - * gnss: gnss { - * compatible = "luatos,air530z"; - * on-off-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; - * }; - * }; - * - * - * PATTERN B: External GPIO power with deferred init (GPS at 115200 baud) - * GPS power is managed by ZephyrGPSManager via multiple GPIOs. - * Must use zephyr,deferred-init so driver init waits for GPS power-up. - * Must use CONFIG_UART_ASYNC_API=y in board.conf (see notes there). - * Example: T1000-E (AG3335 via lc76g driver, PAIR protocol) - * - * WHY DEFERRED INIT: The GNSS driver's init() immediately opens the - * modem pipe and sends commands. Without deferred-init, the driver - * tries to talk to the GPS before it's powered → guaranteed timeout. - * With deferred-init, ZephyrGPSManager powers up GPIO first, then - * calls device_init() to start the driver conversation. - * - * &uart0 { - * compatible = "nordic,nrf-uarte"; - * status = "okay"; - * current-speed = <115200>; - * pinctrl-0 = <&uart0_default>; - * pinctrl-1 = <&uart0_sleep>; - * pinctrl-names = "default", "sleep"; - * - * gnss: gnss { - * compatible = "quectel,lc76g"; - * pps-mode = "GNSS_PPS_MODE_DISABLED"; - * zephyr,deferred-init; - * }; - * }; - * - * - * GPS POWER GPIO ALIASES (add to aliases block above): - * For Pattern A: only gps-enable is needed (shared with on-off-gpios) - * For Pattern B: define all GPIOs your GPS module requires - * - * aliases { - * gps-enable = &gps_en_pin; Required: GPS main power - * gps-vrtc-enable = &gps_vrtc_pin; Optional: VRTC backup power (warm standby) - * gps-reset = &gps_reset_pin; Optional: GPS hardware reset - * gps-sleep-int = &gps_sleep_pin; Optional: GPS sleep/wake interrupt - * gps-rtc-int = &gps_rtcint_pin; Optional: GPS RTC interrupt (held LOW) - * gps-resetb = &gps_resetb_pin; Optional: Active-LOW reset (INPUT_PULLUP!) - * }; - * - * GPS GPIO nodes (add to root / { } block): - * - * gps_power: gps-power { - * compatible = "gpio-leds"; - * gps_en_pin: gps_en { - * gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; - * label = "GPS Enable"; - * }; - * }; - * - * IMPORTANT for AG3335-based boards (T1000-E, etc.): - * gps-resetb (active-LOW reset) MUST be configured as INPUT_PULLUP. - * Without pull-up, the pin floats LOW → AG3335 stays in permanent - * reset → zero UART output. ZephyrGPSManager handles this for - * power-on (INPUT_PULLUP) and power-off (OUTPUT_LOW). - */ + user_btn_longpress { + compatible = "zephyr,input-longpress"; + input = <&buttons>; + input-codes = ; + short-codes = ; + long-codes = ; + long-delay-ms = <1000>; + }; -/* --- OLED Display on I2C (any platform with I2C) --- - * - * Supported: "sinowealth,sh1106", "solomon,ssd1306fb" - * Remember to set zephyr,display = &oled; in chosen block. - * - * &i2c0 { - * zephyr,concat-buf-size = <256>; - * oled: sh1106@3c { - * compatible = "sinowealth,sh1106"; - * reg = <0x3c>; - * width = <128>; - * height = <64>; - * segment-offset = <2>; - * page-offset = <0>; - * display-offset = <0>; - * multiplex-ratio = <63>; - * segment-remap; - * com-invdir; - * }; - * }; - */ + /* No `input` phandle: KEY_A comes from the longpress pseudo-device, not + * from &buttons, so this filter must listen to every device. */ + user_btn_multitap { + compatible = "zephcore,input-multi-tap"; + input-codes = ; + tap-codes = ; + tap-delay-ms = <400>; + }; +}; -/* --- E-Paper Display on SPI (any platform with SPI) --- - * - * Supported: "solomon,ssd1681" (1.54" 200x200 mono) - * Remember to set zephyr,display = &epd; in chosen block. - * - * IMPORTANT — HEAP: The CFB framebuffer is allocated via k_malloc at boot. - * The default heap (2048) is sized for 128x64 OLEDs (1024 bytes). - * Larger displays need more heap in board.conf: - * - * Display Resolution Framebuffer Heap needed - * SSD1306/SH1106 128x64 1024 bytes 2048 (default) - * SSD1681 200x200 5000 bytes 6144+ - * SSD1680 296x128 4736 bytes 6144+ - * - * If the heap is too small, cfb_framebuffer_init() silently fails - * with -ENOMEM and the display stays blank (white for e-paper). - * - * The MIPI DBI node wraps SPI with DC and RST GPIOs. - * The display node is a child of the MIPI DBI node. - * - * If your EPD has a separate power/boost-converter enable GPIO, - * add a regulator-fixed node with regulator-boot-on (see ThinkNode M1). - * - * / { - * mipi_dbi { - * compatible = "zephyr,mipi-dbi-spi"; - * spi-dev = <&spi3>; - * dc-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; - * reset-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; - * #address-cells = <1>; - * #size-cells = <0>; - * - * epd: ssd16xxfb@0 { - * compatible = "solomon,ssd1681"; - * mipi-max-frequency = <4000000>; - * reg = <0>; - * width = <200>; - * height = <200>; - * busy-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; - * }; - * }; - * }; - */ +/* nRF52 only, at the END of the file, and only if a node labelled `buttons` + * exists (a missing label is a build error). */ +#include "../../common/nrf52_wakeup.dtsi" +#endif -/* --- I2C Environment Sensors (any platform with I2C) --- - * - * Auto-detected at runtime. Just include the common dtsi. - * - * &i2c0 { - * #include "../../common/sensors-i2c.dtsi" - * }; - */ +#if 0 /* ---- One button, board WITH a display (rak4631) ---- + * + * 1 tap → KEY_1 next page + * 2 taps → KEY_LEFT previous page + * long press → KEY_ENTER activate the item on the page + * + * board.conf: CONFIG_ZEPHCORE_UI_CONFIRM_WINDOW_MS=3000, because two + * long presses do not fit in the default 500 ms confirm window. + * Two-button boards map one button straight to KEY_ENTER (thinknode_m1). */ +/ { + aliases { + sw0 = &user_button; + }; -/* --- Latched supply rail / true power-off (soft-power boards) --- + buttons: buttons { + compatible = "gpio-keys"; + + user_button: button_0 { + gpios = <&gpio0 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + zephyr,code = ; + label = "User Button"; + }; + }; + + user_btn_longpress { + compatible = "zephyr,input-longpress"; + input = <&buttons>; + input-codes = ; + short-codes = ; + long-codes = ; + long-delay-ms = <1000>; + }; + + page_btn_multitap { + compatible = "zephcore,input-multi-tap"; + input-codes = ; + tap-codes = ; + tap-delay-ms = <400>; + }; +}; + +#include "../../common/nrf52_wakeup.dtsi" +#endif + +/* ---- GPS ----------------------------------------------------------- * - * Only needed if your board has NO mechanical power switch and instead keeps - * its own supply alive by asserting a "hold" GPIO — typically a companion I/O - * controller that gates a DCDC regulator (see muzi works R1 Neo). + * Label the GNSS node `gnss`. Drivers in use: "gnss-nmea-generic" (a passive + * NMEA listener; works with anything) and "luatos,air530z" (CASIC / PCAS + * receivers: Air530Z, L76K). * - * On such a board sys_poweroff() alone is not a power-off. The SoC reaches - * System OFF at ~1 uA, but nRF GPIO output latches persist across the - * transition, so the hold line stays asserted and the rail keeps draining the - * battery. Declare the pins here and ui_prepare_for_system_off() releases them - * as its very last step, after the wake button has been observed released. + * Power control is separate from the driver. The GPS manager uses whichever + * of these the board declares; the pins are children of a "gpio-leds" node, + * named through aliases: * - * Order is preserved and matters: downstream loads first, rail latch LAST. + * gps-enable supply switch; or the module's standby pin when that + * is the only control the board has (wio_tracker_l1) + * gps-wakeup module standby pin on a board that ALSO has a supply + * switch: standby keeps the supply (hot start), a full + * off cuts it (sensecap_solar) + * gps-reset hardware reset line + * gps-resetb active-low reset that must idle as an input with + * pull-up; left floating it holds an AG3335 in reset + * gps-vrtc-enable backup (VRTC) supply, kept on for warm starts + * gps-sleep-int, gps-rtc-int AG3335 sleep / RTC interrupt pins (t1000_e) * - * Pins are usually the same ones a gpio-hog asserts at boot. That is fine and - * intended — a hog is init-time configuration, not a runtime reservation, so - * the hog does the early assert and this node does the shutdown release. + * chosen zephcore,gps-power a regulator instead of a pin: a + * regulator-fixed node, or a PMU rail (heltec_t096, + * ttgo_tbeam) * - * Boards without this node are entirely unaffected (the loop compiles away). - * - * / { - * poweroff_gpios: poweroff-gpios { - * compatible = "zephcore,poweroff-gpios"; - * gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>, radio module power - * <&gpio0 29 GPIO_ACTIVE_HIGH>, I/O controller "MCU on" - * <&gpio0 13 GPIO_ACTIVE_HIGH>; rail latch - cuts power - * }; - * }; - */ + * Do not hand a rail to the GPS manager unless it feeds the GPS alone. A + * shared rail (sensors, display, a PA boost converter) is hogged on instead + * (rak4631, rak3401_1watt). + * ------------------------------------------------------------------- */ + +#if 0 /* ---- GPS A: NMEA module with no power control (rak4631, xiao_nrf54l15) ---- */ +&uart1 { + status = "okay"; + current-speed = <9600>; + + gnss: gnss-nmea-generic { + compatible = "gnss-nmea-generic"; + }; +}; +#endif + +#if 0 /* ---- GPS B: CASIC receiver, standby pin driven by the driver (wio_tracker_l1) ---- + * The same pin is also exposed as gps-enable so the GPS manager can + * switch it without going through the driver. */ +/ { + aliases { + gps-enable = &gps_enable_pin; + }; + + gps_en: gps-enable { + compatible = "gpio-leds"; + + gps_enable_pin: gps_enable { + gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; + label = "GPS Enable"; + }; + }; +}; + +&uart0 { + current-speed = <9600>; + + gnss: gnss { + compatible = "luatos,air530z"; + on-off-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; + }; +}; +#endif + +#if 0 /* ---- GPS C: module that needs a power-up sequence first (t1000_e) ---- + * zephyr,deferred-init keeps the driver from starting at boot; the GPS + * manager sequences the pins, then initialises the device. The T1000-E + * declares gps-vrtc-enable, gps-rtc-int and gps-resetb the same way. */ +/ { + aliases { + gps-enable = &gps_enable_pin; + gps-reset = &gps_reset_pin; + }; + + gps_en: gps-enable { + compatible = "gpio-leds"; + + gps_enable_pin: gps_enable { + gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; + label = "GPS Enable"; + }; + }; + + gps_rst: gps-reset { + compatible = "gpio-leds"; + + gps_reset_pin: gps_reset { + gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + label = "GPS Reset"; + }; + }; +}; + +&uart0 { + compatible = "nordic,nrf-uarte"; + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; + + gnss: gnss { + compatible = "gnss-nmea-generic"; + zephyr,deferred-init; + }; +}; +#endif + +#if 0 /* ---- GPS D: dedicated GPS rail behind a regulator (heltec_t096) ---- */ +/ { + chosen { + zephcore,gps-power = &gps_power; + }; + + gps_power: gps-power { + compatible = "regulator-fixed"; + regulator-name = "gps-power"; + enable-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; + regulator-boot-on; + startup-delay-us = <10000>; + }; +}; +#endif + +#if 0 /* ---- OLED on I2C (rak4631; the buffer line is from wio_tracker_l1) ---- + * Compatibles in use: "solomon,ssd1306", "sinowealth,sh1106". + * A bus that is "nordic,nrf-twim" needs zephyr,concat-buf-size: a page + * write is 1 command byte + 128 data bytes, and without the buffer the + * panel ACKs its init and then never shows anything. Harmless on the + * legacy "nordic,nrf-twi" bus the rak4631 uses. */ +/ { + chosen { + zephyr,display = &ssd1306; + }; +}; + +&i2c0 { + status = "okay"; + zephyr,concat-buf-size = <256>; + + ssd1306: ssd1306@3c { + compatible = "solomon,ssd1306"; + reg = <0x3c>; + width = <128>; + height = <64>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <63>; + segment-remap; + com-invdir; + prechargep = <0x22>; + }; +}; +#endif + +#if 0 /* ---- E-paper on SPI (thinknode_m1) ---- + * board.conf: CONFIG_HEAP_MEM_POOL_SIZE=6144 on nRF. The framebuffer is + * allocated at boot and a 200x200 panel does not fit the default heap; + * the only symptom is a blank panel. + * A panel with its own supply or boost enable gets a regulator-fixed + * node with regulator-boot-on. TFT panels: heltec_t114, heltec_t096. */ +/ { + chosen { + zephyr,display = &epd; + }; + + mipi_dbi { + compatible = "zephyr,mipi-dbi-spi"; + spi-dev = <&spi3>; + dc-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <0>; + + epd: ssd16xxfb@0 { + compatible = "solomon,ssd1681"; + mipi-max-frequency = <4000000>; + reg = <0>; + width = <200>; + height = <200>; + busy-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; + }; + }; +}; +#endif + +#if 0 /* ---- I2C sensors and hardware RTC ---- + * sensors-i2c.dtsi declares every supported environment and power + * sensor; parts that are not fitted fail their probe and are skipped. + * A board with a BME680 swaps the default BME280 node (rak4631). + * rtc-i2c.dtsi is opt-in: include it ONLY when a battery-backed RTC is + * fitted, because its addresses collide with common parts (an IMU at + * 0x68). A sealed board with no sensor header includes neither. */ +&i2c0 { + status = "okay"; + + #include "../../common/sensors-i2c.dtsi" + #include "../../common/rtc-i2c.dtsi" +}; +#endif + +#if 0 /* ---- PWM buzzer (thinknode_m1) ---- + * The LABEL buzzer is what enables the buzzer code; the alias is how the + * code finds the PWM channel. Both are needed. */ +/ { + aliases { + buzzer = &buzzer; + }; + + pwmbuzzer { + compatible = "pwm-leds"; + + buzzer: buzzer { + pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>; + }; + }; +}; + +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-names = "default", "sleep"; +}; +#endif + +#if 0 /* ---- QSPI flash, /ext volume (nRF52840: wio_tracker_l1, thinknode_m1) ---- + * + * With /ext mounted, contacts, channels and blobs move to the external + * flash; identity and prefs stay on /lfs. + * + * Declare it ONLY if the chip is fitted. An upstream board DTS that + * enables &qspi for a chip your variant lacks must be overridden with + * status = "disabled", or the driver hangs the boot (rak4631). + * + * jedec-id, sfdp-bfp and size (in BITS) are per chip; the values below + * are a P25Q16H, 2 MB, in single-bit mode. qspi-ext.dtsi adds the /ext + * mount and defers the flash probe until first use, because a probe at + * boot can beat a cold supply rail. It must come after the node. */ +&qspi { + status = "okay"; + + qspi_flash: qspi_flash@0 { + compatible = "nordic,qspi-nor"; + reg = <0>; + writeoc = "pp"; + readoc = "fastread"; + sck-frequency = <16000000>; + jedec-id = [85 60 15]; + sfdp-bfp = [ + e5 20 f1 ff ff ff ff 01 44 eb 08 6b 08 3b 04 bb + ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 + 10 d8 00 ff + ]; + size = <16777216>; + has-dpd; + t-enter-dpd = <3000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + qspi_storage_partition: partition@0 { + label = "qspi_storage"; + reg = <0x00000000 0x00200000>; + }; + }; + }; +}; + +#include "../../common/qspi-ext.dtsi" +#endif + +#if 0 /* ---- Latched supply rail: true power-off (muziworks_r1neo) ---- + * + * Only for a board with no mechanical power switch that keeps its own + * supply alive through a "hold" GPIO. There, sys_poweroff() alone is not + * a power-off: GPIO output latches persist in System OFF, so the rail + * stays up and drains the battery. The pins listed here are released as + * the very last step of shutdown, in order: loads first, rail latch + * LAST. They are usually the pins a gpio-hog asserts at boot, which is + * intended. */ +/ { + poweroff_gpios: poweroff-gpios { + compatible = "zephcore,poweroff-gpios"; + gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>, /* radio module power */ + <&gpio0 29 GPIO_ACTIVE_HIGH>, /* I/O controller "MCU on" */ + <&gpio0 13 GPIO_ACTIVE_HIGH>; /* rail latch: cuts power */ + }; +}; +#endif diff --git a/zephcore/boards/example_board/partitions.overlay b/zephcore/boards/example_board/partitions.overlay new file mode 100644 index 0000000..80ae764 --- /dev/null +++ b/zephcore/boards/example_board/partitions.overlay @@ -0,0 +1,154 @@ +/* + * ============================================================================ + * ZephCore — partitions.overlay template (ESP32-S3 and ESP32-C3/C6 boards) + * ============================================================================ + * + * The flash map of an ESP32-S3 / C-series board lives in this file, not in + * board.overlay, because it is fed to BOTH images of a sysbuild build: the app + * (last entry of EXTRA_DTC_OVERLAY_FILE, zephcore/CMakeLists.txt) and MCUboot + * (mcuboot_EXTRA_DTC_OVERLAY_FILE, zephcore/sysbuild/CMakeLists.txt). If the two + * disagree, MCUboot looks for the OTA image at a different address than the app + * wrote it to and every WiFi OTA "succeeds", then boots the old firmware. + * Background: docs/adr/0001-esp32-boot-and-flash-layout.md. + * + * Who needs this file: + * ESP32-S3, ESP32-C3, ESP32-C6 yes (repeater and release builds use MCUboot) + * Classic ESP32 no: simple boot only; split the stock + * storage_partition in board.overlay instead + * (boards/esp32/ttgo_tbeam/board.overlay) + * nRF52 / nRF54L / MG24 / STM32WL no: see board.overlay + * + * Rules every layout here follows: + * - slot0 at 0x10000, right after the 64 KB MCUboot region. This is also the + * Arduino / ESP-IDF app offset, which keeps the release image compatible + * with the Mesh America "flash-update" flow. + * - slot0 and slot1 the same size (MCUboot swap). + * - `storage` (16 KB NVS, BLE bonds) and `lfs` (LittleFS: identity, prefs, + * contacts) at the top of flash. Once a board has shipped, never move + * them: a reflash that moves them wipes every user's identity and bonds. + * - every partition carries compatible = "zephyr,mapped-partition" (Zephyr + * main); without it the code that opens the partition fails to compile + * with "..._PARTITION_ID undeclared". + * + * THE BASE-TABLE TRAP. Espressif ships two stock tables per flash size, + * partitions_0x0_default_.dtsi and partitions_0x0_amp_.dtsi. The amp + * one also defines slot0_appcpu_partition and slot1_appcpu_partition. The + * /delete-node/ lists below match the DEFAULT table: on top of an amp table the + * two appcpu nodes survive and overlap the new slot1 and storage. Nothing checks + * partition overlap at build time, so it builds, boots and is silently wrong. + * - Custom board DTS: include partitions_0x0_default_.dtsi + * (boards/esp32/lilygo_t3s3/lilygo_t3s3_procpu.dts). + * - Upstream board whose DTS includes the amp table: add the two appcpu + * labels to the delete list. + * + * After the first build, read the map back instead of trusting this file: + * build/zephyr/zephyr.dts for a plain build; for --sysbuild compare + * build/zephcore/zephyr/zephyr.dts with build/mcuboot/zephyr/zephyr.dts. A + * correct map is contiguous, free of overlaps and ends at the flash size. + * + * Layouts in the tree, by chip and flash size (copy the nearest sibling): + * + * Chip / flash storage (NVS) lfs Sibling board + * ─────────────── ────────────────── ─────────────────── ────────────────────── + * S3, 4 MB 0x39C000 16 KB 0x3A0000 384 KB xiao_esp32s3, lilygo_t3s3 + * S3, 8 MB 0x79C000 16 KB 0x7A0000 384 KB heltec_wifi_lora32_v3 + * S3, 16 MB 0xF9C000 16 KB 0xFA0000 384 KB heltec_wifi_lora32_v4, station_g2 + * C3/C6, 4 MB 0x3E0000 16 KB 0x3B0000 192 KB xiao_esp32c3, xiao_esp32c6 + * + * Remove the #if 0 / #endif around the one layout that fits and delete the + * other. board.overlay must still end with + * #include "../../common/filesystem.dtsi" + * which mounts &lfs_partition at /lfs. + * + * SPDX-License-Identifier: MIT + */ + +#if 0 /* ---------- LAYOUT A: ESP32-S3, 4 MB flash (xiao_esp32s3) ---------- */ + +/delete-node/ &sys_partition; +/delete-node/ &slot0_lpcore_partition; +/delete-node/ &slot1_lpcore_partition; +/delete-node/ &storage_partition; +/delete-node/ &coredump_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +/ { + chosen { + zephyr,settings-partition = &storage_partition; + }; +}; + +&flash0 { + partitions { + /* App slots — equal size, slot0 at 0x10000. */ + slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; + label = "image-0"; + reg = <0x10000 0x1C6000>; + }; + slot1_partition: partition@1D6000 { + compatible = "zephyr,mapped-partition"; + label = "image-1"; + reg = <0x1D6000 0x1C6000>; + }; + /* BLE bond NVS, 16 KB. */ + storage_partition: partition@39C000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x39C000 0x4000>; + }; + /* LittleFS, 384 KB (identity / prefs / contacts). */ + lfs_partition: partition@3A0000 { + compatible = "zephyr,mapped-partition"; + label = "lfs"; + reg = <0x3A0000 0x60000>; + }; + }; +}; + +#endif /* LAYOUT A */ + +#if 0 /* ---------- LAYOUT B: ESP32-C3 / C6, 4 MB flash (xiao_esp32c3) ---------- */ + +/* The stock LP-core slots at 0x3A0000 are kept, so the app slots stop there. */ +/delete-node/ &sys_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &storage_partition; + +/ { + chosen { + zephyr,settings-partition = &storage_partition; + }; +}; + +&flash0 { + partitions { + /* App slots — equal size, slot0 at 0x10000. */ + slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; + label = "image-0"; + reg = <0x10000 0x1C8000>; + }; + slot1_partition: partition@1d8000 { + compatible = "zephyr,mapped-partition"; + label = "image-1"; + reg = <0x1D8000 0x1C8000>; + }; + /* LittleFS, 192 KB (identity / prefs / contacts). */ + lfs_partition: partition@3b0000 { + compatible = "zephyr,mapped-partition"; + label = "lfs"; + reg = <0x3B0000 0x30000>; + }; + /* BLE bond NVS, 16 KB. */ + storage_partition: partition@3e0000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x3E0000 0x4000>; + }; + }; +}; + +#endif /* LAYOUT B */ diff --git a/zephcore/boards/example_board/zephcore.yml b/zephcore/boards/example_board/zephcore.yml new file mode 100644 index 0000000..166a934 --- /dev/null +++ b/zephcore/boards/example_board/zephcore.yml @@ -0,0 +1,50 @@ +# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py +# +# TEMPLATE. Copy to boards///zephcore.yml. This copy is not +# read by anything: the manifest loader only scans the platform directories. +# +# The manifest is the one place a board is registered. build.sh (the release +# matrix), gen_provider_catalog.py, docs/supported_boards.md and the board +# count in the root README are all generated from it. After editing: +# +# python zephcore/scripts/board_manifest.py check +# python zephcore/scripts/board_manifest.py docs + +# REQUIRED. The `west build -b` string, qualifiers included. It must start with +# the directory name: +# nRF52840 / MG24 / STM32WL / ESP32-C3 my_board +# ESP32-S3 my_board/esp32s3/procpu +# ESP32-C6 my_board/esp32c6/hpcore +# Classic ESP32 my_board/esp32/procpu +# nRF54L15 my_board/nrf54l15/cpuapp +target: my_board + +# OPTIONAL, ESP32 only. CMake reads these by line match, so keep the exact +# two-space-indented ` name: true` form. +# capabilities: +# wifi: true # companion also gets the WiFi (TCP) transport: the +# # board has the RAM for WiFi and BLE together (ADR 0009) +# light_sleep: true # repeater light-sleeps between events. Declare it only +# # after boards/common/pm_esp32.conf's checks pass on +# # hardware; qualifying wiring alone is not enough + +# Leave `release:` out while the board is in bring-up: it then builds locally +# and is listed as "source-only", but no release carries firmware for it. +# release: +# roles: [companion, repeater] # also: room_server, observer +# variants: # extra published builds of this board +# - id: noscreen # artifact name infix +# conf: no_display.conf # in this directory; a no_display.overlay +# # beside it is paired automatically +# subtitle: "No screen" + +# Mesh America configurator tile. Needs `release:`. `device` must be MeshCore's +# exact name for the board, or it will not fold into their tile; take it from +# https://raw.githubusercontent.com/meshcore-dev/flasher.meshcore.io/main/config.json +# (docs/PROVIDER_CATALOG.md). `maker` is a key of MAKERS in gen_provider_catalog.py. +# catalog: +# device: "Vendor My Board" +# maker: vendor +# new: true # MeshCore has no tile for it: publish our own +# img: lora.svg # with `new`: stock art, or own_img: my_board.jpg +# subtitle: "30 dBm" # tells two builds in one tile apart