From efa5240b69a315d0d8a6e3f30c86c849459829f7 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:27:28 +0200 Subject: [PATCH] add meshnology w12 --- build.sh | 1 + zephcore/CMakeLists.txt | 20 +- zephcore/boards/esp32/meshnology_w12/Kconfig | 3 + .../meshnology_w12/Kconfig.meshnology_w12 | 3 + .../boards/esp32/meshnology_w12/board.cmake | 7 + .../boards/esp32/meshnology_w12/board.conf | 44 +++ .../boards/esp32/meshnology_w12/board.overlay | 72 ++++ .../boards/esp32/meshnology_w12/board.yml | 6 + .../meshnology_w12-pinctrl.dtsi | 67 ++++ .../meshnology_w12/meshnology_w12_procpu.dts | 341 ++++++++++++++++++ .../meshnology_w12_procpu_defconfig | 12 + .../esp32/meshnology_w12/partitions.overlay | 53 +++ zephcore/boards/example_board/README.md | 8 + zephcore/cmake/zephyr_new_files.cmake | 37 ++ .../drivers/lora/lr20xx/lr20xx_lora.c | 62 +++- .../dts/bindings/lora/semtech,lr2021.yaml | 32 +- zephcore/sysbuild/CMakeLists.txt | 9 + 17 files changed, 750 insertions(+), 27 deletions(-) create mode 100644 zephcore/boards/esp32/meshnology_w12/Kconfig create mode 100644 zephcore/boards/esp32/meshnology_w12/Kconfig.meshnology_w12 create mode 100644 zephcore/boards/esp32/meshnology_w12/board.cmake create mode 100644 zephcore/boards/esp32/meshnology_w12/board.conf create mode 100644 zephcore/boards/esp32/meshnology_w12/board.overlay create mode 100644 zephcore/boards/esp32/meshnology_w12/board.yml create mode 100644 zephcore/boards/esp32/meshnology_w12/meshnology_w12-pinctrl.dtsi create mode 100644 zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu.dts create mode 100644 zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu_defconfig create mode 100644 zephcore/boards/esp32/meshnology_w12/partitions.overlay create mode 100644 zephcore/cmake/zephyr_new_files.cmake diff --git a/build.sh b/build.sh index 401c795..848eece 100644 --- a/build.sh +++ b/build.sh @@ -70,6 +70,7 @@ ESP32_boards=( heltec_wifi_lora32_v43/esp32s3/procpu heltec_wireless_tracker/esp32s3/procpu heltec_wireless_tracker_v2/esp32s3/procpu + meshnology_w12/esp32s3/procpu ttgo_tbeam/esp32/procpu ) diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index ac54083..8f10f34 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -231,18 +231,12 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/modules/hal-espressif) ) endif() -# Copy new files into Zephyr tree -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new) - file(GLOB_RECURSE ZEPHCORE_NEW_FILES - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new - ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new/*) - foreach(REL_PATH ${ZEPHCORE_NEW_FILES}) - set(SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new/${REL_PATH}) - set(DST_FILE ${ZEPHYR_DIR}/${REL_PATH}) - configure_file(${SRC_FILE} ${DST_FILE} COPYONLY) - message(STATUS " [zephyr-new] ${REL_PATH}") - endforeach() -endif() +# Copy new files into Zephyr tree. +# Shared with sysbuild/CMakeLists.txt, which must do the same thing before +# MCUboot's devicetree pass — see the module for why. +set(ZEPHCORE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZEPHCORE_ZEPHYR_DIR ${ZEPHYR_DIR}) +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/zephyr_new_files.cmake) # Custom board root: boards/// discovered via BOARD_ROOT list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) @@ -329,7 +323,7 @@ if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wi set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf") elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*nrf52.*") set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf") -elseif(BOARD MATCHES ".*esp32.*" OR BOARD MATCHES "station_g2" OR BOARD MATCHES "thinknode_m9" OR BOARD MATCHES "lilygo_tlora_c6" OR BOARD MATCHES "heltec_wifi_lora32_v3" OR BOARD MATCHES "heltec_wifi_lora32_v4") +elseif(BOARD MATCHES ".*esp32.*" OR BOARD MATCHES "station_g2" OR BOARD MATCHES "thinknode_m9" OR BOARD MATCHES "lilygo_tlora_c6" OR BOARD MATCHES "heltec_wifi_lora32_v3" OR BOARD MATCHES "heltec_wifi_lora32_v4" OR BOARD MATCHES "meshnology_w12") set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_common.conf") elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*esp32.*") set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_common.conf") diff --git a/zephcore/boards/esp32/meshnology_w12/Kconfig b/zephcore/boards/esp32/meshnology_w12/Kconfig new file mode 100644 index 0000000..959ef82 --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/Kconfig @@ -0,0 +1,3 @@ +config HEAP_MEM_POOL_ADD_SIZE_BOARD + int + default 4096 diff --git a/zephcore/boards/esp32/meshnology_w12/Kconfig.meshnology_w12 b/zephcore/boards/esp32/meshnology_w12/Kconfig.meshnology_w12 new file mode 100644 index 0000000..a817907 --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/Kconfig.meshnology_w12 @@ -0,0 +1,3 @@ +config BOARD_MESHNOLOGY_W12 + select SOC_ESP32S3_WROOM_N16R8 + select SOC_ESP32S3_PROCPU if BOARD_MESHNOLOGY_W12_ESP32S3_PROCPU diff --git a/zephcore/boards/esp32/meshnology_w12/board.cmake b/zephcore/boards/esp32/meshnology_w12/board.cmake new file mode 100644 index 0000000..f3c3ac1 --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/board.cmake @@ -0,0 +1,7 @@ +if(NOT "${OPENOCD}" MATCHES "^${ESPRESSIF_TOOLCHAIN_PATH}/.*") + set(OPENOCD OPENOCD-NOTFOUND) +endif() +find_program(OPENOCD openocd PATHS ${ESPRESSIF_TOOLCHAIN_PATH}/openocd-esp32/bin NO_DEFAULT_PATH) + +include(${ZEPHYR_BASE}/boards/common/esp32.board.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) diff --git a/zephcore/boards/esp32/meshnology_w12/board.conf b/zephcore/boards/esp32/meshnology_w12/board.conf new file mode 100644 index 0000000..e0340a1 --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/board.conf @@ -0,0 +1,44 @@ +# Meshnology W12 "WiFi LoRa 32 V5" — ESP32-S3R8 + LR2021 + GC1109 FEM +# +# Hardware (schematic W12-MB-V0.2, archived in devdocs/w12/vendor/): +# - ESP32-S3R8 (WROOM-1 N16R8): 16 MB QIO flash, 8 MB OPI PSRAM +# - LR2021 on SPI2 (GPIO8 NSS, 9 SCK, 10 MOSI, 11 MISO, 12 RST, 13 BUSY), +# IRQ on GPIO14 = chip DIO8, plain crystal (no TCXO) +# - GC1109 sub-GHz PA/LNA on chip DIO9/10/11, rail enable GPIO4 (active HIGH) +# - RFX2402E 2.4 GHz PA on chip DIO5/6, rail enable GPIO3 (held LOW, unused) +# - SSD1315 128x64 bicolour OLED @ 0x3C on I2C0 (SDA 17, SCL 18, RST 47) +# - Battery ADC GPIO1 = ADC1_CH0, divider enable GPIO2 (active HIGH), ratio 4.90 +# - Vext GPIO45 active LOW; user button GPIO0; RGB LED GPIO46 (unused, see below) +# - GNSS header on UART1 (unpopulated from the factory) +# +# Include order: prj.conf → zephcore_common.conf → esp32_common.conf → board.conf + +CONFIG_ZEPHCORE_BOARD_NAME="Meshnology W12" +CONFIG_BT_DIS_MODEL_NUMBER_STR="Meshnology W12 LR2021" + +CONFIG_ZEPHCORE_RADIO_LR2021=y + +# Flash — 16 MB (override the 4 MB esp32_common.conf default) +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y + +# 8 MB OPI PSRAM. Enable is auto-detected from DTS by Kconfig.psram; only the +# mode is set here, same as station_g2 / thinknode_m9 / xiao_esp32s3. +CONFIG_SPIRAM_MODE_OCT=y + +# ========== TX power: the number here is CHIP output, not antenna output ========== +# +# The GC1109 sits between the LR2021 and the antenna and adds roughly 26 dB. +# The vendor rates the finished board at 30 +/- 1 dBm conducted, and MeshCore's +# variant notes the part's absolute maximum TX-port input is +5 dBm with full +# saturation already reached around 3-4 dBm. So 4 dBm at the chip is both the +# most that is useful and the most that is safe: beyond it the extra drive turns +# into distortion and heat, not range. +# +# Cap and default are deliberately the same value. Unlike Station G2 (default 15, +# cap 19) there is no useful headroom above saturation to leave a user, and the +# knob still spans -9..4 downward — roughly 17..30 dBm at the antenna — which is +# the range that matters for staying inside a regional ERP limit. Anyone setting +# this for EU 868 should note that even -9 dBm at the chip is well above 14 dBm +# ERP once the FEM has had its way. +CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=4 +CONFIG_ZEPHCORE_MAX_TX_POWER_DBM=4 diff --git a/zephcore/boards/esp32/meshnology_w12/board.overlay b/zephcore/boards/esp32/meshnology_w12/board.overlay new file mode 100644 index 0000000..d2a6768 --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/board.overlay @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: MIT + * Meshnology W12 — ZephCore overlay + * + * The board's own DTS (meshnology_w12_procpu.dts) already declares the LR2021, + * the SSD1315, the FEM rails, the battery ADC, the GNSS UART and the button. + * This overlay only adds the ZephCore integration on top: the runtime sensor + * bus, the LittleFS mount, the button gesture chain and the USB companion node. + */ + +#include + +/ { + /* Button chain — same mapping as RAK4631 / Heltec V3. + * GPIO0 is the only button the firmware sees; the second hole in the + * case is the ESP32 EN/reset key, which never reaches software. */ + 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>; + }; + + chosen { + /* Plain-UART companion backend (CONFIG_ZEPHCORE_COMPANION_SERIAL). + * uart0 is GPIO43/44 on the expansion header — there is no bridge + * chip, so this is a wired-header option rather than the USB-C port. + * Inert unless that conf is built. */ + zephcore,companion-uart = &uart0; + }; +}; + +/* Runtime-probed I2C sensors share the OLED bus, which is broken out on the + * display connector and the expansion header. */ +&i2c0 { + #include "../../common/sensors-i2c.dtsi" +}; + +/* + * Flash partitions live in partitions.overlay (this dir) so the identical map + * is fed to both the app and the MCUboot image — see that file and + * sysbuild/CMakeLists.txt. + */ + +/* LittleFS auto-mount — standard /lfs mount point */ +#include "../../common/filesystem.dtsi" + +/* USB OTG companion transport. Including this dtsi is also the signal that + * CMakeLists.txt greps for to auto-enable boards/common/esp32s3_usb.conf on + * companion builds, so the companion speaks its protocol over the USB-C port. + * + * That matters more here than on most boards: the W12 has no USB-UART bridge, + * so the USB-C port is the ONLY wired route in. The cost is the documented one + * — once the port is USB OTG CDC-ACM rather than USB Serial/JTAG, esptool can + * no longer auto-reset the chip into download mode. Use `start dfu`, an + * Arduino-style 1200-baud touch (the vendor's own PlatformIO board JSON sets + * use_1200bps_touch for the same reason), or hold BOOT while plugging in. + * + * The console reroute to uart0 lives in the auto-paired esp32s3_usb.overlay and + * applies only on those companion builds. Do NOT include + * esp32s3_console_uart0.dtsi here — repeater and debug builds keep their USB + * Serial/JTAG console, which is the only console they have. */ +#include "../../common/esp32s3_usb_otg.dtsi" diff --git a/zephcore/boards/esp32/meshnology_w12/board.yml b/zephcore/boards/esp32/meshnology_w12/board.yml new file mode 100644 index 0000000..52315ca --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/board.yml @@ -0,0 +1,6 @@ +board: + name: meshnology_w12 + full_name: Meshnology W12 LR2021 + vendor: meshnology + socs: + - name: esp32s3 diff --git a/zephcore/boards/esp32/meshnology_w12/meshnology_w12-pinctrl.dtsi b/zephcore/boards/esp32/meshnology_w12/meshnology_w12-pinctrl.dtsi new file mode 100644 index 0000000..1b8398e --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/meshnology_w12-pinctrl.dtsi @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: MIT + * Meshnology W12 — Pin Control Definitions + * + * Verified against vendor schematic W12-MB-V0.2 (devdocs/w12/vendor/). + * + * UART0: TX=GPIO43, RX=GPIO44 + * I2C0: SDA=GPIO17, SCL=GPIO18 (SSD1315 OLED + expansion header) + * SPI2: SCLK=GPIO9, MOSI=GPIO10, MISO=GPIO11 (LR2021 LoRa) + * UART1: TX=GPIO38, RX=GPIO39 (GNSS header, unpopulated from factory) + * + * The FEM control pins (GPIO3/GPIO4) and the LoRa NSS/RST/BUSY/IRQ lines are + * plain GPIO — no pinctrl entries needed. + */ + +#include +#include +#include + +&pinctrl { + uart0_default: uart0_default { + group1 { + pinmux = ; + output-high; + }; + group2 { + pinmux = ; + bias-pull-up; + }; + }; + + /* GNSS header. The vendor pin table names these from the GNSS module's + * point of view — "GNSS_RX" (GPIO38) is the module's RX, i.e. the SoC's + * TX, and "GNSS_TX" (GPIO39) is the SoC's RX. Module-perspective naming is + * the usual trap here (same one as lilygo_timpulse_plus and the RAK3401); + * the schematic net names settle it and this is the wiring that follows + * from them. */ + uart1_default: uart1_default { + group1 { + pinmux = ; + output-high; + }; + group2 { + pinmux = ; + bias-pull-up; + }; + }; + + i2c0_default: i2c0_default { + group1 { + pinmux = , + ; + drive-open-drain; + }; + }; + + spim2_default: spim2_default { + group1 { + pinmux = , + ; + }; + group2 { + pinmux = ; + output-low; + }; + }; +}; diff --git a/zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu.dts b/zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu.dts new file mode 100644 index 0000000..add0366 --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu.dts @@ -0,0 +1,341 @@ +/* + * SPDX-License-Identifier: MIT + * Meshnology W12 "WiFi LoRa 32 V5" — ZephCore procpu DTS + * + * Every pin and polarity below is taken from the vendor schematic + * W12-MB-V0.2 and the vendor pin-assignment sheet, both archived in + * devdocs/w12/vendor/. Several of them contradict what the Arduino-side + * variants for this board claim; the schematic decides every time — see + * devdocs/w12/HANDOVER_MESHNOLOGY_W12.md for the list. + * + * Hardware: + * MCU: ESP32-S3R8 (WROOM-1 N16R8) — 16 MB QIO flash, 8 MB OPI PSRAM + * Radio: Semtech LR2021 on SPI2, plain 32 MHz crystal (no TCXO) + * FEM: GC1109 sub-GHz PA/LNA (~30 dBm out) + RFX2402E 2.4 GHz PA + * OLED: SSD1315 128x64 (SSD1306-compatible) on I2C0, bicolour glass + * Button: GPIO0 (BOOT doubles as the user key) + * BatADC: GPIO1 = ADC1_CH0, divider enable GPIO2 active-HIGH + * Vext: GPIO45 active-LOW (AO3401 P-FET gating an LDO enable) + * GNSS: header on UART1, unpopulated from the factory + * Console: native USB Serial/JTAG (there is no USB-UART bridge) + * + * LoRa SPI2: SCLK=9, MOSI=10, MISO=11, NSS=8 + * LoRa ctrl: RESET=12, BUSY=13, IRQ=14 (chip DIO8) + * FEM rails: sub-GHz enable GPIO4, 2.4 GHz enable GPIO3 (both active-HIGH) + * FEM switch: chip DIO9=CTX, DIO10=CPS, DIO11=CSD, DIO5/6 = 2.4 GHz TX/RX EN + */ + +/dts-v1/; + +#include +#include "meshnology_w12-pinctrl.dtsi" +#include +#include +#include + +/ { + model = "Meshnology W12 PROCPU"; + compatible = "meshnology,w12"; + + aliases { + uart-0 = &uart0; + i2c-0 = &i2c0; + lora0 = &lora0; + sw0 = &button0; + watchdog0 = &wdt0; + }; + + buttons: buttons { + compatible = "gpio-keys"; + button0: button_0 { + gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "USER Key"; + zephyr,code = ; + }; + }; + + /* Vext rail — GPIO45, ACTIVE LOW. + * + * Schematic sheet 1: GPIO45 drives the gate of Q1 (AO3401, P-channel) + * through R3 1k. Q1's source sits on VDD_3V3 with R2 100k gate-to-source + * holding it off, and its drain feeds the EN pin of U4 (TLV75733 LDO), + * which R5 100k pulls to ground. So GPIO45 LOW turns the P-FET on, pulls + * EN high and brings VEXT_3V3 up; GPIO45 high or floating leaves the rail + * dead. That makes this active-LOW, and it makes the rail OFF by default. + * + * MeshCore's variant declares PIN_VEXT_EN_ACTIVE=HIGH, which is wrong for + * this circuit. Do not "fix" this to active-HIGH to match it without + * re-reading sheet 1 — the whole peripheral rail, OLED included, hangs + * off this pin. + * + * GPIO45 is also an ESP32-S3 strapping pin (VDD_SPI voltage select) with + * an internal pull-down at reset. That default reads as 0, which is the + * same level we drive to enable the rail, so there is no boot conflict — + * but never add a pull-up here, it would flip the strap. */ + vext_enable: vext-enable { + compatible = "regulator-fixed"; + regulator-name = "vext"; + enable-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; /* GPIO45 */ + regulator-boot-on; + regulator-always-on; + }; + + /* Sub-GHz front end rail — GPIO4 (PA_EN_M), ACTIVE HIGH. + * + * Drives the EN pin of the adjustable regulator that supplies the GC1109 + * (net VR_PA). Its pull-up R56 is marked NC on the schematic, so the rail + * is off until firmware asserts this — the radio is deaf and mute without + * it, in both directions, because the GC1109 carries the RX LNA too. + * + * startup-delay-us matches the 100 ms MeshCore waits after enabling the + * FEM before touching the radio. Expressed as a regulator startup delay it + * is paid once at regulator init, which runs at priority 75 — ahead of the + * LoRa driver — instead of being a sleep on the radio init path. */ + fem_lf_power: fem-lf-power { + compatible = "regulator-fixed"; + regulator-name = "fem-lf"; + enable-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>; /* GPIO4 = PA_EN_M */ + startup-delay-us = <100000>; + regulator-boot-on; + regulator-always-on; + }; + + /* 2.4 GHz front end rail — GPIO3 (PA_EN_G), ACTIVE HIGH, deliberately + * left DISABLED. ZephCore is sub-GHz only, so the RFX2402E never needs + * power, and leaving its rail up would burn current for nothing. + * + * This is a regulator rather than a comment because an undriven GPIO3 + * floats: R38 (the EN pull-up) is marked NC, so nothing else defines the + * level. regulator_fixed_init() configures the enable pin + * GPIO_OUTPUT_INACTIVE when the node has neither regulator-boot-on nor + * regulator-always-on, which actively drives GPIO3 LOW at boot. That is + * the entire purpose of this node — do not add boot-on/always-on. */ + fem_hf_power: fem-hf-power { + compatible = "regulator-fixed"; + regulator-name = "fem-hf"; + enable-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; /* GPIO3 = PA_EN_G */ + }; + + /* Battery divider enable — GPIO2, ACTIVE HIGH. + * GPIO2 -> R52 1k -> base of Q7 (S8050 NPN); Q7's collector pulls the gate + * of Q6 (AO3401A P-FET) down through R48 1k, switching BAT onto the + * R50 390K / R51 100K divider. Driving GPIO2 high therefore enables the + * divider; undriven, the divider is open and GPIO1 reads a hard 0 mV. + * ZephyrBoard toggles this per reading, so the divider draws nothing + * between samples. */ + vbat_enable: vbat-enable { + compatible = "regulator-fixed"; + regulator-name = "vbat-enable"; + enable-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; /* GPIO2 = ADC_CTRL */ + }; + + /* GNSS module power — GPIO48 (GNSS_CTRL), ACTIVE LOW. + * The header is unpopulated on shipping units; the node is unconditional + * so a user-fitted module just works, exactly like the RAK4631's optional + * RAK1910. With nothing fitted the driver simply never sees a fix. */ + gps_en: gps-enable { + compatible = "gpio-leds"; + gps_enable_pin: gps_enable { + gpios = <&gpio1 16 GPIO_ACTIVE_LOW>; /* GPIO48 */ + label = "GNSS Enable"; + }; + }; + + /* Battery ADC — ZephCore reads this via zephyr,user { io-channels }. + * Multiplier = internal reference (1100 mV) x divider ratio 4.90 + * ((390K + 100K) / 100K, schematic R50/R51) = 5390. */ + zephyr,user { + io-channels = <&adc0 0>; + vbat-mv-multiplier = <5390>; + }; + + aliases { + gps-enable = &gps_enable_pin; + }; + + chosen { + zephyr,sram = &sram1; + /* There is no USB-UART bridge on this board — GPIO43/44 only reach + * the expansion header — so the stock console is USB Serial/JTAG. + * A companion build pulls in boards/common/esp32s3_usb.conf, which + * hands D+/D- to USB OTG CDC-ACM and reroutes this console to + * uart0 on the header. */ + zephyr,console = &usb_serial; + zephyr,shell-uart = &usb_serial; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_partition; + zephyr,bt-hci = &esp32_bt_hci; + zephyr,display = &ssd1306_128x64; + }; +}; + +/* Battery ADC on adc0 (ADC unit 1 -> GPIO1 = ADC1_CH0). The node *labelled* + * adc0 is unit 1; the node labelled adc1 is unit 2 (GPIO11 = LR2021 MISO). + * The channel@0 node is required — without it adc_channel_setup_dt() fails and + * getBattMilliVolts() returns 0 before ever performing a conversion. + * ADC_GAIN_1 (0 dB) with ADC_REF_INTERNAL (~1100 mV): a 3.0-4.2 V cell lands at + * 612-857 mV after the 4.90 divider, comfortably inside range. */ +&adc0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; + +/* ADC unit 2 (label adc1) is unused; keep it disabled so nothing can claim + * GPIO11 (LR2021 MISO) as an ADC pad. */ +&adc1 { + status = "disabled"; +}; + +&usb_serial { + status = "okay"; +}; + +&uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_default>; + pinctrl-names = "default"; +}; + +&uart1 { + status = "okay"; + current-speed = <9600>; + pinctrl-0 = <&uart1_default>; + pinctrl-names = "default"; + + gnss: gnss-nmea-generic { + compatible = "gnss-nmea-generic"; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; + clock-frequency = ; + pinctrl-0 = <&i2c0_default>; + pinctrl-names = "default"; + + /* SSD1315 — register-compatible with the SSD1306 the Zephyr driver binds + * to; the difference is the panel glass (bicolour: top 16 rows yellow), + * which is invisible to the driver. Same part as lilygo_timpulse_plus. + * + * Reset is GPIO47, not GPIO21. The vendor pin sheet lists IO47 = OLED/LCD + * RST and IO21 = OLED/LCD CS (CS belongs to the SPI-LCD option this + * connector also supports and the shipping I2C panel does not use). + * MeshCore's pins_arduino.h has RST_OLED = 21, which is the CS pin. */ + ssd1306_128x64: display-controller@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; + inversion-on; + prechargep = <0x22>; + reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; /* GPIO47 */ + vin-supply = <&vext_enable>; + }; +}; + +&spi2 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-0 = <&spim2_default>; + pinctrl-names = "default"; + + /* The LR2021 driver takes NSS from here but strips CS out of its own + * spi_dt_spec and toggles the pin itself, because the chip needs NSS held + * across a two-window read. The ESP32 SPI controller therefore never + * drives CS and the two cannot fight. + * + * No overrun-character property, and none is needed: the LR2021 HAL clocks + * NULL TX buffers on its read windows and the chip parses those bytes, so + * MOSI must idle at 0x00 (its NOP) rather than 0xff. On Nordic that takes + * an explicit overrun-character; spi_esp32_spim allocates a zero-filled TX + * buffer for any RX-only transfer, so ESP32 already sends 0x00. */ + cs-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; /* NSS = GPIO8 */ + + lora0: lora@0 { + compatible = "semtech,lr2021"; + reg = <0>; + spi-max-frequency = <8000000>; + + reset-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; /* GPIO12 */ + busy-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; /* GPIO13 */ + dio1-gpios = <&gpio0 14 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;/* GPIO14 */ + + /* Chip DIO8 -> GPIO14 (schematic net IO14_LORA_INT2). DIO7 goes to + * GPIO7 as a second IRQ line (IO7_LORA_INT1) that ZephCore does not + * use. DIO5 is NOT free for interrupts here — it drives the 2.4 GHz + * PA's TX enable — so leaving irq-dio at its default of 9 would also + * be wrong. */ + irq-dio = <8>; + + /* No tcxo-voltage-mv: schematic sheet 3 shows a plain crystal on + * XTA/XTB (pins 4/5) and pin 6 wired to the VNTC net, not a TCXO + * supply. Setting a TCXO voltage on a board without one wedges the + * chip at HF-XOSC startup — that is what happened on the + * promicro_lr2021 module. */ + + rx-boosted; + + /* RF switch — the LR2021 drives the whole front end itself. + * + * DIO5 (bit 0) = RFX2402E 2.4 GHz TX_EN + * DIO6 (bit 1) = RFX2402E 2.4 GHz RX_EN + * DIO9 (bit 4) = GC1109 CTX (transmit path select) + * DIO10 (bit 5) = GC1109 CPS (PA vs bypass path) + * DIO11 (bit 6) = GC1109 CSD (shutdown, HIGH = awake) + * + * CSD and CPS carry 10k pull-downs (R26/R27), so the GC1109 boots + * into shutdown and stays there until these are configured. That is + * why the driver's DIO5..DIO8-only loop had to be widened to + * DIO5..DIO11 for this board: with DIO9/10/11 unconfigured the node + * looks healthy on every register read and hears nothing. + * + * RX is split per band because the two front ends are mutually + * exclusive: sub-GHz RX wakes the GC1109 (CSD high, CTX low = LNA + * path), while 2.4 GHz RX must leave it in shutdown and enable the + * RFX2402E instead. ZephCore only ever uses the sub-GHz side; the HF + * masks are here so the table is a faithful description of the + * hardware rather than a half-truth. */ + rfswitch-enable = <0x73>; /* DIO5,6,9,10,11 */ + rfswitch-standby = <0x00>; /* everything off */ + rfswitch-rx-lf = <0x40>; /* DIO11: GC1109 out of shutdown, LNA */ + rfswitch-rx-hf = <0x02>; /* DIO6: 2.4 GHz LNA, GC1109 shut down */ + rfswitch-tx = <0x70>; /* DIO9|10|11: GC1109 full PA */ + rfswitch-tx-hp = <0x01>; /* DIO5: 2.4 GHz PA */ + }; +}; + +&timer0 { status = "okay"; }; +&timer1 { status = "okay"; }; +&timer2 { status = "okay"; }; +&timer3 { status = "okay"; }; + +&wdt0 { status = "okay"; }; +&trng0 { status = "okay"; }; + +&esp32_bt_hci { status = "okay"; }; +&wifi { status = "okay"; }; diff --git a/zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu_defconfig b/zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu_defconfig new file mode 100644 index 0000000..cb1f384 --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/meshnology_w12_procpu_defconfig @@ -0,0 +1,12 @@ +# Meshnology W12 — minimal Zephyr defconfig (procpu) +CONFIG_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_UART_CONSOLE=y +CONFIG_GPIO=y +CONFIG_CLOCK_CONTROL=y +CONFIG_PINCTRL=y +CONFIG_SPI=y +CONFIG_I2C=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y diff --git a/zephcore/boards/esp32/meshnology_w12/partitions.overlay b/zephcore/boards/esp32/meshnology_w12/partitions.overlay new file mode 100644 index 0000000..5a300ab --- /dev/null +++ b/zephcore/boards/esp32/meshnology_w12/partitions.overlay @@ -0,0 +1,53 @@ +/* + * Flash partition layout — SHARED by the app image and MCUboot. + * + * Fed to BOTH images (app via EXTRA_DTC_OVERLAY_FILE, MCUboot via + * mcuboot_EXTRA_DTC_OVERLAY_FILE in sysbuild/CMakeLists.txt) so their flash maps + * are identical — otherwise MCUboot stages/looks for the OTA image at a different + * address than the app writes it to and OTA silently reverts on reboot. + * + * slot0 starts at 0x10000 (right after the 64KB MCUboot region), matching the + * Arduino/ESP-IDF app offset (Mesh America "flash-update" compatible). sys_partition + * is reclaimed. Slots are equal size and slot1 ends exactly on the unchanged + * storage/lfs offsets, so a -merged reflash keeps identity/prefs/contacts/BLE-bonds. + * + * 16MB layout. + */ +/delete-node/ &sys_partition; +/delete-node/ &slot0_lpcore_partition; +/delete-node/ &slot1_lpcore_partition; +/delete-node/ &storage_partition; +/delete-node/ &scratch_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 (swap-mode compatible), slot0 at 0x10000. */ + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x10000 0x7C6000>; + }; + slot1_partition: partition@7D6000 { + label = "image-1"; + reg = <0x7D6000 0x7C6000>; + }; + /* BLE bond NVS — offset unchanged so bonds survive the reflash. */ + storage_partition: partition@F9C000 { + label = "storage"; + reg = <0xF9C000 0x4000>; + }; + /* LittleFS (identity/prefs/contacts) — offset unchanged. */ + lfs_partition: partition@FA0000 { + label = "lfs"; + reg = <0xFA0000 0x60000>; + }; + }; +}; diff --git a/zephcore/boards/example_board/README.md b/zephcore/boards/example_board/README.md index a4c8456..64bb8d5 100644 --- a/zephcore/boards/example_board/README.md +++ b/zephcore/boards/example_board/README.md @@ -51,9 +51,17 @@ SWD flash: `west flash` (requires J-Link, pyocd, or nrfjprog connected). | 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` | **Heltec V3 console:** ZephCore routes console/shell to `uart0` on V3. Use the UART serial port for boot logs and CLI. +**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/`. + **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 diff --git a/zephcore/cmake/zephyr_new_files.cmake b/zephcore/cmake/zephyr_new_files.cmake new file mode 100644 index 0000000..f749877 --- /dev/null +++ b/zephcore/cmake/zephyr_new_files.cmake @@ -0,0 +1,37 @@ +# Copy patches/zephyr-new/* into the Zephyr tree. +# +# These are whole files ZephCore adds to Zephyr that have no upstream to patch +# against — today the LR11xx and LR20xx LoRa drivers and their devicetree +# bindings. They are copied rather than patched because there is nothing to +# apply a diff to. +# +# This lives in its own module because it has to run from TWO places, and the +# order matters: +# +# - zephcore/CMakeLists.txt, for the application image. +# - zephcore/sysbuild/CMakeLists.txt, BEFORE find_package(Sysbuild). +# +# Under --sysbuild, sysbuild configures the MCUboot image first, and MCUboot +# runs a full devicetree pass over the same board DTS the app uses. If the +# copy only happened in the app's CMakeLists, MCUboot's DTS pass would see +# whatever version of these bindings a previous build happened to leave behind +# — or, on a clean workspace, none at all. Editing a vendored binding then +# fails the MCUboot image with "not declared in 'properties:'" while the app +# image would have been perfectly happy, and the error points at the copy in +# the Zephyr tree rather than at the real source in patches/zephyr-new. +# +# configure_file(... COPYONLY) is a no-op when the contents already match, so +# running this twice per configure costs nothing and never churns timestamps. +# +# Expects ZEPHCORE_SOURCE_DIR and ZEPHCORE_ZEPHYR_DIR to be set by the caller. + +if(EXISTS ${ZEPHCORE_SOURCE_DIR}/patches/zephyr-new) + file(GLOB_RECURSE ZEPHCORE_NEW_FILES + RELATIVE ${ZEPHCORE_SOURCE_DIR}/patches/zephyr-new + ${ZEPHCORE_SOURCE_DIR}/patches/zephyr-new/*) + foreach(REL_PATH ${ZEPHCORE_NEW_FILES}) + configure_file(${ZEPHCORE_SOURCE_DIR}/patches/zephyr-new/${REL_PATH} + ${ZEPHCORE_ZEPHYR_DIR}/${REL_PATH} COPYONLY) + message(STATUS " [zephyr-new] ${REL_PATH}") + endforeach() +endif() diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c index 92c791a..dcff79c 100644 --- a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c +++ b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c @@ -61,7 +61,12 @@ struct lr20xx_config { /* RF switch DIO bitmasks (bit 0 = DIO5, bit 1 = DIO6, ...) */ uint8_t rfswitch_enable; uint8_t rfswitch_standby; - uint8_t rfswitch_rx; + /* RX masks are split per band: a front end that shuts its sub-GHz PA + * down during 2.4 GHz RX (Meshnology W12 / GC1109 CSD) needs the two + * to differ. Boards that set only the combined `rfswitch-rx` get the + * same mask in both, which is the pre-split behaviour. */ + uint8_t rfswitch_rx_lf; + uint8_t rfswitch_rx_hf; uint8_t rfswitch_tx; uint8_t rfswitch_tx_hp; }; @@ -350,19 +355,45 @@ lr20xx_irq_dio_pull(const struct lr20xx_config *cfg) /* ── Configure RF switch DIOs ───────────────────────────────────────── */ +/* DIO5..DIO11 inclusive — the full RF-switch-capable range on this chip. + * Bit i of every rfswitch-* mask is DIO(5 + i), so the masks stay uint8_t. */ +#define LR20XX_RFSWITCH_DIO_COUNT \ + (LR20XX_SYSTEM_DIO_11 - LR20XX_SYSTEM_DIO_5 + 1) + static void lr20xx_configure_rfswitch(void *ctx, const struct lr20xx_config *cfg) { /* LR20xx RF switch uses per-DIO configuration. - * DIO5..DIO8 map to enable bitmask bits 0..3. + * DIO5..DIO11 map to enable bitmask bits 0..6 (the chip has no RF + * switch DIOs outside 5..11 — see lr20xx_system_dio_t). * For each enabled DIO, compute which operational modes - * should drive it HIGH by looking at the per-mode bitmasks. */ - for (int i = 0; i < 4; i++) { + * should drive it HIGH by looking at the per-mode bitmasks. + * + * This used to stop at bit 3 (DIO5..DIO8), which silently dropped the + * top three DIOs from any board that used them: the enable bit was set + * in devicetree, the loop never reached it, so the DIO kept its default + * function and the front end it drives never switched. The Meshnology + * W12 puts its entire sub-GHz front end there (DIO9 = GC1109 CTX, + * DIO10 = CPS, DIO11 = CSD), and with those three left unconfigured the + * FEM stays in the shutdown its 10k pull-downs assert at reset — the + * node is both deaf and mute while every register readback looks fine. */ + for (int i = 0; i < LR20XX_RFSWITCH_DIO_COUNT; i++) { if (!(cfg->rfswitch_enable & BIT(i))) { continue; } lr20xx_system_dio_t dio = (lr20xx_system_dio_t)(LR20XX_SYSTEM_DIO_5 + i); + /* Never repurpose the interrupt line. A board that sets the IRQ + * DIO's bit in rfswitch-enable by mistake would otherwise have its + * only IRQ pin switched to RF-switch duty here, and the driver + * would then wait forever on TX_DONE/RX_DONE with no way to tell + * why. Skipping it turns a silent hang into a warning. */ + if (dio == lr20xx_irq_dio(cfg)) { + LOG_WRN("rfswitch: DIO%d is the IRQ line, not configuring " + "it as an RF switch", (int)dio); + continue; + } + /* Set this DIO function to RF switch control */ lr20xx_system_set_dio_function(ctx, dio, LR20XX_SYSTEM_DIO_FUNC_RF_SWITCH, @@ -375,9 +406,11 @@ static void lr20xx_configure_rfswitch(void *ctx, const struct lr20xx_config *cfg if (cfg->rfswitch_standby & BIT(i)) { sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_STANDBY; } - if (cfg->rfswitch_rx & BIT(i)) { - sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_LF | - LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_HF; + if (cfg->rfswitch_rx_lf & BIT(i)) { + sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_LF; + } + if (cfg->rfswitch_rx_hf & BIT(i)) { + sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_HF; } if (cfg->rfswitch_tx & BIT(i)) { sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_TX_LF; @@ -3238,8 +3271,10 @@ static int lr20xx_hw_init(struct lr20xx_data *data, lr20xx_status_t st; lr20xx_configure_rfswitch(ctx, cfg); - LOG_DBG("RF switch: en=0x%02x stby=0x%02x rx=0x%02x tx=0x%02x txhp=0x%02x", - cfg->rfswitch_enable, cfg->rfswitch_standby, cfg->rfswitch_rx, + LOG_DBG("RF switch: en=0x%02x stby=0x%02x rxlf=0x%02x rxhf=0x%02x " + "tx=0x%02x txhp=0x%02x", + cfg->rfswitch_enable, cfg->rfswitch_standby, + cfg->rfswitch_rx_lf, cfg->rfswitch_rx_hf, cfg->rfswitch_tx, cfg->rfswitch_tx_hp); st = lr20xx_system_set_dio_function(ctx, lr20xx_irq_dio(cfg), @@ -3408,7 +3443,14 @@ static DEVICE_API(lora, lr20xx_lora_api) = { .irq_dio = DT_INST_PROP_OR(n, irq_dio, 9), \ .rfswitch_enable = DT_INST_PROP_OR(n, rfswitch_enable, 0), \ .rfswitch_standby = DT_INST_PROP_OR(n, rfswitch_standby, 0),\ - .rfswitch_rx = DT_INST_PROP_OR(n, rfswitch_rx, 0), \ + /* rfswitch-rx-lf / -hf are optional and carry no binding \ + * default, so an absent one falls back to the combined \ + * rfswitch-rx — which keeps every pre-split board (e.g. \ + * promicro_lr2021) byte-identical. */ \ + .rfswitch_rx_lf = DT_INST_PROP_OR(n, rfswitch_rx_lf, \ + DT_INST_PROP_OR(n, rfswitch_rx, 0)), \ + .rfswitch_rx_hf = DT_INST_PROP_OR(n, rfswitch_rx_hf, \ + DT_INST_PROP_OR(n, rfswitch_rx, 0)), \ .rfswitch_tx = DT_INST_PROP_OR(n, rfswitch_tx, 0), \ .rfswitch_tx_hp = DT_INST_PROP_OR(n, rfswitch_tx_hp, 0), \ }; \ diff --git a/zephcore/patches/zephyr-new/dts/bindings/lora/semtech,lr2021.yaml b/zephcore/patches/zephyr-new/dts/bindings/lora/semtech,lr2021.yaml index c7e00c6..20e7b87 100644 --- a/zephcore/patches/zephyr-new/dts/bindings/lora/semtech,lr2021.yaml +++ b/zephcore/patches/zephyr-new/dts/bindings/lora/semtech,lr2021.yaml @@ -60,8 +60,10 @@ properties: type: int default: 0 description: | - RF switch DIO enable bitmask (bit 0 = DIO5, bit 1 = DIO6, ...). - Set a bit to include that DIO in RF switch control. + RF switch DIO enable bitmask (bit 0 = DIO5, bit 1 = DIO6, ... bit 6 = + DIO11). DIO5..DIO11 is the chip's entire RF-switch-capable range, so + bit 7 is unused. Set a bit to include that DIO in RF switch control. + The DIO named by irq-dio is skipped even if its bit is set here. rfswitch-standby: type: int @@ -74,8 +76,30 @@ properties: type: int default: 0 description: | - Bitmask of RF switch DIOs that are HIGH in RX mode (both LF and HF). - Bit mapping matches rfswitch-enable. + Bitmask of RF switch DIOs that are HIGH in RX mode, in BOTH bands. + Bit mapping matches rfswitch-enable. This is the convenient form for + the common single-band board; set rfswitch-rx-lf / rfswitch-rx-hf + instead when the two bands need different masks. + + rfswitch-rx-lf: + type: int + description: | + Bitmask of RF switch DIOs that are HIGH in sub-GHz RX only. + Defaults to rfswitch-rx when omitted. Bit mapping matches + rfswitch-enable. + + rfswitch-rx-hf: + type: int + description: | + Bitmask of RF switch DIOs that are HIGH in 2.4 GHz RX only. + Defaults to rfswitch-rx when omitted. Bit mapping matches + rfswitch-enable. + + Split from rfswitch-rx for front ends whose two bands are mutually + exclusive: the Meshnology W12 must hold the GC1109 sub-GHz FEM in + shutdown (DIO11 low) during 2.4 GHz RX, which a single shared mask + cannot express. Do NOT give these two a `default:` here — the driver + falls back to rfswitch-rx by testing for the property's absence. rfswitch-tx: type: int diff --git a/zephcore/sysbuild/CMakeLists.txt b/zephcore/sysbuild/CMakeLists.txt index 8018d1d..be957e8 100644 --- a/zephcore/sysbuild/CMakeLists.txt +++ b/zephcore/sysbuild/CMakeLists.txt @@ -2,6 +2,15 @@ # so the BOARD_ROOT set there never fires. Set it here, before find_package(Sysbuild). set(BOARD_ROOT "${APP_DIR}" CACHE STRING "" FORCE) +# Same story for the files ZephCore adds to the Zephyr tree: the app copies them +# in its own CMakeLists, but MCUboot is configured first and runs a devicetree +# pass over the same board DTS. A board whose radio node uses a ZephCore-only +# binding (the LR11xx / LR20xx LoRa drivers) needs that binding present before +# MCUboot's DTS pass, not after. See cmake/zephyr_new_files.cmake. +set(ZEPHCORE_SOURCE_DIR "${APP_DIR}") +set(ZEPHCORE_ZEPHYR_DIR "$ENV{ZEPHYR_BASE}") +include("${APP_DIR}/cmake/zephyr_new_files.cmake") + # Auto-propagate flash size from board.conf to MCUboot. # # MCUboot is built as a separate image and does NOT inherit the app's board.conf