Skip to content

ports/nrf: Add Zephyr BLE with on-core controller support.#22

Draft
andrewleech wants to merge 8 commits intopr/zephyr-ble-zephyrfrom
pr/zephyr-ble-nrf
Draft

ports/nrf: Add Zephyr BLE with on-core controller support.#22
andrewleech wants to merge 8 commits intopr/zephyr-ble-zephyrfrom
pr/zephyr-ble-nrf

Conversation

@andrewleech
Copy link
Owner

Summary

Adds Zephyr BLE support to the nRF port (PCA10056 DK and PCA10059 dongle) using the Zephyr BLE host stack with an on-core BLE controller. Unlike the RP2 and STM32 integrations which rely on external BLE controllers (CYW43, STM32WB M0+), the nRF52840 runs the Zephyr BLE controller directly on the application core alongside MicroPython.

This required building the full Zephyr BLE controller (link layer, radio HAL, ticker) against the HAL shim layer, plus IRQ management shims that bridge Zephyr's irq_connect_dynamic() / irq_enable() / irq_disable() to NVIC operations. The controller's radio ISR and ticker timing run at hardware interrupt priority while the host stack processes events cooperatively from the main task via mp_sched_schedule_node().

Also adds SysTick, soft timer, and PendSV dispatch infrastructure to the nRF port which were previously missing and are needed for the scheduler-based BLE polling.

Depends on #21.

Testing

12/12 BLE multitests passing on nRF52840 DK with PYBD as central, and 12/12 Z2Z (DK as peripheral + PCA10059 dongle as central). Bond persistence test passes via the SUSPENDED mode reactivation path (on-core controller can't cleanly restart after bt_disable()).

Performance: ~35ms/notification (GATT), ~2184 B/s (L2CAP) with PYBD; ~25ms/notification, ~2681 B/s Z2Z.

428KB flash (42%), ~220KB RAM (84%) on PCA10056.

Trade-offs and Alternatives

The on-core controller means bt_disable() can't cleanly shut down and restart the radio — the Zephyr controller wasn't designed for this. Instead, ble.active(0) enters a SUSPENDED state that stops advertising/scanning/connections but leaves the stack running. This wastes some power but avoids the need for a full chip reset to reinitialise.

The hci_driver_wrapper.c wraps Zephyr's hci_driver.c to expose file-scope variables for RX polling, avoiding a submodule patch.

Generative AI

I used generative AI tools when creating this PR, but a human has checked the code and is responsible for the description above.

pi-anl added 8 commits March 5, 2026 17:42
Extends the Zephyr BLE HAL layer to support running the BLE controller
on-core alongside the host stack. All changes are guarded by
MICROPY_BLUETOOTH_ZEPHYR_CONTROLLER so host-only ports are unaffected.

New files provide IRQ management (NVIC wrappers), ISR dispatch table,
clock control (HFCLK/LFCLK), and controller kernel stubs (k_poll,
k_thread_create). Existing shims are updated to use real PRIMASK-based
interrupt control and IPSR-based ISR detection when the controller is
active.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Adds Zephyr BLE controller source files (ULL, LLL, ticker, HAL) to
zephyr_ble.mk under MICROPY_BLUETOOTH_ZEPHYR_CONTROLLER=1 guard. Adds
controller configuration defines (CONFIG_BT_CTLR_*, CONFIG_SOC_*,
ticker, LLCP) and header stubs needed by controller code (devicetree,
IRQ, entropy, version).

Also enables L2CAP dynamic channels (COC) in the shared build flags
and adds LTO type-mismatch warning suppression for stub declarations
that intentionally differ from Zephyr internals.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Add 1kHz SysTick handler with millisecond counter (uwTick), PendSV
dispatch mechanism for deferred soft timer processing, and SysTick
init function. Enable SEVONPEND so WFE wakes on SysTick interrupts.

Also unconditionally enable the MicroPython scheduler (previously
gated behind MICROPY_HW_ENABLE_USBDEV) since BLE event processing
requires mp_sched_schedule_node().

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Add Makefile integration, linker script extensions, and board variant
configurations (PCA10056, PCA10059) for building the nRF port with
Zephyr BLE. Includes PCA10059 DAPLink variant and hci_driver_poll_rx
support for the on-core controller.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Add mpzephyrport_nrf.c with on-core BLE controller initialization,
LFXO startup, cooperative HCI polling, and scheduler node callback
handling. Includes NULL callback guard for safe deinit during scheduler
node draining.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Extend the bt_disable deinit path to the nRF port's on-core controller.
The controller is shut down via ll_deinit() called from bt_disable(),
with proper LFXO re-start on next init.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Add work processing interleaving to the nRF port's controller polling
loop. Each HCI packet is followed by a work_process() call to ensure
connection events are handled before subsequent packets.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Move the remaining two Zephyr submodule patches (HCI driver, quirk
reset) into wrapper files, eliminating all custom patches from the
lib/zephyr submodule.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants