feat: add Nordic nRF54L15-DK variant (Zephyr + BLE + LoRa)#10193
feat: add Nordic nRF54L15-DK variant (Zephyr + BLE + LoRa)#10193cvaldess wants to merge 6 commits intomeshtastic:developfrom
Conversation
|
Heads-up for reviewers — one Zephyr framework patch was applied locally and is intentionally not in this PR. On my bench the BLE host's TX processor ( The workaround is a one-line change in #if defined(CONFIG_BT_RECV_WORKQ_BT)
k_work_submit_to_queue(&bt_workq, &tx_work);
#else
k_work_submit(&tx_work);
#endifi.e. submit I'm deliberately not shipping this as a framework fork in the PR — it belongs upstream in Zephyr / nrfxlib, not in the Meshtastic repo. If CI (or another reviewer's hardware) can't reproduce stable BLE, this is almost certainly why. Happy to file the upstream Zephyr issue if useful; flagging it here so it's not a surprise. |
There was a problem hiding this comment.
Pull request overview
Adds a new Zephyr-based Meshtastic platform/variant for the Nordic nRF54L15-DK, including a BLE peripheral implementation, LittleFS-backed persistence, and build-system wiring to integrate the new target without impacting existing Arduino-based platforms.
Changes:
- Introduces
src/platform/nrf54l15/Zephyr “Arduino shim” layer plus a Zephyr BT-host GATT peripheral (NRF54L15Bluetooth). - Adds the nRF54L15-DK variant + PlatformIO environment/board definition and Zephyr Kconfig/DTS overlay configuration.
- Makes small cross-platform guards/adjustments in shared code to compile and run with the new Zephyr target.
Reviewed changes
Copilot reviewed 44 out of 45 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
zephyr/prj.conf |
Adds Zephyr Kconfig enabling C++17, RTT logging, LittleFS, and detailed BLE host/controller tuning for iOS compatibility. |
zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.overlay |
Board-level DTS overlay to disable unused peripherals and free SPI resources for the SX1262 wiring. |
variants/rp2350/rp2350.ini |
Excludes the new platform/nrf54l15/ source tree from RP2350 builds. |
variants/nrf54l15/nrf54l15dk/variant.h |
Defines the nRF54L15-DK pin map and radio configuration macros for the E22 (SX1262) module. |
variants/nrf54l15/nrf54l15dk/variant.cpp |
Adds the variant init hook (currently minimal/no-op). |
variants/nrf54l15/nrf54l15dk/platformio.ini |
Adds the nrf54l15dk PlatformIO environment configuration. |
variants/nrf54l15/nrf54l15dk/nrf54l15dk.overlay |
Provides an additional DTS overlay under variants/ (currently conflicting with the board overlay). |
variants/nrf54l15/nrf54l15dk/README.md |
Documents wiring, required DIO2→TXEN bridge, build/flash, and RTT monitoring steps. |
variants/nrf54l15/nrf54l15.ini |
Adds the nRF54L15 Zephyr base PlatformIO configuration, build flags, and src filters. |
src/platform/nrf54l15/utility/bonding.h |
Adds a stub header to satisfy bonding-related include chains when building under Zephyr. |
src/platform/nrf54l15/nrf54l15_main.cpp |
Adds the Zephyr main() entry point with crash capture/logging plumbing. |
src/platform/nrf54l15/nrf54l15_arduino.cpp |
Implements Arduino API shims (timing/GPIO/SPI/String/Print/etc.) atop Zephyr primitives. |
src/platform/nrf54l15/main-nrf54l15.cpp |
Adds nRF54L15 platform hooks for setup/loop, power, BLE enable/disable, and deep sleep stubs. |
src/platform/nrf54l15/bluefruit.h |
Adds a Bluefruit SDK stub to satisfy nRF52-specific include paths. |
src/platform/nrf54l15/architecture.h |
Defines ARCH_NRF54L15 and feature flags / HW_VENDOR mapping for the new platform. |
src/platform/nrf54l15/Wire.h |
Adds an Arduino TwoWire stub (compile-only at present). |
src/platform/nrf54l15/WProgram.h |
Adds legacy Arduino header shim redirecting to Arduino.h. |
src/platform/nrf54l15/Tone.h |
Adds a tone/noTone shim header redirecting to Arduino.h. |
src/platform/nrf54l15/Stream.h |
Adds a Stream shim header redirecting to Arduino.h. |
src/platform/nrf54l15/SPI.h |
Adds an Arduino SPI shim header for Zephyr-backed SPI transfers. |
src/platform/nrf54l15/Print.h |
Adds a Print shim header redirecting to Arduino.h. |
src/platform/nrf54l15/Nrf52SaadcLock.h |
Adds a stub to satisfy nRF52 SAADC lock include chains. |
src/platform/nrf54l15/NRF54L15Bluetooth.h |
Declares the Zephyr BLE backend implementing the project’s Bluetooth API interface. |
src/platform/nrf54l15/NRF54L15Bluetooth.cpp |
Implements the Meshtastic BLE GATT service/peripheral using Zephyr BT host APIs + watchdog logic. |
src/platform/nrf54l15/NRF52Bluetooth.h |
Adds a stub NRF52Bluetooth header for Zephyr builds. |
src/platform/nrf54l15/InternalFileSystem.h |
Declares Zephyr LittleFS-backed InternalFileSystem compatible with existing FS abstractions. |
src/platform/nrf54l15/InternalFileSystem.cpp |
Implements mount/open/read/write/dir traversal and recursive delete using Zephyr FS APIs. |
src/platform/nrf54l15/IPAddress.h |
Adds a stub IPAddress type for code paths expecting Arduino networking types. |
src/platform/nrf54l15/Arduino.h |
Adds the core Arduino compatibility layer used throughout Meshtastic and 3rd-party Arduino libs. |
src/modules/AdminModule.cpp |
Adds guards/helpers around GPIO output config handling and nRF54L15 BLE status plumbing. |
src/mesh/RadioLibInterface.h |
Clears the static instance on destruction; adds a TX_DONE missed-IRQ polling hook declaration. |
src/mesh/RadioLibInterface.cpp |
Adds missed TX_DONE IRQ polling to recover from dropped IRQ events. |
src/mesh/PhoneAPI.cpp |
Skips filesystem manifest enumeration on NRF54L15-DK due to an FS recursion abort on this target. |
src/mesh/NodeDB.cpp |
Adds nRF54L15 device-id derivation; adds additional LoRa USERPREFS overrides and “always-apply” logic after load. |
src/mesh/MeshService.cpp |
Avoids abort() on to-phone queue enqueue failures by releasing to pool and returning. |
src/mesh/Channels.cpp |
Applies compile-time LoRa USERPREFS overrides when building default LoRa config. |
src/main.h |
Adds nRF54L15 Bluetooth externs and updates platform hook declarations (including a new rp2040Loop declaration). |
src/main.cpp |
Adds nRF54L15 setup/loop calls and adds an RP2040 loop hook call; includes InputBroker conditionally. |
src/RedirectablePrint.cpp |
Adds BLE-log routing via nrf54l15Bluetooth when building for ARCH_NRF54L15. |
src/FSCommon.h |
Wires FSCommon to use the new Zephyr InternalFS implementation on ARCH_NRF54L15. |
src/FSCommon.cpp |
Enables recursive LittleFS directory deletion for ARCH_NRF54L15. |
platformio.ini |
Registers a post-build script to work around Zephyr’s two-pass link script generation for nRF54L15. |
extra_scripts/nrf54l15_linker.py |
Implements the post-build linker.cmd generation workaround by parsing build.ninja and invoking gcc -E. |
boards/nrf54l15dk.json |
Adds a PlatformIO board definition for the nRF54L15-DK. |
.gitignore |
Ignores J-Link/RTT debug artifacts. |
Reserves enum value 132 for the Nordic nRF54L15-DK community firmware port. The port is tracked in meshtastic/firmware#10193 and currently uses HardwareModel_PRIVATE_HW as a placeholder until this PR merges and the regenerated protobufs propagate back into the firmware tree. Board: Nordic nRF54L15-DK (PCA10156), Zephyr RTOS, external EBYTE E22-900M30S (SX1262) LoRa module. Firmware variant: nrf54l15dk.
|
Companion PR to reserve the HardwareModel enum value has been opened: meshtastic/protobufs#896 ( |
0712a36 to
4b64236
Compare
|
Force-pushed 4b64236 with CI fixes:
check-label failure — a maintainer will need to apply one of the required labels ( |
4b64236 to
b337f86
Compare
|
Force-pushed b337f86 addressing all Copilot review comments. Each item below maps to one of the unresolved threads. Real bugs:
Documentation / config inconsistencies:
Tested on hardware after push: clean boot, SX1262 init OK, iOS pairs + streams config + mesh TX/RX still working end-to-end. |
b337f86 to
e1d77ce
Compare
|
Force-pushed e1d77ce addressing @fifieldt's review comment on Expanded the feature-flags comment block into two paragraphs covering (1) what the No behavior change — comment-only edit on a single commit. Amended + force-pushed to keep the PR as one squashed commit, matching the earlier rounds. |
Adds a community hardware variant for the Nordic nRF54L15-DK (PCA10156)
with an external EBYTE E22-900M30S (SX1262) LoRa module. First Meshtastic
port running on the Zephyr RTOS; all other Nordic targets use the nRF5
SoftDevice stack.
Scope
-----
- New Zephyr-based platform layer under src/platform/nrf54l15/ providing
Arduino-compatible shims (Arduino.h, SPI, Wire, Print, Stream) over the
Zephyr APIs plus a LittleFS-backed InternalFileSystem on SPIM20.
- Bluetooth LE peripheral (NRF54L15Bluetooth.*) built on the Zephyr BT
host stack, exposing the Meshtastic GATT service with legacy
connectable advertising, just-works pairing, dynamic MTU exchange
(up to 247 bytes), and iOS connection-parameter tweaks.
- Variant directory variants/nrf54l15/nrf54l15dk/ with pin map for the
E22 module on connector J1, PlatformIO env (nrf54l15dk), Zephyr
DT overlay and a wiring README.
- Zephyr project config (zephyr/prj.conf + board overlay) tuned for
BT + LoRa: 16 KB main stack, 4 KB BT RX thread, RTT logging in
immediate mode, newlib-nano heap sized to leave room for the GATT
pools while still allowing ATT MTU=247.
- extra_scripts/nrf54l15_linker.py works around a PlatformIO + old Ninja
issue where Zephyr's two-pass linker script generation does not run
automatically; the post-script parses build.ninja and invokes the
gcc -E step directly before the final link.
- boards/nrf54l15dk.json board definition (PlatformIO needs it for the
DK; the Seeed platform only ships the XIAO variants).
- variants/rp2350/rp2350.ini excludes platform/nrf54l15/ from RP2350
build_src_filter so the shared platform tree does not leak between
targets.
- .gitignore: add nRF J-Link / RTT debug artifacts (flash.jlink,
rtt_*.txt).
Shared source changes
---------------------
- src/main.{cpp,h}, src/RedirectablePrint.cpp, src/FSCommon.{cpp,h},
src/mesh/{Channels,NodeDB,RadioLibInterface,MeshService,PhoneAPI}.cpp,
src/mesh/RadioLibInterface.h, src/modules/AdminModule.cpp: add small
guards / helpers so the Zephyr build compiles alongside the Arduino
targets. Behavior on existing boards is unchanged.
Hardware model
--------------
HW_VENDOR maps to meshtastic_HardwareModel_PRIVATE_HW until a dedicated
protobuf enum value is assigned upstream. The variant declares
custom_meshtastic_hw_model = 132 so the maintainers can wire the new
enum value through the protobufs repo after merge.
Hardware note
-------------
The E22-900M30S does not connect its DIO2 pin to TXEN internally — a
wire/solder bridge between DIO2 and TXEN on the module is required for
TX to work. Details and full pin map are in the variant README.
Validation
----------
Built clean against develop. On real hardware (April 2026) the port
passes end-to-end: iOS companion app pairs and connects, configuration
round-trip works, LoRa TX/RX reaches a canonical tbeam on the same mesh
channel, NodeDB updates propagate both ways, and traceroute completes.
Zephyr LittleFS on nrf54l15 supports fs_rename natively, so route it through the same atomic path as ESP32. The previous copyFile+remove fallback truncated the destination before copying, leaving 0-byte files if interrupted mid-write. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LittleFS on the default 9-block (36KB) storage_partition ran out of space during copy-on-write of config.proto, causing fs_write to return ENOSPC and pb_encode to surface "io error" when saving configuration via the mobile app. Reclaim slot1_partition (the MCUboot secondary slot — unused since we flash directly via J-Link) and grow storage_partition to span 0xb6000..0x165000 (~175 blocks). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NodeDB rewrites LoRa config from USERPREFS_LORACONFIG_* on every boot, which prevented reconfiguration via the BLE/serial app. Drop the variant-level defaults; users configure region and modem preset through the app like every other variant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1bdda74 to
f5210fa
Compare
- Add MESH_PERM_READ/MESH_PERM_WRITE macros (READ_AUTHEN/WRITE_AUTHEN) on all mesh service characteristics so clients must complete passkey exchange before accessing fromNum/fromRadio/toRadio/logRadio. - Wire FIXED_PIN mode to bt_passkey_set() so the device advertises a known PIN (config.bluetooth.fixed_pin); RANDOM_PIN keeps default per-pairing random passkey. - Reduce BleDeferredThread HARD_WATCHDOG_MS from 3min to 1min. - prj.conf: CONFIG_BT_SMP_ENFORCE_MITM=y, CONFIG_BT_FIXED_PASSKEY=y, CONFIG_BT_SMP_SC_PAIR_ONLY=n (legacy fallback for clients that abort SC pairing with reason 0x01 within 150ms).
Summary
src/platform/nrf54l15/and does not touch the existingsrc/platform/nrf52/tree.NRF54L15Bluetooth.*): Meshtastic GATT service, legacy connectable advertising, just-works pairing, MTU exchange up to 247, iOS-friendly connection parameters.InternalFileSystemon SPIM20 for config/NodeDB persistence.nrf54l15dk. Preset region isEU_868at 869.5875 MHz / SFNarrow (easy to override via user prefs).Hardware
E22 pins on the DK's J2 header (all E22 pins sit in the P2 HP-domain, 3.0 V; P1 at 1.8 V is below the SX1262 VIH threshold) — full table + reserved-pin map in
variants/nrf54l15/nrf54l15dk/README.md.Files changed
src/platform/nrf54l15/— new platform layer (Arduino shims over Zephyr, SPI/Wire/Stream,InternalFileSystem,NRF54L15Bluetooth, main entry point).variants/nrf54l15/nrf54l15dk/— variant config (PlatformIO env, DT overlay, pin map, wiring README).zephyr/prj.conf+zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.overlay— Zephyr project + board-level config (BT, GPIO, SPI, RTT logging, heap/stack budgets).boards/nrf54l15dk.json— PlatformIO board definition (the Seeed platform only ships the XIAO variants).extra_scripts/nrf54l15_linker.py— post-script that parsesbuild.ninjaand runs the Zephyr two-pass linker-script generation directly, working around a PlatformIO + old-Ninja issue where the second pass never fires on its own.variants/rp2350/rp2350.ini— excludeplatform/nrf54l15/from the RP2350build_src_filter.src/main.*,src/FSCommon.*,src/RedirectablePrint.cpp,src/mesh/{Channels,NodeDB,RadioLibInterface,MeshService,PhoneAPI}.cpp,src/mesh/RadioLibInterface.h,src/modules/AdminModule.cpp) — small guards/helpers so the Zephyr build compiles alongside the Arduino targets. Behavior on existing boards is unchanged..gitignore— addflash.jlinkandrtt_*.txt(nRF J-Link / RTT debug artifacts).Hardware model
HW_VENDORmaps tomeshtastic_HardwareModel_PRIVATE_HWuntil a dedicated protobuf enum value is assigned upstream. The variant declarescustom_meshtastic_hw_model = 132so the enum can be wired through the protobufs repo after merge.Notes on scope
#ifdefguards so other targets are unaffected; easy to review.Test plan
pio run -e nrf54l15dk) against currentdevelop.🤖 Generated with Claude Code