feat: add Nordic nRF54L15-DK variant (Zephyr + BLE + LoRa)#10193
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).
The `Merge branch 'develop'` left two ~RadioLibInterface() declarations in src/mesh/RadioLibInterface.h: the inline version added upstream by PR meshtastic#10254 (which independently applied the same UAF guard this PR was carrying) and the out-of-line version this PR introduced. GCC rejects the duplicate, breaking every platform build. Drop the out-of-line declaration + definition; keep upstream's inline form. Also silence the 13 cppcheck low warnings introduced by the new nrf54l15 Arduino shim — Arduino's `String`/`SPISettings` API contract relies on implicit single-arg constructors used pervasively by existing Meshtastic code, so suppress `noExplicitConstructor` inline with a comment instead of breaking the API. The few mechanical wins (`const tmp[2]`, `const uint32_t *sp`) are applied directly.
…-port # Conflicts: # src/main.cpp
bt_pub_key_gen() runs the ECC P256 key generation on bt_long_wq. At default prio=10 (preemptible) and stack=1400 it gets starved by Meshtastic app threads at boot — sc_public_key stays NULL for minutes, smp_public_key() defers with SMP_FLAG_PKEY_SEND, and every SC pairing attempt stalls right after the public-key exchange. iOS shows "Connecting…" forever with no PIN prompt; bleak/CLI fails the first CCC notify write with "Protocol Error 0x05: Insufficient Authentication". Set CONFIG_BT_LONG_WQ_PRIO=0 (highest preemptible, ties with main) and CONFIG_BT_LONG_WQ_STACK_SIZE=4096 (margin for the P256M driver frames). Validated E2E with iOS Meshtastic app: bt_smp_pkey_ready fires within ~40 s of boot, 20 SC Passkey Entry rounds complete with matching pcnf/cfm, encrypt 0x01 / sec_level 0x04 (Authenticated MITM), bonded=1.
|
Thanks @thebentern for the develop merge in Current status:
Quick note on |
Adds the Arduino TwoWire layer for the nRF54L15-DK so Meshtastic's sensor drivers can talk to external I2C devices over the hardware TWIM30 peripheral. Bus binding: - &uart30 disabled in the board overlay (peripheral instance 30 is shared between UARTE30 / TWIM30 / SPIM30 — pick one). Console stays on RTT via CONFIG_RTT_CONSOLE. - New i2c30_default / i2c30_sleep pinctrl with SDA=P0.03 / SCL=P0.04. External 4.7 kOhm pull-ups required on both lines. - &i2c30 enabled at I2C_BITRATE_FAST (400 kHz). - button_3 (SW3, P0.04) deleted from DTS so the pad can be claimed by i2c30 pinctrl; SW3 is still wired to the pad on the DK, do not press it during I2C use or it will short SCL to GND. Arduino layer: - src/platform/nrf54l15/Wire.cpp resolves the DT node at compile time via DEVICE_DT_GET(DT_NODELABEL(i2c30)) and dispatches Arduino's beginTransmission / write / endTransmission / requestFrom to i2c_write / i2c_write_read / i2c_read. Buffer is sized to 256 bytes for forward compatibility with the SE050 secure element on the custom PCB. - Wire.h drops the prior compile-only stubs and exposes the real TwoWire surface. - Arduino.h: BitOrder becomes an enum (not #define) so Adafruit_BusIO's `typedef BitOrder BusIOBitOrder;` compiles. Variant + build flags: - nrf54l15.ini flips HAS_WIRE / HAS_SENSOR / HAS_TELEMETRY from 0 to 1 and cherry-picks the sensor libs Meshtastic needs (BusIO, Sensor, BMP280, BME280, INA219/226/260/3221, SHT4X). The full environmental_base group is avoided because it pulls Adafruit_SSD1306 / Adafruit_GFX which rely on Arduino pin macros the Zephyr shim does not implement. - nrf54l15dk variant.h defines PIN_WIRE_SDA / PIN_WIRE_SCL for parity with the Arduino convention used by other variants. The actual bus wiring is fixed by the overlay pinctrl above. Validated 2026-05-14/15 on the DK with BMP280 @ 0x76 (temperature + barometric pressure) and INA3221 @ 0x42 (rail voltage / current); EnvironmentTelemetry / PowerTelemetry packets transmit successfully over LoRa. Footprint cost on nrf54l15dk: +45 KB flash, +1.7 KB RAM.
installDefaultConfig() now respects two new compile-time prefs: USERPREFS_CONFIG_ENV_TELEM_UPDATE_INTERVAL USERPREFS_CONFIG_ENVIRONMENT_MEASUREMENT_ENABLED The mobile apps enforce a 30 min floor on environment_update_interval in the settings UI, which makes short-interval bring-up testing of new sensor hardware painful — you have to wait half an hour for the first LoRa packet to confirm wiring + driver. With these prefs baked into the variant, the firmware can ship a freshly-flashed device that broadcasts on a shorter cadence (e.g. 900 s) the moment storage_partition is empty. Both prefs are gated on #ifdef so the behavior is unchanged for any variant that does not opt in. Documented in userPrefs.jsonc with the existing telemetry-interval pref block.
CONFIG_BT_MAX_PAIRED defaults to 1, so once the first peer (e.g. an iOS phone) has paired and bonded, every subsequent pairing attempt from a different MAC fails inside bt_keys_get_addr() with no free key slot — the host returns BT_SECURITY_ERR_KEY_DOES_NOT_EXIST and the second peer never gets past SMP. Raise the slot count to 4 so the device can simultaneously hold an iOS phone, a Windows host, a Linux host, and one spare bond. Add BT_KEYS_OVERWRITE_OLDEST so that once the table fills, the LRU peer is evicted on the next pairing rather than rejecting the new peer. This matches the behavior other Meshtastic ports already provide (nRF52 uses CONFIG_BT_PERIPHERAL_PRIO_CONN with similar semantics). Discovered while bringing up the Python CLI on Windows alongside the existing iOS bond.
cppcheck on every CI target (esp32s3, rp2040, rp2350, nrf52840, ...) was
failing the build with two `uninitMemberVar` warnings on TwoWire's
constructor: `txBuf` and `rxBuf` (256-byte arrays) were not initialized.
Even though the buffers are only read after txLen/rxLen is set, leaving
them uninitialized is a footgun if any future caller bypasses the
len-set step. Use C++11 value-initialization in the member initializer
list — costs ~512 B of memset at boot, gains a clean cppcheck pass and
defensive-against-future-changes semantics.
Also reformat Wire.{cpp,h} with the project's `.trunk/configs/.clang-format`
config so the Trunk Check Runner passes — clang-format moved the
`<errno.h>` include before the Zephyr-namespaced ones in Wire.cpp and
collapsed two inline overloads to single lines in Wire.h.
|
Thanks for setting up meshtastic/zephyr @thebentern — really appreciate the offer. Quick update so the fork doesn't end up as dead weight: the upstream fix already exists and was merged into Zephyr mainline back in late 2025 — zephyrproject-rtos/zephyr#97913 "Bluetooth: Host: Run tx_processor on its own thread" (commit The reason the patch is still showing up locally is that the PIO package this PR builds against — So the actual target for the patch isn't
Happy to ping Seeed about (1) on this PR's behalf if useful, or to spike (2) on a follow-up branch. In either case, the meshtastic/zephyr fork doesn't strictly need a branch for this patch — but it's a great fallback for the next time we hit something that hasn't already landed upstream (the Windows-post-MTU LL bug is a likelier candidate; still tracking that one separately via zephyr#109217 + DevZone 360037). Also just pushed 1336fbd — clean merge of |
…nces OUTPUT_GPIO_PIN is never defined and modules/GpioOutputModule.h doesn't exist in the codebase; all #ifdef OUTPUT_GPIO_PIN branches were dead code introduced by the nRF54L15-DK variant commit. Strips the include, the output_gpio_enabled OFF→ON/ON→OFF transition logic in handleSetConfig(), and the digitalRead() reflection in handleGetConfig(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SHTXXSensor (the SHT4x driver) includes <SHTSensor.h>, gated by __has_include(<SHTSensor.h>). That header ships in Sensirion/arduino-sht. Adafruit_SHT4X ships Adafruit_SHT4X.h and has no consumer anywhere in src/, so the SHT40 driver was silently excluded from the build -- the nRF54L15 variant could not read an SHT4x sensor as committed in meshtastic#10193. Replace the dead Adafruit_SHT4X libdep with arduino-sht v1.2.6. Validated on nRF54L15-DK: SHT40-AD1B @0x44, 24.3h soak, 0 reboots, temperature/humidity telemetry stable end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SHTXXSensor (the SHT4x driver) includes <SHTSensor.h>, gated by __has_include(<SHTSensor.h>). That header ships in Sensirion/arduino-sht. Adafruit_SHT4X ships Adafruit_SHT4X.h and has no consumer anywhere in src/, so the SHT40 driver was silently excluded from the build -- the nRF54L15 variant could not read an SHT4x sensor as committed in #10193. Replace the dead Adafruit_SHT4X libdep with arduino-sht v1.2.6. Validated on nRF54L15-DK: SHT40-AD1B @0x44, 24.3h soak, 0 reboots, temperature/humidity telemetry stable end-to-end. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
|
@cvaldess would be curious to hear you thoughts if you end up playing with https://github.com/kartben/meshtastic-zephyr :) |
|
@ Quick context on why I went with the in-tree shim for this PR rather than pulling in ArduinoCore-zephyr:
That said — none of this is a religious choice. Once ArduinoCore-zephyrs PIO integration is solid and the shim starts feeling like the long pole, swapping it out is the obvious next step, and Id happily take a follow-up PR in that direction. Ill definitely take your |
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