Small boards: RP2350, RP2040 and Nordic¶
The microcontroller boards MoonLight's next ports target, and where to buy them in the EU. No MoonLight firmware runs on these boards. They are the targets of the next ports, and a board moves from here to a page of its own when its port ships. The Teensy 4.1 has its own page.
Prices are from 2026-10-11 and include Dutch VAT unless marked ex. AliExpress is worth it only for Waveshare, Seeed and WeAct boards through their official stores, checking the seller per listing; Raspberry Pi and Nordic have no official store there.
RP2350¶
| Board (maker) | Why | Where, price | |
|---|---|---|---|
![]() |
Pico 2 W (Raspberry Pi) | The reference board, with WiFi | Antratek €12.04, Kiwi €8.77 |
![]() |
W5500-EVB-Pico2 (WIZnet) | Wired Ethernet | The Pi Hut £11.20 |
![]() |
Pico Plus 2 W (Pimoroni) | 8 MB PSRAM, 16 MB flash, WiFi | about €25, pre-order |
![]() |
RP2350-Zero (Waveshare) | The tiny one | Tinytronics €6, AliExpress about €6 |
RP2040¶
| Board (maker) | Why | Where, price | |
|---|---|---|---|
![]() |
Pico W (Raspberry Pi) | The only RP2040 with WiFi | Antratek €9.08, Kiwi €6.52 |
![]() |
RP2040-Zero (Waveshare) | The mini one people own | Tinytronics €5, AliExpress |
![]() |
XIAO RP2040 (Seeed) | The XIAO form factor | Kiwi €4.46 |
Nordic¶
No Nordic development kit is needed: the XIAO and the Debug Probe below cover the nRF52840, and the nRF5340 comes after it, on a module board.
| Board (maker) | Why | Where, price | |
|---|---|---|---|
![]() |
XIAO nRF52840 Plus (Seeed) | Tiny, UF2 bootloader, debugged through the Debug Probe | Antratek €12.09 |
![]() |
MDBT53-DB-40 (Raytac) | The nRF5340, once the nRF52840 port runs. Zephyr supports it. | DigiKey, about €15 ex. Raytac ships it in about 14 days, with its own shipping fee. |
Nordic and Zephyr as a target¶
Verdict¶
As a big-LED controller: poor fit. As a portable-core + BLE-UI showcase: a genuinely interesting spike, gated on one load-bearing question (BLE-UI feasibility), which the research below finds to be clean, not hacky. Strongest yes: Zephyr is a first-class C++20 + BSD-sockets + LittleFS RTOS whose primitives map onto almost every function in platform.h cleanly and MoonLight's core is deliberately domain-neutral, so "MoonLight's runtime runs on a tiny BLE chip" is a real, achievable, marketable claim, the port is mechanical, not architectural. Strongest no: the chips have no WiFi, no PSRAM, and no wide parallel LED DMA peripheral, so nRF loses badly at the large-installation job MoonLight's light domain is tuned for.
The reframe from the product owner (see § Reframing below) changes the weighing: the question is not "can nRF drive 16 K LEDs" (it can't) but "is MoonLight's core a compelling showcase on a small BLE device, and can its UI reach that device over BLE with no WiFi/HTTP?" On the first, the core is ~90% portable as-is. On the second, the crux, a BLE GATT channel comfortably carries our JSON control/state model (BLE 5 sustains ~100–700 kbps in practice, orders of magnitude above what the UI needs), and config-over-Web-Bluetooth is well-trodden prior art. The one honest caveat is client reach: Web Bluetooth is Chrome/Edge/Android/desktop only, never iOS Safari, so a no-install browser UI works everywhere except iPhone, where a companion app or a third-party BLE browser is required. That caveat shapes the spike; it does not sink it. Recommendation moves from "defer" to "a scoped showcase spike is justified regardless of the LED limits, if the iOS-client gap is acceptable for a demo."
The nRF hardware¶
| Chip | Core(s) | Clock | RAM | Flash | Radio | Notes |
|---|---|---|---|---|---|---|
| nRF52832 | Cortex-M4F | 64 MHz | 64 KB | 512 KB | BLE 5 | Too small; excluded below |
| nRF52840 | Cortex-M4F | 64 MHz | 256 KB | 1 MB | BLE 5 + 802.15.4 | USB device; QSPI (flash XIP, not a RAM heap) |
| nRF5340 | 2× Cortex-M33 | app 128 MHz + net 64 MHz | 512 KB (app) | 1 MB | BLE 5.4 + 802.15.4 | The second core is the network core, reserved for the radio stack, not a general app core |
| nRF54L15 | Cortex-M33 + RISC-V VPR | 128 MHz | 256 KB | 1.5 MB RRAM | BLE 6 + 802.15.4 | The VPR is a peripheral coprocessor, not an app core |
| nRF54H20 | multi-M33 + RISC-V VPRs | app up to 320 MHz | 1 MB | 2 MB MRAM | BLE 5.4 + 802.15.4 | The family flagship; app + radio cores, like the 5340 pattern scaled up |
No PSRAM, anywhere in the family. There is no external-RAM interface comparable to ESP32's PSRAM (the nRF52840 QSPI does execute-in-place flash, not a mappable data heap). The RAM ceiling is the table above minus the BLE controller/stack (~30–60 KB), Zephyr kernel, and network buffers. Rough usable-for-buffers estimate: ~150 KB on a 256 KB part, ~350 KB on the 5340, ~700–800 KB on the 54H20. Against MoonLight's memory-scaling table (16 K lights proven on the ESP32's ~320 KB internal; Teensy's 1 MB rated "comfortable headroom"), the 52840/54L15 sit below the no-PSRAM ESP32, the 5340 slightly above it, and only the 54H20 reaches Teensy class.
WS2812 output: the DMA story¶
Nordic has no RMT, no LCD_CAM/i80, no Parlio, no FlexIO, no one-shot wide-parallel DMA peripheral at all. Three documented single-lane routes exist, each abusing a serial peripheral + EasyDMA:
- PWM + EasyDMA: each WS2812 bit becomes one 16-bit PWM compare value (≈ 0.32/0.64 µs duty at 800 kHz), DMA-fed from a RAM sequence. Cost: 2 bytes per bit = 48 bytes per RGB LED per lane. This is what Adafruit's nRF52 NeoPixel support and FastLED's
clockless_arm_nrf52use. The nRF52840 has 4 PWM instances (52832 has 3); one WS2812 stream per instance → up to ~4 PWM lanes, each with its own 48 B/LED encode buffer. - I2S + EasyDMA: the classic Nordic-blog trick: run I2S SDOUT at 3.2 MHz and encode each WS2812 bit as a 4-bit symbol (
0xE/0x8). Cost: 4 bytes per color byte = 12 bytes per RGB LED; one EasyDMA transfer carries up to 16 KB ≈ 1365 LEDs. One I2S peripheral, one SDOUT pin → 1 lane. This is the closest analogue to thei80Ws2812*whole-frame-DMA shape. - SPI(M) MOSI: Zephyr's default
worldsemi,ws2812-spidriver; ~3–4 SPI bits per WS2812 bit. One lane per SPIM instance; the nRF52840's SPIM3 runs 32 MHz with large EasyDMA, the lesser SPIMs have small DMA lengths.
Realistic total: ~4–6 heterogeneous lanes (4× PWM + 1× I2S + 1× SPIM3), each behind a different peripheral driver, versus 8–16 uniform lanes on one peripheral on ESP32-S3/P4, or 40+ on Teensy via FlexIO. Combined with the RAM ceiling (a 4-lane PWM setup burns 48 B/LED/lane for encode buffers: 4 × 500 LEDs ≈ 96 KB of the ~150 KB usable), the honest capability estimate for a 256 KB part is ~1–2 K LEDs total, ~4 K on the 5340, maybe 8–10 K on the 54H20. One more hazard FastLED's issue tracker documents: the BLE controller runs at interrupt priorities application code can't mask, so any non-DMA (bit-banged) output glitches under radio load, DMA routes are mandatory, which the three above all are.
Zephyr fit with the platform.h seam¶
Zephyr fundamentals check out. C++20 is a supported language standard (CONFIG_STD_CPP20), and the full GNU libstdc++ is there when the toolchain carries it and the application selects it (CONFIG_GLIBCXX_LIBCPP). The usual embedded restrictions apply: no std::thread or std::mutex, where Zephyr has k_thread and k_mutex, which does not matter here since the seam already hides threading behind spawnPinnedTask; no static-object destructors; exceptions and RTTI opt-in. BSD sockets (a subset), LittleFS, and a devicetree-driven driver model are all first-class.
platform.h seam group |
Zephyr provides | Assessment |
|---|---|---|
Time (millis/micros), yield/delay, reboot |
k_uptime_get, k_cycle_get_32, k_yield, k_msleep, k_busy_wait, sys_reboot |
Easy: direct 1:1 |
Heap (alloc/free, introspection) |
k_malloc / sys_heap + runtime stats |
Easy; hasPsram = false, one heap, the maxAllocBlock-style diagnostics need sys_heap stats plumbing |
allocExec/writeExec (MoonLive JIT) |
Cortex-M executes from RAM; no W^X obstacle | Easy: the Thumb-2 backend the Teensy uses (src/platform/teensy/moonlive_asm_thumb2.cpp) emits Thumb-2, the instruction set the nRF52840's Cortex-M4 shares; unverified there |
RTOS introspection (taskSnapshot), spawnPinnedTask/notifyTask |
k_thread_foreach, k_thread_create + k_event/k_sem |
Easy: but every viable nRF is single application core (the 5340/54H20 second core belongs to the radio stack), so the multicore Step 2 render/encode split has no core to pin to. The seam already handles this: spawnPinnedTask returns false → the caller runs inline, the documented allocate-and-degrade fallback. Same stance as Teensy ("desktop is OS-threaded, Teensy single-core", measured on the P4, see performance.md) |
| GPIO capability/live state | Zephyr gpio API + hand-written per-pin caps table |
With effort: same per-chip table work every target needs |
LED DMA seams (rmtWs2812*, i80Ws2812*, parlioWs2812*) |
None of these peripherals exist | New seam required: an nrfPwm/i2s WS2812 function pair in the same init/buffer/transmit/wait shape. The capability-flag pattern absorbs this cleanly (rmtTxChannels = 0, lcdLanes = 0, parlioLanes = 0, a new lane constant), the existing drivers go inert exactly as they do on desktop, and a new driver guards on the new flag. Loopback self-test possible via GPIOTE+timer capture, but bespoke |
UdpSocket/TcpConnection/TcpServer |
BSD sockets subset, fd-based | Easy port, IF an IP link exists at all (the crux, next section) |
WiFi (wifiSta*, wifiAp*) |
Only via the nRF7002 companion IC (QSPI/SPI-attached, Zephyr WiFi mgmt API) | With effort + extra chip: the same shape as the P4's hasWifiCoprocessor C6 story, so the seam precedent exists |
Ethernet (ethInit) |
Zephyr ships a W5500 SPI MAC driver | With effort: mirrors the S3's hasEthW5500 path exactly |
mDNS, hostname, httpRequest |
Zephyr mDNS responder, sockets | With effort / easy respectively, given IP |
OTA (http_fetch_to_ota, otaBegin, otaWrite, otaEnd) |
MCUboot + mcumgr/SMP (BLE or serial DFU), or hand-rolled HTTP-to-slot given IP | With effort: a different but standard mechanism; the seam's status-buffer contract is implementable |
| Improv provisioning (UART) | USB CDC-ACM on 52840; Improv-BLE is the natural transport on this family | With effort: note Improv's BLE variant fits nRF better than the serial one, but that's a new transport in our Improv module, more than a platform stub |
Audio in (audioMicInit, audioMicRead, audioMicSharedBusFree, audioMicDeinit) |
nRF PDM peripheral (built for MEMS mics) or I2S RX | Easy-to-moderate: arguably nicer than ESP32 (PDM is purpose-built); the analysis is shared domain code, so the port is the microphone's setup, read, bus sharing and release |
i2cScan, filesystem (fs*) |
Zephyr i2c, LittleFS |
Easy: LittleFS is the same fs ESP32 uses |
IR (irRead) |
No RMT-RX equivalent; GPIOTE + timer capture | Bespoke effort: low priority, can stub false like desktop |
Summary: the seam itself is no obstacle, Zephyr is the most MoonLight-shaped RTOS outside ESP-IDF, and the platform_config.h flag pattern (everything inert-by-if constexpr when a capability constant is 0/false) absorbs the missing peripherals exactly as it does for desktop. The three structural gaps are: single app core (multicore split degrades to inline, acceptable, Teensy has the same), no PSRAM (hard memory ceiling, the 256 KB parts sit below the no-PSRAM ESP32), and no native IP networking (below).
The architectural question: is nRF an IP-networked LED controller?¶
Honestly: no, not natively. MoonLight's product surface is IP end to end, the HTTP config server + WebSocket UI, ArtNet/DDP/WLED-sync over UDP, mDNS discovery, OTA over HTTP, Improv provisioning of WiFi credentials. An nRF SoC has BLE + 802.15.4 and nothing else:
- BLE carries no IP for our purposes (IPSP is dead in practice). A BLE-only nRF node cannot run NetworkModule, HttpServerModule, ArtnetDriver, or WLED sync at all, the entire networked surface would be replaced by a new BLE GATT control plane that doesn't exist in MoonLight. That is not a port; it is a second product.
- Thread/802.15.4 does give real UDP/IPv6 (OpenThread), so
UdpSockettechnically works, but at a 250 kbps radio, pixel streaming (one 170-LED DMX universe at 44 Hz ≈ 190 kbps) saturates the mesh. Thread is a control-plane transport, not a pixel-data one. - WiFi via nRF7002 (companion IC over QSPI, Wi-Fi 6, the nRF7002-DK pairs it with an nRF5340) or Ethernet via W5500 SPI restores the whole MoonLight stack unchanged, sockets, HTTP, ArtNet, everything. Both have Zephyr drivers and both match existing seam precedents (the P4's WiFi co-processor, the S3's SPI Ethernet). But then the BOM is a two-chip radio board with fewer LED lanes, less RAM, and a slower CPU than a single ESP32-S3, the nRF's low-power/BLE strengths are dead weight in a mains-powered LED installation.
So the honest framing: as an IP node it fits our architecture but loses to ESP32 on every axis we care about; as a BLE node it doesn't fit our architecture. The genuinely nRF-shaped use case, a battery-powered, BLE- or Matter-controlled wearable/small-strip product, phone-app-controlled, sub-500 LEDs, is real, but it is not the product MoonLight is (large installations, web UI, network protocols, no-reboot live reconfig of a mains-powered controller).
Precedent¶
- FastLED supports nRF52 (Adafruit nRF52 boards): the
clockless_arm_nrf52path drives WS281x via the PWM peripheral + EasyDMA, effectively one strip per PWM instance, with documented friction (BLE interrupt priority vs timing, per-board pin/compile issues, GitHub issues #595, #747). It proves single-to-few-lane WS2812 works; nobody uses it at installation scale. - WLED is ESP-only (ESP8266/ESP32 families); no nRF port exists or is planned, a data point that the networked-LED-controller product category has not found nRF worth the port either.
- Zephyr itself ships three single-lane
led_stripWS2812 drivers (SPI default, I2S, GPIO-bitbang) with nRF DK sample overlays, good bring-up references for a platform-layer driver, all small-N demos. - Nordic's own I2S-WS2812 blog (2016, still the canonical reference) demonstrates the 4-bit-symbol I2S encode; community repos (electronut, cheehieu, edwios) replicate it at 12–240 LEDs.
Reframing: core-as-library and the BLE-UI question¶
The sections above answer "is nRF a good LED-installation controller" (no). But MoonLight is more than its light domain, its src/core/ is a domain-neutral runtime that the architecture explicitly holds apart from lights (see architecture.md § Core and light domain: "Core: MoonModule base, controls, scheduling, persistence, platform abstraction... Domain-neutral. Knows nothing about lights."). Offered as a library, that core is a compelling thing in its own right, and the interesting fit question becomes: does MoonLight's runtime, not its LED pipeline, run cleanly on a tiny BLE device, and can its UI reach it? This reframes nRF from "loses the big-installation race" to "a showcase of how far MoonLight's core stretches."
1. How much of the core is portable as-is?¶
Nearly all of it. What the core owns, and how its network modules go inert on a capability flag: MoonCore. Zephyr gives the runtime a heap, a filesystem, and a clock, which is all it asks, and the UI transport that would normally ride HTTP/WS has to come from somewhere else (§ 3).
The honest figure: the domain-neutral core ports with essentially no architectural work, it's the same code the desktop build already runs headless-of-LEDs. The port cost is the platform seam (§ Zephyr fit above) plus one new thing the desktop never needed: a UI transport that isn't HTTP (§ 3). That single item is the whole ballgame.
2. A few LEDs is a feature¶
The ~1–2 K-LED ceiling on a 256 KB nRF part reads as a shortfall against 16 K-on-ESP32, but for the device class nRF is (battery-powered, BLE-remote, wearable, retail/prop lighting), a handful to a few hundred WS2812 is the whole point, and MoonLight's pipeline runs identically at that scale (the architecture's "effects must run at every grid size" rule guarantees a 16-LED strip or an 8×8 matrix is a first-class configuration, not a degraded one). The single-lane I2S-EasyDMA route (≈ 1365 LEDs, § WS2812 output) is more than enough for this niche, and the PWM route gives a few independent short strips. So the small-device story isn't "MoonLight crippled", it's "the same full pipeline, small, battery-friendly, phone-controlled," which is a coherent product the ESP32 line doesn't naturally serve (ESP32 + WiFi is mains-power-shaped). Re-weighed: the LED limit is a positioning, not a defect, for this specific niche.
3. THE crux: can MoonLight's UI run over BLE alone?¶
MoonLight's UI ↔ device link is, at its heart, a JSON control/state channel: the browser sends control writes, the device notifies state back (over a WebSocket). Nothing about that model is HTTP-specific, it maps directly onto a BLE GATT custom service: one write (or write-without-response) characteristic for control-set, one notify characteristic for state-push. This is the exact shape of well-established prior art, and the research says the path is clean, not hacky:
- Throughput is a non-issue. BLE 5 with a 247-byte ATT MTU and DLE sustains a theoretical ~97 kB/s (≈ 780 kbps) of notifications, and real-world BLE-throughput guides put practical rates at ~100–700 kbps depending on connection interval and MTU. MoonLight's state sync is a few hundred bytes to low-KB of JSON per push at ~1 Hz for full state, with small deltas on control changes, three-plus orders of magnitude under the ceiling. Chunking a larger JSON blob across successive notifications (each ≤ MTU−3 bytes) is the standard technique and is trivial here. BLE bandwidth is not the limiting factor for our UI.
- The channel maps to our existing seams naturally. State-notify ↔ the WS state push (produced already, framed onto a characteristic); control-write ↔ the
/api/controlmutation path (the same apply-core + per-control validators the HTTP API and the ImprovAPPLY_OP"REST over serial" path already reuse). In other words MoonLight already has a transport-agnostic control/state core with two transports (HTTP/WS and Improv-serial); BLE GATT is a third transport onto the same core, which is precisely the extensibility the "Improv = REST over serial" design demonstrated. - Prior art is abundant and specifically config-UI-over-BLE. Zephyr + Web Bluetooth config UIs, Moddable's JSON-defined GATT services, Nordic's custom-BLE-service tutorials, and the entire ESP32-WiFi-provisioning-over-BLE ecosystem (Espressif's own BLE provisioning, plus community Web-BLE config portals like UriShX's
esp32_web-ble_wifi_config) all do exactly "browser/app ↔ BLE GATT ↔ device settings." MoonLight's Improv-BLE-style provisioning would slot in beside it. - The one real caveat, client reach. Web Bluetooth (browser → GATT with no app install) is Chromium-only: Chrome/Edge on desktop + Android, never Firefox, and crucially never Safari on iOS/iPadOS (Apple's WebKit rule means every iOS browser lacks it, Chrome-on-iOS included). Global support is ~76% (April 2026). So the frictionless "open a web page, connect, configure" experience works on Android + desktop but not on iPhone, where the fallbacks are a native companion app, a third-party BLE browser (Bluefy/WebBLE), or Nordic's nRF Connect app. This is the single honest wart on the BLE-UI story: the device side is clean, but the no-install browser client has an iOS-shaped hole.
Verdict on the crux: the BLE-UI path is clean at the protocol/device level and a well-trodden pattern, it is a weekend-to-two-weeks spike, not a research project, for the device firmware + an Android/desktop Web Bluetooth client. It becomes a larger undertaking only if the showcase must include a first-class iOS client (then: a small companion app, a separate effort). Framed as a demo/advertisement, the Android/desktop Web Bluetooth client is enough to make the claim land.
The advertising value¶
"MoonLight's full runtime, the same MoonModule lifecycle, controls, persistence, and generic UI you run on a 16 K-LED ESP32 installation, also runs on a coin-cell BLE chip, configured live from your browser over Bluetooth" is a strong, concrete credibility claim. It demonstrates the domain-neutral-core thesis better than any doc: the same core, top to bottom, from a battery BLE wearable to a mains ESP32 wall of light. That is a real reason to do the spike independent of whether nRF ever becomes a shipping LED target.
Recommendation¶
Do a scoped showcase spike; do not commit to nRF as a shipping LED target. The two questions have opposite answers and must not be conflated:
- nRF as a big-LED installation controller: decline / defer. Every such configuration underperforms an ESP32 (no PSRAM, ~4–6 heterogeneous lanes, companion chip needed for IP). The multicore split, PSRAM-scale buffers, and 8–16-lane output on the roadmap all regress here. Teensy remains the right next hardware LED target (1 MB RAM, built-in Ethernet, 40+ DMA lanes).
- MoonLight's portable core + BLE-UI as a showcase: pursue as a time-boxed spike. The core ports with essentially no architectural work, the small-LED story is a coherent niche rather than a crippling, and, the decisive finding, the BLE-UI channel is clean and well-precedented, bandwidth-comfortable, and reuses the transport-agnostic control/state core we already have. The showcase/advertising value is real and lands independent of LED scale. Gate: acceptable only if the iOS-client gap (Web Bluetooth ≠ Safari) is fine for a demo, which for an advertisement/proof-of-concept it is (Android + desktop Chrome/Edge reach the device; an iOS companion app is a later, separate call).
Concrete first step if pursued (sized as a spike, ~1–2 weeks):
1. A src/platform/nrf/ skeleton under Zephyr on an nRF52840-DK: platform_config.h with all LED-lane constants 0 except one new PWM/I2S lane flag, hasNetwork = false, hasWiFi/hasEthernet/hasOta = false; implement the time / heap / fs (LittleFS) / GPIO seam groups. This alone proves the domain-neutral core boots and runs its Scheduler + persistence on Zephyr.
2. One I2S-EasyDMA WS2812 driver (the whole-frame-DMA shape, closest to i80Ws2812*) on a single lane driving a small strip/matrix, the visible output that makes the demo tangible.
3. The BLE-UI transport: a GATT custom service with a control-write and a state-notify characteristic, framing the existing JSON control/state onto BLE (chunked to MTU), plus a minimal Web Bluetooth client page (Android/desktop Chrome) that renders the generic module UI from the notified state and writes control changes back. This is the load-bearing deliverable, it's what turns "the core runs on nRF" into "and you can use it from your browser over Bluetooth."
That spike answers the three real unknowns, how much RAM survives Zephyr + the BLE stack (does the core fit a 256 KB part?), BLE-radio-vs-DMA timing under load, and end-to-end BLE control/state round-trip latency in the actual UI, for about two weeks, and produces a genuinely marketable artifact whether or not nRF ever ships as an LED target.
What would additionally have to be true to promote it from showcase to a shipping target: a concrete product pull toward BLE/Matter-controlled small-strip devices (wearables, retail, battery props); a decision on the iOS client (companion app vs. Android/desktop-only).
Sources¶
- Nordic product pages: nRF52840, nRF5340, nRF54L15, nRF54H20, nRF7002 Wi-Fi 6 companion IC
- Family comparisons: nRF52840 vs nRF54L15 (BLEFYI), nRF5340 vs nRF54L15 (BLEFYI), nRF54H20 announcement (CNX)
- WS2812 on nRF: Nordic DevZone blog, Driving WS2812B via I2S (the 16 KB EasyDMA ≈ 1365-LED figure), Electronut Labs write-up, nRF52 PWM peripheral guide
- Zephyr: C++ language support (C++98–C++20, restrictions), BSD sockets, WS2812 led_strip sample + SPI/I2S/GPIO drivers, worldsemi,ws2812-i2s binding, nRF7002 DK board doc, nRF54H20 DK board doc
- FastLED nRF52: issue #595, NRF52 status, issue #747, nRF52840 support
- WLED supported platforms (ESP8266/ESP32 only): WLED docs
- BLE-UI throughput: Punch Through, Maximizing BLE Throughput, Memfault Interrupt, A Practical Guide to BLE Throughput, Nordic GATT Throughput Service (the ~97 kB/s @ 251-byte PDU figure)
- Web Bluetooth support: caniuse, Web Bluetooth, MDN, Web Bluetooth API, WebBluetoothCG implementation status, Chromium issue, Web Bluetooth on iOS (unimplemented)
- Config-over-BLE / Web Bluetooth prior art: Zephyr + Web Bluetooth getting started, Moddable JSON-defined GATT services, Nordic custom BLE service tutorial, ESP32 WiFi provisioning over BLE (Espressif/community), UriShX esp32_web-ble_wifi_config (Web-BLE config portal)
Tools¶
| Tool (maker) | Why | Where, price | |
|---|---|---|---|
![]() |
Debug Probe (Raspberry Pi) | SWD for the RP2040, the RP2350 and both Nordic chips, through pyOCD or OpenOCD | Kiwi €13.18 |
![]() |
SN74AHCT125N (TI), two or three | 3.3 V to 5 V for WS2812 data, since every board here drives its pins at 3.3 V | Tinytronics €1 each |
Where to order¶
- Antratek: the Pico 2 W, the Pico W and the XIAO nRF52840 Plus, together with any Teensy items.
- Tinytronics: the level shifters and both Zero boards.
- Kiwi: the Debug Probe.
- The Pi Hut: the W5500-EVB-Pico2.
- DigiKey: the Raytac board, when the nRF5340 comes up.










