add MinewSemi ME25LS02 board (nRF54L15 + SX1262)

This commit is contained in:
liquidraver
2026-08-22 19:09:13 +02:00
parent 7eb5f3832c
commit 8423693ca6
16 changed files with 474 additions and 2 deletions
+4
View File
@@ -59,6 +59,10 @@ Notes on the mapping:
custom `companionTcp` role.
- **Heltec T114** lists screen and "No screen" variants (subTitle) under one tile.
- **Native-Linux** boards (femtofox, rak6421) are `noflash` download-only tiles.
- **nRF54L15** boards (ME25LS02) are `noflash` too, for a different reason: the SoC has no USB
peripheral, so no bootloader exists and nothing the configurator can drive is possible. They
publish a `.hex` as a plain download, flashed over SWD, and the companion is listed under
`companionBle` only — there is no USB transport to offer.
- **Thumbnails:** folded devices inherit MeshCore's image automatically (device-level
fields come from the official entry on merge), so only the new tiles set a `tooltip`.
Those reuse the configurator's own image set via jsDelivr (`IMG_BASE` in the script);
+24
View File
@@ -35,6 +35,16 @@ Linux_boards=(
rak6421_pi5
)
# nRF54L15 boards. No bootloader exists for this SoC (it has no USB
# peripheral at all), so these build with --no-sysbuild and the app links at
# RRAM base 0x0 -- zephyr.hex IS the whole image. Flashing is SWD only, which
# is why they publish a .hex and no .uf2/.zip, and why they are download-only
# in the Mesh America catalog.
nRF54L_boards=(
me25ls02/nrf54l15/cpuapp
xiao_nrf54l15/nrf54l15/cpuapp
)
ESP32_boards=(
xiao_esp32c3
xiao_esp32c6/esp32c6/hpcore
@@ -115,6 +125,20 @@ if [[ $1 == "nrf" ]]; then
fi
fi
if [[ $1 == "nrf54l" ]]; then
for board in "${nRF54L_boards[@]}"; do
board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g')
echo "Now building $board companion"
west build -b "$board" zephcore --pristine --no-sysbuild
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH".hex
echo "Now building $board repeater"
west build -b "$board" zephcore --pristine --no-sysbuild -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH".hex
done
fi
if [[ $1 == "linux" ]]; then
for board in "${Linux_boards[@]}"; do
# Pick the native_sim variant + cross toolchain for the target SBC arch.
+19 -1
View File
@@ -54,6 +54,7 @@ MAKERS = {
"Ikoka": "Ikoka",
"femtofox": "Femtofox",
"muziworks": "muzi works",
"minewsemi": "MinewSemi",
}
# Manufacturer per device name.
@@ -93,6 +94,7 @@ MAKER_BY_DEVICE = {
"RAK6421 WisMesh (Raspberry Pi)": "rak",
"RAK6421 WisMesh (Raspberry Pi 5)": "rak",
"muzi works R1 Neo": "muziworks",
"MinewSemi ME25LS02": "minewsemi",
}
DESCRIPTION = (
@@ -202,13 +204,22 @@ BOARDS = [
dict(stem="lilygo_tlora_c6-esp32c6-hpcore", kind="esp32", device="LilyGo T-Lora C6", new=True, img="lilygo_tlora_c6.svg"),
dict(stem="heltec_wifi_lora32_v43-esp32s3-procpu", kind="esp32", device="Heltec v4.3", new=True, img="heltec_v4.svg"),
# --- nRF54L15 (SWD only, download-only tile): new tile ----------------
# The nRF54L15 has no USB peripheral, so there is no bootloader and no
# WebUSB/DFU path the configurator could drive -- the Type-C port on the
# MX25LE02 carrier is a CH340x UART bridge. The tile therefore publishes
# the .hex as a plain download and the user flashes it over SWD.
dict(stem="me25ls02-nrf54l15-cpuapp", kind="nrf54l", device="MinewSemi ME25LS02",
new=True, img="lora.svg"),
# --- Native Linux (noflash, download only): new tiles ----------------
dict(stem="zephcore_linux_femtofox", kind="linux", device="Femtofox (Luckfox Pico Mini)", new=True, img="lora.svg"),
dict(stem="zephcore_linux_rak6421", kind="linux", device="RAK6421 WisMesh (Raspberry Pi)", new=True, img="rpi.svg"),
dict(stem="zephcore_linux_rak6421_pi5", kind="linux", device="RAK6421 WisMesh (Raspberry Pi 5)", new=True, img="rpi.svg"),
]
DEVICE_TYPE = {"nrf": "nrf52", "esp32": "esp32", "linux": "noflash"}
DEVICE_TYPE = {"nrf": "nrf52", "esp32": "esp32", "linux": "noflash",
"nrf54l": "noflash"}
# nRF52 erase package (spec §4a). ZephCore's LittleFS layout differs from MeshCore's,
# so the official erase would wipe the wrong region — we point `erase` at ZephCore's
@@ -231,6 +242,8 @@ COMPANION_ROLES = {
"nrf": ["companionBle", "companionUsb"],
"esp32": ["companionBle", "companionUsb"],
"linux": ["companionTcp"],
# nRF54L15 has no USB peripheral -- the companion is BLE-only there.
"nrf54l": ["companionBle"],
}
HASH_RE = r"[0-9a-f]{7,40}"
@@ -270,6 +283,11 @@ def files_for(assets, board, token):
update = find_file(assets, stem, token, variant, r"-update\.bin")
if update:
out.append(("flash-update", update, "Update (app only; keeps settings)"))
elif kind == "nrf54l":
hexf = find_file(assets, stem, token, variant, r"\.hex")
if hexf:
out.append(("download", hexf,
"Firmware image -- flash over SWD (no USB bootloader on this SoC)"))
elif kind == "linux":
elf = find_file(assets, stem, token, variant, r"")
if elf:
@@ -49,6 +49,29 @@ The **LilyGo T3S3** is now supported — ESP32-S3 with an OLED screen, a button
> LR1121) under one name. Flashed onto any of the others it boots and looks fine but never transmits or
> receives. Check which radio your board has first.
## New board: MinewSemi ME25LS02
The **ME25LS02** is now supported — a MinewSemi module pairing an nRF54L15 with an SX1262, aimed at
people building low-cost repeaters. Both companion and repeater builds are provided, and the module's
LLCC68 variant uses the same firmware. Requested by **xpiREC**
([@xpiREC](https://github.com/xpiREC)), who also published the development board's documentation —
[#68](https://github.com/liquidraver/ZephCore/issues/68).
> [!IMPORTANT]
> **This board can only be flashed with an SWD probe** — a J-Link or similar. The nRF54L15 has no USB
> hardware at all, so there is no drag-and-drop UF2 file and no update over a cable. The USB-C socket on
> the development board is a serial adapter for the console, not a programming port. The firmware is
> published as a `.hex` file, and the Mesh America configurator lists the board as a download rather than
> offering to flash it.
The development board's RGB indicator and its user button both work, and the console appears on the
USB-C serial port at 115200 baud.
> [!NOTE]
> **The node keeps time less precisely than most boards.** The module has no 32.768 kHz crystal of its
> own, so ZephCore runs the chip's internal oscillator instead — accurate to about ±250 ppm rather than
> the ±50 ppm a crystal gives. Everything works normally; the clock simply drifts a little faster.
## Commands to your own node get their tick straight away
When you send a command to your node's own chat contact (the one named `v` plus your node name), the app
@@ -244,6 +267,11 @@ Housekeeping, listed for completeness — nothing here changes how a node behave
but no real node has been broken and recovered. Normal healthy nodes are unaffected.
- **Why that one node's identity broke is still unknown.** This release makes sure the same thing can no
longer go unnoticed, but the original cause has not been found.
- **The ME25LS02 has not been run on hardware.** Nobody involved has one yet. Both builds compile, and
the pin mapping was taken from the module's own schematic and cross-checked against MinewSemi's working
sample firmware — but no packet has been sent or received on a real module. Note also that the module's
datasheet contradicts its own schematic about three of the radio's control pins, and lists one pin
twice; the schematic is what ZephCore follows.
- **The antenna amplifier receive setting is compile-checked on every affected board, but has not been
measured on hardware in this form.** The 23 dB figure above comes from the original proposal, which
reached the same pin by a different route. Leaving the setting alone keeps every node behaving exactly
+1 -1
View File
@@ -273,7 +273,7 @@ elseif(BOARD MATCHES ".*esp32.*" OR BOARD MATCHES "station_g2" OR BOARD MATCHES
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")
elseif(BOARD MATCHES ".*nrf54l.*")
elseif(BOARD MATCHES ".*nrf54l.*" OR BOARD MATCHES "me25ls02")
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf54l_common.conf")
elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*nrf54l.*")
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf54l_common.conf")
+5
View File
@@ -132,10 +132,15 @@ revision.
| Board | Build string | Flash |
|----------------------|------------------------------------------------------------------------|-----------------|
| XIAO nRF54L15 | `west build -b xiao_nrf54l15/nrf54l15/cpuapp zephcore --no-sysbuild` | `west flash` |
| MinewSemi ME25LS02 | `west build -b me25ls02/nrf54l15/cpuapp zephcore --no-sysbuild` | SWD (`west flash`) |
Requires J-Link or CMSIS-DAP (built into XIAO board via SAMD11 bridge).
The `--no-sysbuild` flag is required (no MCUboot support yet).
The SoC has no USB peripheral at all, so neither board has a UF2 or DFU path — `zephyr.hex`
links at RRAM base 0x0 and is the complete image, written over SWD. On the ME25LS02's MX25LE02
carrier the USB-C port is a CH340x UART bridge (console only), so it needs an external probe.
### MG24 (Silicon Labs)
| Board | Build string | Flash |
@@ -0,0 +1,9 @@
# Copyright (c) 2026 ZephCore
# SPDX-License-Identifier: MIT
if BOARD_ME25LS02_NRF54L15_CPUAPP
config HAS_BT_CTLR
default BT
endif # BOARD_ME25LS02_NRF54L15_CPUAPP
@@ -0,0 +1,6 @@
# MinewSemi ME25LS02 board configuration
# Copyright (c) 2026 ZephCore
# SPDX-License-Identifier: MIT
config BOARD_ME25LS02
select SOC_NRF54L15_CPUAPP if BOARD_ME25LS02_NRF54L15_CPUAPP
@@ -0,0 +1,14 @@
# Copyright (c) 2026 ZephCore
# SPDX-License-Identifier: MIT
#
# The nRF54L15 has no USB peripheral and this board has no bootloader the
# Type-C port is a CH340x UART bridge only. Flashing is SWD, via a J-Link or
# any probe openocd/nrfutil can drive. `west flash` picks nrfutil first.
board_runner_args(jlink "--device=nRF54L15_M33" "--speed=4000")
board_runner_args(openocd "--cmd-load=nrf54l-load" -c "targets nrf54l.cpu")
include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake)
@@ -0,0 +1,20 @@
# MinewSemi ME25LS02 (nRF54L15 + SX1262) on the MX25LE02 dev board
# Board-specific configuration
# Board identification
CONFIG_ZEPHCORE_BOARD_NAME="ME25LS02"
# Device Information Service model name
CONFIG_BT_DIS_MODEL_NUMBER_STR="ME25LS02"
# 32.768 kHz source: calibrated RC, not a crystal.
# The module carries no LFXO of its own (XL1/XL2 are brought out on P1.00/P1.01
# and the carrier's Y1 sits behind solder bridges), so selecting the crystal
# would hang at boot on any board that lacks one. See the LFXO comment block in
# me25ls02.dts before changing this.
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION=y
# Same SoC and RAM budget as the XIAO nRF54L15 — 450 contacts is the ceiling
# that still fits.
CONFIG_ZEPHCORE_MAX_CONTACTS=450
@@ -0,0 +1,9 @@
# Copyright (c) 2026 ZephCore
# SPDX-License-Identifier: MIT
board:
name: me25ls02
full_name: MinewSemi ME25LS02 on MX25LE02 dev board (nRF54L15 + SX1262)
vendor: minewsemi
socs:
- name: nrf54l15
@@ -0,0 +1,54 @@
/*
* MinewSemi ME25LS02 pin control
* Copyright (c) 2026 ZephCore
* SPDX-License-Identifier: MIT
*
* Pin assignments are fixed by the module: the SX1262 is wired inside the
* ME25LS02 can and those pins are not available for anything else.
* Source: ME25LS02 datasheet rev K §6 electrical schematic (the net list
* beside U2), cross-checked against MinewSemi's own working nRF Connect
* sample. Note that the DS §5.3.2 *table* disagrees with its own schematic
* and lists P0.01 twice the schematic is the one that matches the sample
* and is what we follow. See datasheets/README.md.
*/
&pinctrl {
/* SX1262 on spi00 (the high-speed SPI instance) */
spi00_default: spi00_default {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 2, 1)>,
<NRF_PSEL(SPIM_MOSI, 2, 2)>,
<NRF_PSEL(SPIM_MISO, 2, 4)>;
};
};
spi00_sleep: spi00_sleep {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 2, 1)>,
<NRF_PSEL(SPIM_MOSI, 2, 2)>,
<NRF_PSEL(SPIM_MISO, 2, 4)>;
low-power-enable;
};
};
/* Console UART -> CH340x -> USB-C. Jumpers JP1/JP2 on the dev board
* connect these to the bridge; they are fitted by default. */
uart20_default: uart20_default {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 5)>;
};
group2 {
psels = <NRF_PSEL(UART_RX, 1, 4)>;
bias-pull-up;
};
};
uart20_sleep: uart20_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 5)>,
<NRF_PSEL(UART_RX, 1, 4)>;
low-power-enable;
};
};
};
@@ -0,0 +1,246 @@
/*
* MinewSemi ME25LS02 (nRF54L15 + SX1262/LLCC68) on the MX25LE02 dev board
* Copyright (c) 2026 ZephCore
* SPDX-License-Identifier: MIT
*
* Module (fixed, inside the can schematic rev K §6):
* SPI00: SCK P2.01, MOSI P2.02, MISO P2.04, NSS P2.05
* DIO1 P0.00 BUSY P0.01 NRESET P2.00
* DIO2 -> ANT_SW1 via R3: the SX1262 drives the antenna switch itself.
* There is no RXEN line (ANT_SW2 is strapped to VDD_RADIO by R4).
* Radio clock: 32 MHz crystal Y3 across XTA/XTB. NOT a TCXO DIO3 is
* unconnected, so no dio3-tcxo-voltage here.
* SX1262 DC-DC is fitted (L7 on DCC_SW).
* nRF54L15 DC-DC is fitted (ML1 on DCC), so vregmain runs in DCDC mode.
* HFXO uses external load capacitors (MC12/MC13 at XC1/XC2).
*
* Dev board (MX25LE02 datasheet §5):
* USB-C is a CH340x UART bridge the nRF54L15 has no USB peripheral.
* Console on uart20: TX P1.05, RX P1.04.
* RGB LED D1, common anode to 3V3 (active low): R P0.02, B P0.03, G P0.04.
* K2 user button -> P1.08 (to GND). K1 is nRESET, wired to the SoC pin.
* 32.768 kHz crystal Y1 is on the carrier behind solder bridges SB1/SB3
* see the LFXO note at the bottom of this file before enabling it.
*/
/dts-v1/;
#include <nordic/nrf54l15_cpuapp.dtsi>
#include "me25ls02-pinctrl.dtsi"
#include <zephyr/dt-bindings/lora/sx126x.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
model = "MinewSemi ME25LS02";
compatible = "minewsemi,me25ls02";
chosen {
zephyr,sram = &cpuapp_sram;
zephyr,flash = &cpuapp_rram;
zephyr,flash-controller = &rram_controller;
zephyr,console = &uart20;
zephyr,shell-uart = &uart20;
/*
* Deliberately no zephyr,code-partition: there is no bootloader
* on this part, so the image links at RRAM base 0x0 and the ROM
* starts it directly. CONFIG_USE_DT_CODE_PARTITION stays off.
*/
};
aliases {
lora0 = &lora;
led0 = &green_led;
led1 = &blue_led;
led2 = &red_led;
sw0 = &user_button;
};
leds {
compatible = "gpio-leds";
/* D1 is a common-anode RGB tied to 3V3 — all three are active low. */
red_led: led_0 {
gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
label = "Red LED";
};
green_led: led_1 {
gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
label = "Green LED";
};
blue_led: led_2 {
gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
label = "Blue LED";
};
};
buttons: buttons {
compatible = "gpio-keys";
/* K2 shorts P1.08 to GND; C13 does the hardware debounce. */
user_button: button_0 {
gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
zephyr,code = <INPUT_KEY_0>;
label = "User Button";
};
};
};
&cpuapp_sram {
status = "okay";
};
&grtc {
owned-channels = <0 1 2 3 4 5 6 7 8 9 10 11>;
/* Channels 7-11 reserved for Zero Latency IRQs, 3-4 for FLPR */
child-owned-channels = <3 4 7 8 9 10 11>;
status = "okay";
};
&clock {
status = "okay";
};
&uart20 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&uart20_default>;
pinctrl-1 = <&uart20_sleep>;
pinctrl-names = "default", "sleep";
};
&spi00 {
status = "okay";
cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
pinctrl-0 = <&spi00_default>;
pinctrl-1 = <&spi00_sleep>;
pinctrl-names = "default", "sleep";
/*
* The nRF54L SPIM applies an RX sample delay that the SX126x does not
* tolerate at these clock rates MinewSemi drop it in their own sample
* and so do we. Symptom if left in: garbage on MISO, driver never sees
* the chip.
*/
/delete-property/ rx-delay-supported;
/delete-property/ rx-delay;
lora: lora@0 {
compatible = "semtech,sx1262";
reg = <0>;
spi-max-frequency = <8000000>;
reset-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>;
busy-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
dio1-gpios = <&gpio0 0 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
/* DIO2 drives ANT_SW1; there is no separate RXEN line. */
dio2-tx-enable;
/* Crystal, not TCXO — do not add dio3-tcxo-voltage. */
rx-boosted;
};
};
&gpio0 {
status = "okay";
};
&gpio1 {
status = "okay";
};
&gpio2 {
status = "okay";
};
&gpiote20 {
status = "okay";
};
/* DIO1 and BUSY are on P0.x, which is served by GPIOTE30. */
&gpiote30 {
status = "okay";
};
&regulators {
status = "okay";
};
&vregmain {
status = "okay";
regulator-initial-mode = <NRF5X_REG_MODE_DCDC>;
};
/*
* HFXO: the module fits its own external load capacitors at XC1/XC2, so the
* internal ones must stay off or the 32 MHz oscillator is pulled off frequency.
*/
&hfxo {
load-capacitors = "external";
status = "okay";
};
/*
* LFXO is deliberately NOT enabled.
*
* The ME25LS02 module does not contain a 32.768 kHz crystal it brings XL1/XL2
* out on P1.00/P1.01 and the datasheet shows the crystal only as a reference
* design. The MX25LE02 carrier does fit one (Y1), but behind solder bridges
* SB1/SB3, and a module soldered onto anyone else's board very likely has
* nothing there at all. Configuring LFXO with no crystal present means LFCLK
* never becomes ready and the node hangs at boot instead of failing visibly.
*
* So the board runs the calibrated LFRC instead (K32SRC_RC in board.conf),
* which is what MinewSemi's own shipped firmware does. Cost is clock accuracy:
* ±250 ppm calibrated versus ±50 ppm for a crystal (nRF54L15 DS v1.0 §11.2.2).
* That is fine for BLE and for mesh timing, but it is the reason a node here
* drifts faster than a crystal-equipped board relevant if MeshTimeSync is
* ever tuned against this hardware.
*
* To use the crystal on a carrier that definitely has one, delete
* CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC / add K32SRC_XTAL in board.conf and
* enable &lfxo here with the right load capacitance.
*/
&radio {
status = "okay";
};
&temp {
status = "okay";
};
/* Default Nordic RRAM map, then re-carved below for ZephCore. */
#include <vendor/nordic/nrf54l15_cpuapp_partition.dtsi>
/*
* RRAM layout (1524 KB total, 0x17D000).
*
* No bootloader: the application starts at 0x0 and the linker is given the
* whole RRAM, so slot0/slot1 are meaningless here and are deleted to keep
* anyone from assuming a DFU layout that does not exist. The only partition
* that matters is the filesystem at the top of RRAM.
*
* 0x000000 - 0x14E000 (1336 KB) application (links from 0x0)
* 0x14E000 - 0x165000 (92 KB) LittleFS: identity, prefs, contacts,
* channels, BLE bonds
* 0x165000 - 0x17D000 (96 KB) unused tail
*/
/delete-node/ &slot0_partition;
/delete-node/ &slot1_partition;
/delete-node/ &storage_partition;
&cpuapp_rram {
partitions {
lfs_partition: partition@14e000 {
compatible = "zephyr,mapped-partition";
label = "lfs";
reg = <0x14E000 0x17000>;
};
};
};
/* LittleFS auto-mount — standard /lfs mount point */
#include "../../common/filesystem.dtsi"
@@ -0,0 +1,26 @@
# MinewSemi ME25LS02 default configuration
# SPDX-License-Identifier: MIT
CONFIG_ARM_MPU=y
CONFIG_HW_STACK_PROTECTION=y
CONFIG_NULL_POINTER_EXCEPTION_DETECTION_NONE=y
CONFIG_CACHE_MANAGEMENT=y
CONFIG_EXTERNAL_CACHE=y
# Console on uart20 -> CH340x -> USB-C (no USB peripheral on this SoC)
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_GPIO=y
CONFIG_PINCTRL=y
CONFIG_SPI=y
# RRAM + LittleFS
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NRF_GRTC_START_SYSCOUNTER=y
CONFIG_REGULATOR=y
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: MIT
# Suppress "unique_unit_address_if_enabled" to handle some overlaps
list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled")
+6
View File
@@ -102,9 +102,15 @@ xiao_mg24
```
xiao_nrf54l15/nrf54l15/cpuapp
me25ls02/nrf54l15/cpuapp
```
> Requires `--no-sysbuild` flag: `west build -b xiao_nrf54l15/nrf54l15/cpuapp zephcore --no-sysbuild`
>
> The nRF54L15 has no USB peripheral, so there is no UF2 or DFU path on either board — flash
> `zephyr.hex` over SWD. `me25ls02` is the MinewSemi ME25LS02 module on its MX25LE02 carrier;
> its USB-C port is a CH340x UART bridge carrying the console, not a programming port.
> Untested on hardware.
## Native Linux