/* * ============================================================================ * ZephCore — board.overlay template * ============================================================================ * * 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) * * 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 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 * * What the firmware looks up. Only lora0 is required. * * aliases * lora0 the radio node REQUIRED * led0 heartbeat LED * led1 second LED (unread messages on a companion) * lora-tx-led blinks while transmitting * heartbeat-pwm-led, lora-tx-pwm-led * same two roles, brightness-adjustable (leds.brightness); * point both at one pwm-leds node to share a physical LED * 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) * zephcore,kiss-uart KISS modem role on its own UART (header pins * to a host SBC); the baud rate is the node's * current-speed * ================================================================== */ / { aliases { lora0 = &lora; }; }; /* ================================================================== * SECTION 2: LORA RADIO (REQUIRED) * * Without it there is no mesh. Label the node `lora`. * * 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 * * 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. * ================================================================== */ #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"; 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: FLASH LAYOUT AND /lfs (REQUIRED) * * ZephCore keeps identity, prefs, contacts and channels in a LittleFS volume * on lfs_partition, mounted at /lfs by boards/common/filesystem.dtsi. * * 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/) * * 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. * ================================================================== */ #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; / { chosen { zephyr,code-partition = &code_partition; }; }; #include "../../common/nrf52_partitions_sdv7.dtsi" #endif #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: CONSOLE AND USB * * 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). * ================================================================== */ #if 0 /* ---- 4A: nRF52840 custom board, USB CDC ACM (thinknode_m1) ---- */ / { chosen { zephyr,console = &cdc_acm_uart; zephyr,shell-uart = &cdc_acm_uart; }; }; zephyr_udc0: &usbd { compatible = "nordic,nrf-usbd"; status = "okay"; cdc_acm_uart: cdc_acm_uart { compatible = "zephyr,cdc-acm-uart"; }; }; #endif #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: CLOCKS (Nordic) * * 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). * ================================================================== */ #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 6: OPTIONAL PERIPHERALS * * 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. * ================================================================== */ #if 0 /* ---- LEDs ---- */ / { aliases { led0 = &led_green; /* heartbeat */ led1 = &led_blue; /* unread (companion) */ lora-tx-led = &led_blue; /* TX blink */ }; leds { compatible = "gpio-leds"; led_green: led_0 { gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; label = "Green LED"; }; led_blue: led_1 { gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; label = "Blue LED"; }; }; }; #endif #if 0 /* ---- Heartbeat/TX LED, brightness-adjustable (heltec_t096, heltec_wifi_lora32_v43) ---- * One physical LED standing in for both led0 and lora-tx-led, but wired * through a pwm-leds node instead of gpio-leds so leds.brightness can * dim it. Point heartbeat-pwm-led and lora-tx-pwm-led at the same node * to share it, same as the plain-GPIO led0/lora-tx-led pair above. * * ESP32 only: also declare a plain led0 (gpio-leds) for this pin if the * board wants one, but do NOT add GPIO setup for it in the board's * main() -- src/server_main_common.cpp and app/main_observer.cpp skip * that already (CONFIG_ZEPHCORE_LED_PWM) when either PWM alias is * present, because on ESP32 configuring the pin as plain GPIO after * the PWM driver has claimed it takes the pin back from the LEDC * peripheral: the GPIO matrix only routes one signal to a pin at a * time. nRF52 PWM and GPIO don't share hardware that way, so this does * not apply there. */ / { aliases { heartbeat-pwm-led = &pwm_led_white; lora-tx-pwm-led = &pwm_led_white; }; pwmleds { compatible = "pwm-leds"; pwm_led_white: pwm_led_0 { pwms = <&ledc0 0 PWM_MSEC(10) PWM_POLARITY_NORMAL>; label = "White PWM LED"; }; }; }; &ledc0 { status = "okay"; pinctrl-0 = <&ledc0_default>; pinctrl-names = "default"; #address-cells = <1>; #size-cells = <0>; channel0@0 { reg = <0x0>; timer = <0>; }; }; #endif #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>; 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 #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; }; buttons: buttons { compatible = "gpio-keys"; user_button: button_0 { gpios = <&gpio0 8 (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>; }; /* 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>; }; }; /* 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 #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; }; 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 ----------------------------------------------------------- * * 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). * * 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: * * 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) * * Nothing else is needed for `set gps standby`: on every board a standby * longer than that limit (default one hour) is a full power-off, a shorter * one keeps the module's state if the pins above give a way to (gps-wakeup, * or gps-vrtc-enable). With gps-enable alone the two are the same. * * chosen zephcore,gps-power a regulator instead of a pin: a * regulator-fixed node, or a PMU rail (heltec_t096, * ttgo_tbeam) * * 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