Curiosity
Il fratello maggiore dell’XIAO nRF54L15: monta il nRF54LM20A — stessa famiglia nRF54L (Cortex-M33 128 MHz + coprocessore RISC-V FLPR, BLE 6.0 / Thread / Matter / Zigbee) ma con più memoria (~1.5 MB flash / 256 KB RAM). Versione Sense: IMU LSM6DS3TR-C 6 assi + PMIC nPM1300 (charging + fuel gauge) già a bordo. È la direzione MCU del Visla Tag di prossima generazione. Richiede Zephyr RTOS (Arduino non supporta ancora l’nRF54L). A differenza della L15 Sense, entra nella XIAO Expansion Board standard — verificato al bench Visla (OLED + buzzer + pulsante funzionanti).
Specs tecniche
| Parametro | Valore |
|---|---|
| Chip | Nordic nRF54LM20A (Cortex-M33 128 MHz + RISC-V FLPR) |
| Memoria | ~1.5 MB flash / 256 KB RAM (più della L15) |
| Form factor | XIAO standard 21×17.8 mm · 28 pin (D0-D27), 9 analogici |
| Sensori onboard | IMU LSM6DS3TR-C 6 assi (accel+giro) |
| PMIC onboard | nPM1300 (charging LiPo + fuel gauge) |
| Connettività | BLE 6.0 · Thread · Matter · Zigbee · 2.4 GHz proprietario |
| Debug/Flash | CMSIS-DAP onboard → flash da USB-C, niente J-Link né UF2 |
| Software | Zephyr RTOS / nRF Connect SDK (no Arduino IDE) |
| Compat carrier | XIAO Expansion Board ✅ (verificato) · XIAO Grove Base ✅ |
🔧 Bring-up dal bench Visla (21 lug 2026)
Bring-up completo su Zephyr con la XIAO Expansion Board (OLED SSD1306 + buzzer + pulsante + LED RGB): tutto funzionante. Sintesi per non ripartire da zero.
Flash: via CMSIS-DAP onboard (no J-Link, no UF2)
La board ha un debugger CMSIS-DAP integrato → si flasha da USB-C con openocd:
west flash --runner openocd # openocd NCS via nrfutil toolchain-manager launch
openocd auto-sblocca l’APPROTECT di fabbrica (mass-erase via CTRL-AP). Reset manuale: openocd -f <board>/support/openocd.cfg -c "init; reset run; exit". Console = uart20 → USB CDC /dev/cu.usbmodem… a 115200.
Board definition: da Zephyr mainline
boards/seeed/xiao_nrf54lm20a è in Zephyr mainline ma NON in NCS 3.3.0 (che ha solo la xiao_nrf54l15). Il SoC è già supportato → si scaricano i file board da raw.githubusercontent.com/zephyrproject-rtos/zephyr/main/boards/seeed/xiao_nrf54lm20a/ (incluso support/openocd.cfg) dentro il workspace. Build: -b xiao_nrf54lm20a/nrf54lm20a/cpuapp.
I 3 gotcha (board mainline su NCS 3.3.0)
- usbhs_wrapper → vregusb: l’USB nativo referenzia
&vregusb(disabled) → errore__device_dts_ord_NNN undeclared. Fix overlay:&usbhs_wrapper { status = “disabled”; };(l’USB nativo non serve, la console è sul CMSIS-DAP). - CFB (display) usa
k_malloc→ serveCONFIG_HEAP_MEM_POOL_SIZE=4096. - OLED a pixel random: il TWIM nRF54 manda i 1024 B del framebuffer in una transazione ma il concat-buffer è 16 B →
i2c_nrfx_twim: internal buffer insufficient (1+1024>16). Fix:&i2c22 { zephyr,concat-buf-size = <1088>; };.
Pinout Expansion Board (mappa XIAO LM20A)
| Peripheral | Pin XIAO | GPIO nRF54 | Note |
|---|---|---|---|
| OLED SSD1306 128×64 | D4/D5 (I2C) | gpio1.3 / gpio1.7 (i2c22) | @ 0x3C |
| Buzzer passivo | A3 = D3 | gpio1.29 | no PWM instradato → bit-bang ~2.7-3 kHz |
| Pulsante utente | D1 | gpio1.31 | pull-up, active-low |
| LED RGB (blu/rosso/verde) | — | gpio1.22 / 1.23 / 1.24 | onboard |
Firmware di test: xiao-nrf54lm20a-test (OLED via CFB + buzzer + button + LED + BLE advertising). Da qui è nato anche un radar BLE (BLE observer) che sente i beacon Visla vicini e li mostra sull’OLED (device ID, RSSI, contatore pressioni) con beep alla pressione del pulsante.
📡 Channel Sounding — ranging BLE 6.0 (21 lug 2026)
La feature che rende l’nRF54 interessante per Visla: misura la distanza cm-level tra due device. Provata end-to-end al bench Visla — XIAO nRF54LM20A = initiator ↔ Holyiot 25008 (nRF54L15) = reflector. Sull’OLED della XIAO compare la distanza in tempo reale + un allarme di prossimità tipo guinzaglio antifurto (beep oltre soglia).
Risultato al banco
| Metodo di stima | Lettura | Note |
|---|---|---|
| phase_slope | ~2.0 m (stabile) | il più affidabile → è quello che usiamo |
| ifft | 0.6–1.8 m | più rumoroso (multipath) |
| rtt | ~6 m | round-trip time, il meno preciso su BLE |
Spostando il reflector la lettura phase_slope segue la distanza; oltre PROXIMITY_ALARM_M (3 m di default) l’OLED passa a “!! LONTANO !!” con beep intermittente e LED verde spento.
Come è fatto
Sample NCS nrf/samples/bluetooth/channel_sounding (2 ruoli RAS: initiator / reflector). CS NON è broadcast: richiede una connessione tra due device CS-capable, entrambi col SoftDevice Controller di Nordic. Chip CS-capable: nRF54L15/L10, nRF54LM20A/B, nRF54H20 (il nRF54L05 no; nRF52 no).
Gotcha per portarlo su XIAO / Holyiot
- Antenna singola (default) → nessun overlay
cs_antenna_switchnecessario. - XIAO initiator, overlay obbligatorio: la board mainline Seeed forza il controller Zephyr LL (no CS) → ripuntare sul SDC (
&bt_hci_controller{disabled}; &bt_hci_sdc{okay}; chosen{zephyr,bt-hci=&bt_hci_sdc}) + il solito&usbhs_wrapper{disabled}. - Holyiot reflector: nessun overlay, usa già l’SDC di default. Flash via J-Link; la XIAO via CMSIS-DAP.
- Reflector che non riparte dopo un flash della XIAO: resta incastrato “connesso” → power-cycle della CR2032 e riparte in advertising.
Firmware: cs-initiator-xiao (OLED distanza + allarme) · cs-reflector-holyiot.
🛠️ Config del tracker dall’app via BLE (21 lug 2026)
Provisioning del tracker GPS direttamente dall’app, senza cellulare. Un GATT Config Service custom sulla XIAO espone i parametri del tracker come caratteristiche leggibili/scrivibili: l’app si connette in BLE e li configura. Prototipato end-to-end usando il Mac come “app” (Python bleak) → letto la config di fabbrica, riscritto label=‘Tag-Mario’ interval=30s mode=HIGH, riletto ✅, con status notify live (batteria / uptime / GPS fix). L’OLED mostra la config corrente.
Servizio “Visla Config” (UUID base f5a1xxxx-9c3f-4b2a-9e11-435356010000)
| Caratteristica | UUID | Tipo | Accesso |
|---|---|---|---|
| Report interval | ..0002 | uint16 LE (5–3600 s) | R / W |
| GPS mode | ..0003 | uint8 (0 off·1 eco·2 norm·3 high) | R / W |
| Tracker label | ..0004 | utf8 ≤20 B | R / W |
| Status | ..0005 | batt% + uptime + fix | R / Notify |
Advertising nome Visla-Cfg + UUID128 nell’AD (così l’app filtra). Write validate lato firmware (range/lunghezza → BT_ATT_ERR_*).
È un pilastro del Visla Tag/v2: primo setup, cambio APN/server, intervalli — dallo smartphone, senza SIM attiva. Prossimi passi: persistenza in flash (subsystem settings), campi v2 (APN/server/token), pairing + write cifrate (ora il servizio è aperto), poi app iOS/Android.
Firmware: xiao-nrf54lm20a-ble-config (GATT service + OLED) — l’“app” Mac di test è nel README.