From b2e7b08fe7fe49f176aa4fd8140b76469866571f Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 4 Mar 2026 10:56:31 +1100 Subject: [PATCH 1/3] zephyr: Replace BLE bindings with extmod/zephyr_ble. Replace the native Zephyr port's custom BLE bindings with the shared extmod/zephyr_ble integration layer. This unifies the BLE API across all ports using Zephyr BLE, with the native Zephyr port using Zephyr's own kernel primitives instead of the HAL shim stubs. Includes machine.idle() fix to yield to Zephyr threads, and test fixes for nRF52840 DK BLE multitests. Signed-off-by: Andrew Leech --- extmod/zephyr_ble/hal/zephyr_ble_timer.h | 13 +- ports/zephyr/CMakeLists.txt | 40 +- ports/zephyr/boards/nrf52840dk_nrf52840.conf | 51 +- .../boards/nrf52840dk_nrf52840.overlay.usb | 12 + ports/zephyr/boards/nucleo_wb55rg.conf | 3 + .../boards/xiao_ble_nrf52840_sense.conf | 3 + ports/zephyr/modbluetooth_zephyr.c | 997 ------------------ ports/zephyr/modmachine.c | 2 +- ports/zephyr/mpconfigport.h | 51 +- ports/zephyr/mpzephyrport_ble.c | 168 +++ 10 files changed, 294 insertions(+), 1046 deletions(-) create mode 100644 ports/zephyr/boards/nrf52840dk_nrf52840.overlay.usb delete mode 100644 ports/zephyr/modbluetooth_zephyr.c create mode 100644 ports/zephyr/mpzephyrport_ble.c diff --git a/extmod/zephyr_ble/hal/zephyr_ble_timer.h b/extmod/zephyr_ble/hal/zephyr_ble_timer.h index de5952ccd8b1c..7ec71353119c0 100644 --- a/extmod/zephyr_ble/hal/zephyr_ble_timer.h +++ b/extmod/zephyr_ble/hal/zephyr_ble_timer.h @@ -31,6 +31,11 @@ #include #include +#ifdef __ZEPHYR__ +// Native Zephyr: use real kernel types +#include +#else + // Zephyr k_timer abstraction layer for MicroPython // Uses polling-based timer mechanism (cooperative scheduling) @@ -61,12 +66,14 @@ void k_timer_stop(struct k_timer *timer); // Note: K_MSEC, K_NO_WAIT, K_FOREVER are defined in zephyr_ble_work.h // Note: k_yield() is defined in zephyr_ble_kernel.h -// Called by MicroPython scheduler to process timer callbacks -void mp_bluetooth_zephyr_timer_process(void); - // Check if two timeouts are equal static inline bool K_TIMEOUT_EQ(k_timeout_t a, k_timeout_t b) { return a.ticks == b.ticks; } +#endif // __ZEPHYR__ + +// Called by MicroPython scheduler to process timer callbacks +void mp_bluetooth_zephyr_timer_process(void); + #endif // MICROPY_INCLUDED_EXTMOD_ZEPHYR_BLE_HAL_ZEPHYR_BLE_TIMER_H diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 82142de2e084b..f7249f94918a6 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -37,25 +37,6 @@ include(${MICROPY_DIR}/extmod/extmod.cmake) list(APPEND DTS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/dts) -# Define mpy-cross flags, for use with frozen code. -if(CONFIG_RISCV_ISA_RV32I AND CONFIG_RISCV_ISA_EXT_M AND CONFIG_RISCV_ISA_EXT_C) - set(ARCH_FLAGS "") - set(RV32_EXT "") - if(CONFIG_RISCV_ISA_EXT_ZBA) - list(APPEND RV32_EXT "zba") - endif() - if(CONFIG_RISCV_ISA_EXT_ZCMP) - list(APPEND RV32_EXT "zcmp") - endif() - if(RV32_EXT) - list(JOIN RV32_EXT "," RV32_EXT) - set(ARCH_FLAGS "-march-flags=${RV32_EXT}") - endif() - set(MICROPY_CROSS_FLAGS "-march=rv32imc ${ARCH_FLAGS}") -elseif(CONFIG_RISCV_ISA_RV64I AND CONFIG_RISCV_ISA_EXT_M AND CONFIG_RISCV_ISA_EXT_C) - set(MICROPY_CROSS_FLAGS -march=rv64imc) -endif() - if (CONFIG_MICROPY_FROZEN_MODULES) cmake_path(ABSOLUTE_PATH CONFIG_MICROPY_FROZEN_MANIFEST BASE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) set(MICROPY_FROZEN_MANIFEST ${CONFIG_MICROPY_FROZEN_MANIFEST}) @@ -68,7 +49,7 @@ set(MICROPY_SOURCE_PORT machine_spi.c machine_pin.c machine_timer.c - modbluetooth_zephyr.c + mpzephyrport_ble.c modsocket.c modzephyr.c modzsensor.c @@ -173,6 +154,25 @@ zephyr_library_compile_definitions( zephyr_library_sources(${MICROPY_SOURCE_QSTR}) zephyr_library_link_libraries(kernel) +if(CONFIG_BT) + set(MICROPY_SOURCE_ZEPHYR_BLE + ${MICROPY_DIR}/extmod/zephyr_ble/modbluetooth_zephyr.c + ) + zephyr_library_sources(${MICROPY_SOURCE_ZEPHYR_BLE}) + list(APPEND MICROPY_SOURCE_QSTR ${MICROPY_SOURCE_ZEPHYR_BLE}) + zephyr_library_include_directories( + ${MICROPY_DIR}/extmod/zephyr_ble/hal + ${ZEPHYR_BASE}/subsys/bluetooth + ) + zephyr_library_compile_definitions( + MICROPY_BLUETOOTH_ZEPHYR=1 + MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS=1 + MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK=1 + MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE=4096 + ZEPHYR_BLE_DEBUG=0 + ) +endif() + include(${MICROPY_DIR}/py/mkrules.cmake) add_dependencies(BUILD_VERSION_HEADER zephyr_generated_headers) diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.conf b/ports/zephyr/boards/nrf52840dk_nrf52840.conf index dc2ed1c4b49df..72cd68a65b859 100644 --- a/ports/zephyr/boards/nrf52840dk_nrf52840.conf +++ b/ports/zephyr/boards/nrf52840dk_nrf52840.conf @@ -1,18 +1,65 @@ CONFIG_NETWORKING=n + +# USB CDC ACM console +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USBD_CDC_ACM_CLASS=y + CONFIG_BT=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y CONFIG_BT_GATT_DYNAMIC_DB=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y CONFIG_BT_GATT_CLIENT=y -CONFIG_BT_L2CAP_TX_MTU=252 +CONFIG_BT_L2CAP_TX_MTU=512 CONFIG_BT_BUF_ACL_RX_SIZE=256 CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n +# L2CAP CoC throughput: enable DLE and increase buffer counts +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 +CONFIG_BT_BUF_ACL_TX_COUNT=10 +CONFIG_BT_L2CAP_TX_BUF_COUNT=10 +CONFIG_BT_CONN_TX_MAX=10 +CONFIG_BT_CTLR_RX_BUFFERS=3 +CONFIG_BT_BUF_EVT_RX_COUNT=20 +CONFIG_BT_SMP=y +CONFIG_BT_SMP_SC_PAIR_ONLY=n +CONFIG_BT_SMP_ENFORCE_MITM=n +CONFIG_BT_SMP_MIN_ENC_KEY_SIZE=7 +CONFIG_BT_MAX_PAIRED=8 +CONFIG_BT_BONDABLE=y +CONFIG_BT_KEYS_OVERWRITE_OLDEST=y + +# Settings subsystem for bond persistence (required for bonded=true in pairing_complete) +CONFIG_SETTINGS=y +CONFIG_BT_SETTINGS=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y +CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y +CONFIG_BT_RX_STACK_SIZE=8192 + +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 + CONFIG_MICROPY_HEAP_SIZE=98304 -CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=16384 # CONFIG_DYNAMIC_THREAD=y CONFIG_THREAD_CUSTOM_DATA=y CONFIG_THREAD_MONITOR=y CONFIG_THREAD_STACK_INFO=y + +# ECDH config — use default p256-m driver (needed for SMP Secure Connections) +# Full mbedTLS ECP causes DHKey check failure on nRF52840 native controller +CONFIG_BT_LONG_WQ_STACK_SIZE=4096 + +# SMP/pairing debug via RTT +CONFIG_LOG=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BUFFER_SIZE=8192 +CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=8192 +CONFIG_BT_LOG_LEVEL_WRN=y +CONFIG_BT_SMP_LOG_LEVEL_DBG=y +CONFIG_BT_CONN_LOG_LEVEL_INF=y +CONFIG_BT_L2CAP_LOG_LEVEL_INF=y diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.overlay.usb b/ports/zephyr/boards/nrf52840dk_nrf52840.overlay.usb new file mode 100644 index 0000000000000..944f6fdca771a --- /dev/null +++ b/ports/zephyr/boards/nrf52840dk_nrf52840.overlay.usb @@ -0,0 +1,12 @@ +/ { + chosen { + /* Use USB CDC ACM as the console. */ + zephyr,console = &cdc_acm_uart0; + }; +}; + +&zephyr_udc0 { + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; diff --git a/ports/zephyr/boards/nucleo_wb55rg.conf b/ports/zephyr/boards/nucleo_wb55rg.conf index 1c9c2f794cb05..e75fa5fb2d116 100644 --- a/ports/zephyr/boards/nucleo_wb55rg.conf +++ b/ports/zephyr/boards/nucleo_wb55rg.conf @@ -17,6 +17,9 @@ CONFIG_BT_GATT_CLIENT=y CONFIG_BT_L2CAP_TX_MTU=252 CONFIG_BT_BUF_ACL_RX_SIZE=256 CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n +CONFIG_BT_SMP=y +CONFIG_BT_BONDABLE=y +CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y # MicroPython config CONFIG_MICROPY_HEAP_SIZE=131072 diff --git a/ports/zephyr/boards/xiao_ble_nrf52840_sense.conf b/ports/zephyr/boards/xiao_ble_nrf52840_sense.conf index 541ade2af246a..ce2c49e91717d 100644 --- a/ports/zephyr/boards/xiao_ble_nrf52840_sense.conf +++ b/ports/zephyr/boards/xiao_ble_nrf52840_sense.conf @@ -20,6 +20,9 @@ CONFIG_BT_GATT_CLIENT=y CONFIG_BT_L2CAP_TX_MTU=252 CONFIG_BT_BUF_ACL_RX_SIZE=256 CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n +CONFIG_BT_SMP=y +CONFIG_BT_BONDABLE=y +CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y CONFIG_MICROPY_HEAP_SIZE=98304 CONFIG_MAIN_STACK_SIZE=8192 diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c deleted file mode 100644 index c1b8ddfd8b404..0000000000000 --- a/ports/zephyr/modbluetooth_zephyr.c +++ /dev/null @@ -1,997 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019-2021 Damien P. George - * Copyright (c) 2019-2020 Jim Mussared - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/runtime.h" -#include "py/mperrno.h" -#include "py/mphal.h" - -#if MICROPY_PY_BLUETOOTH - -#include -#include -#include -#include -#include -#include -#include "extmod/modbluetooth.h" - -#define DEBUG_printf(...) // printk("BLE: " __VA_ARGS__) - -#define BLE_HCI_SCAN_ITVL_MIN 0x10 -#define BLE_HCI_SCAN_ITVL_MAX 0xffff -#define BLE_HCI_SCAN_WINDOW_MIN 0x10 -#define BLE_HCI_SCAN_WINDOW_MAX 0xffff - -#define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV - -#define MP_BLUETOOTH_ZEPHYR_MAX_SERVICES (8) - -/* This masks Permission bits from GATT API */ -#define GATT_PERM_MASK (BT_GATT_PERM_READ | \ - BT_GATT_PERM_READ_AUTHEN | \ - BT_GATT_PERM_READ_ENCRYPT | \ - BT_GATT_PERM_WRITE | \ - BT_GATT_PERM_WRITE_AUTHEN | \ - BT_GATT_PERM_WRITE_ENCRYPT | \ - BT_GATT_PERM_PREPARE_WRITE) - -#define GATT_PERM_ENC_READ_MASK (BT_GATT_PERM_READ_ENCRYPT | \ - BT_GATT_PERM_READ_AUTHEN) - -#define GATT_PERM_ENC_WRITE_MASK (BT_GATT_PERM_WRITE_ENCRYPT | \ - BT_GATT_PERM_WRITE_AUTHEN) - -enum { - MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF, - MP_BLUETOOTH_ZEPHYR_BLE_STATE_ACTIVE, - MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED, -}; - -enum { - MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE, - MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_DEACTIVATING, - MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_ACTIVE, -}; - -union uuid_u { - struct bt_uuid uuid; - struct bt_uuid_16 u16; - struct bt_uuid_32 u32; - struct bt_uuid_128 u128; -}; - -struct add_characteristic { - uint8_t properties; - uint8_t permissions; - const struct bt_uuid *uuid; -}; - -struct add_descriptor { - uint8_t permissions; - const struct bt_uuid *uuid; -}; - -typedef struct _mp_bt_zephyr_conn_t { - struct bt_conn *conn; - struct _mp_bt_zephyr_conn_t *next; -} mp_bt_zephyr_conn_t; - -typedef struct _mp_bluetooth_zephyr_root_pointers_t { - // list of objects to be tracked by the gc - mp_obj_t objs_list; - - // Characteristic (and descriptor) value storage. - mp_gatts_db_t gatts_db; - - // Service definitions. - size_t n_services; - struct bt_gatt_service *services[MP_BLUETOOTH_ZEPHYR_MAX_SERVICES]; - - // active connections - mp_bt_zephyr_conn_t *connections; -} mp_bluetooth_zephyr_root_pointers_t; - -static int mp_bluetooth_zephyr_ble_state; - -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -static int mp_bluetooth_zephyr_gap_scan_state; -static struct k_timer mp_bluetooth_zephyr_gap_scan_timer; -static struct bt_le_scan_cb mp_bluetooth_zephyr_gap_scan_cb_struct; -#endif - -static struct bt_data bt_ad_data[8]; -static size_t bt_ad_len = 0; -static struct bt_data bt_sd_data[8]; -static size_t bt_sd_len = 0; - -static mp_bt_zephyr_conn_t *mp_bt_zephyr_next_conn; - -static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle); -static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection); -static void mp_bt_zephyr_remove_connection(uint8_t conn_handle); -static void mp_bt_zephyr_connected(struct bt_conn *connected, uint8_t err); -static void mp_bt_zephyr_disconnected(struct bt_conn *disconn, uint8_t reason); -static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid); -static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len); -static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr); -static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value); -static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc); -static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc); -static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc); -static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err); -static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle); - -static struct bt_conn_cb mp_bt_zephyr_conn_callbacks = { - .connected = mp_bt_zephyr_connected, - .disconnected = mp_bt_zephyr_disconnected, -}; - -static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle) { - struct bt_conn_info info; - for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) { - if (connection->conn) { - bt_conn_get_info(connection->conn, &info); - if (info.id == conn_handle) { - return connection; - } - } - } - return NULL; -} - -static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection) { - connection->next = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection; -} - -static void mp_bt_zephyr_remove_connection(uint8_t conn_handle) { - struct bt_conn_info info; - mp_bt_zephyr_conn_t *prev = NULL; - for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) { - if (connection->conn) { - bt_conn_get_info(connection->conn, &info); - if (info.id == conn_handle) { - // unlink this item and the gc will eventually collect it - if (prev != NULL) { - prev->next = connection->next; - } else { - // move the start pointer - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection->next; - } - break; - } else { - prev = connection; - } - } - } -} - -static void mp_bt_zephyr_connected(struct bt_conn *conn, uint8_t err) { - struct bt_conn_info info; - bt_conn_get_info(conn, &info); - - if (err) { - uint8_t addr[6] = {0}; - DEBUG_printf("Connection from central failed (err %u)\n", err); - mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, 0xff, addr); - } else { - DEBUG_printf("Central connected with id %d\n", info.id); - mp_bt_zephyr_next_conn->conn = bt_conn_ref(conn); - mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, info.id, info.le.dst->type, info.le.dst->a.val); - mp_bt_zephyr_insert_connection(mp_bt_zephyr_next_conn); - } -} - -static void mp_bt_zephyr_disconnected(struct bt_conn *conn, uint8_t reason) { - struct bt_conn_info info; - bt_conn_get_info(conn, &info); - DEBUG_printf("Central disconnected (id %d reason %u)\n", info.id, reason); - bt_conn_unref(conn); - mp_bt_zephyr_remove_connection(info.id); - mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, info.le.dst->type, info.le.dst->a.val); -} - -static int bt_err_to_errno(int err) { - // Zephyr uses errno codes directly, but they are negative. - return -err; -} - -// modbluetooth (and the layers above it) work in BE for addresses, Zephyr works in LE. -static void reverse_addr_byte_order(uint8_t *addr_out, const bt_addr_le_t *addr_in) { - for (int i = 0; i < 6; ++i) { - addr_out[i] = addr_in->a.val[5 - i]; - } -} - -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -void gap_scan_cb_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf) { - DEBUG_printf("gap_scan_cb_recv: adv_type=%d\n", info->adv_type); - - if (!mp_bluetooth_is_active()) { - return; - } - - if (mp_bluetooth_zephyr_gap_scan_state != MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_ACTIVE) { - return; - } - - uint8_t addr[6]; - reverse_addr_byte_order(addr, info->addr); - mp_bluetooth_gap_on_scan_result(info->addr->type, addr, info->adv_type, info->rssi, buf->data, buf->len); -} - -static mp_obj_t gap_scan_stop(mp_obj_t unused) { - (void)unused; - mp_bluetooth_gap_scan_stop(); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(gap_scan_stop_obj, gap_scan_stop); - -void gap_scan_cb_timeout(struct k_timer *timer_id) { - DEBUG_printf("gap_scan_cb_timeout\n"); - // Cannot call bt_le_scan_stop from a timer callback because this callback may be - // preempting the BT stack. So schedule it to be called from the main thread. - while (!mp_sched_schedule(MP_OBJ_FROM_PTR(&gap_scan_stop_obj), mp_const_none)) { - k_yield(); - } - // Indicate scanning has stopped so that no more scan result events are generated - // (they may still come in until bt_le_scan_stop is called by gap_scan_stop). - mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_DEACTIVATING; -} -#endif - -int mp_bluetooth_init(void) { - DEBUG_printf("mp_bluetooth_init\n"); - - // Clean up if necessary. - mp_bluetooth_deinit(); - - // Allocate memory for state. - MP_STATE_PORT(bluetooth_zephyr_root_pointers) = m_new0(mp_bluetooth_zephyr_root_pointers_t, 1); - mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db); - - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL; - mp_bt_zephyr_next_conn = NULL; - - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL); - - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE; - k_timer_init(&mp_bluetooth_zephyr_gap_scan_timer, gap_scan_cb_timeout, NULL); - mp_bluetooth_zephyr_gap_scan_cb_struct.recv = gap_scan_cb_recv; - mp_bluetooth_zephyr_gap_scan_cb_struct.timeout = NULL; // currently not implemented in Zephyr - bt_le_scan_cb_register(&mp_bluetooth_zephyr_gap_scan_cb_struct); - #endif - - if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF) { - - bt_conn_cb_register(&mp_bt_zephyr_conn_callbacks); - - // bt_enable can only be called once. - int ret = bt_enable(NULL); - if (ret) { - return bt_err_to_errno(ret); - } - } - - mp_bluetooth_zephyr_ble_state = MP_BLUETOOTH_ZEPHYR_BLE_STATE_ACTIVE; - - DEBUG_printf("mp_bluetooth_init: ready\n"); - - return 0; -} - -int mp_bluetooth_deinit(void) { - DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_zephyr_ble_state); - if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF - || mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED) { - return 0; - } - - mp_bluetooth_gap_advertise_stop(); - - #if CONFIG_BT_GATT_DYNAMIC_DB - for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) { - bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]); - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL; - } - #endif - - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - mp_bluetooth_gap_scan_stop(); - bt_le_scan_cb_unregister(&mp_bluetooth_zephyr_gap_scan_cb_struct); - #endif - - // There is no way to turn off the BT stack in Zephyr, so just set the - // state as suspended so it can be correctly reactivated later. - mp_bluetooth_zephyr_ble_state = MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED; - - MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL; - mp_bt_zephyr_next_conn = NULL; - return 0; -} - -bool mp_bluetooth_is_active(void) { - return mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_ACTIVE; -} - -void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) { - if (!mp_bluetooth_is_active()) { - mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); - } - bt_addr_le_t le_addr; - size_t count = 1; - bt_id_get(&le_addr, &count); - if (count == 0) { - mp_raise_OSError(EIO); - } - reverse_addr_byte_order(addr, &le_addr); - *addr_type = le_addr.type; -} - -void mp_bluetooth_set_address_mode(uint8_t addr_mode) { - mp_raise_OSError(MP_EOPNOTSUPP); -} - -size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { - const char *name = bt_get_name(); - *buf = (const uint8_t *)name; - return strlen(name); -} - -int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) { - char tmp_buf[CONFIG_BT_DEVICE_NAME_MAX + 1]; - if (len + 1 > sizeof(tmp_buf)) { - return MP_EINVAL; - } - memcpy(tmp_buf, buf, len); - tmp_buf[len] = '\0'; - return bt_err_to_errno(bt_set_name(tmp_buf)); -} - -// Zephyr takes advertising/scan data as an array of (type, len, payload) packets, -// and this function constructs such an array from raw advertising/scan data. -static void mp_bluetooth_prepare_bt_data(const uint8_t *data, size_t len, struct bt_data *bt_data, size_t *bt_len) { - size_t i = 0; - const uint8_t *d = data; - while (d < data + len && i < *bt_len) { - bt_data[i].type = d[1]; - bt_data[i].data_len = d[0] - 1; - bt_data[i].data = &d[2]; - i += 1; - d += 1 + d[0]; - } - *bt_len = i; -} - -int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - - mp_bluetooth_gap_advertise_stop(); - - if (adv_data) { - bt_ad_len = MP_ARRAY_SIZE(bt_ad_data); - mp_bluetooth_prepare_bt_data(adv_data, adv_data_len, bt_ad_data, &bt_ad_len); - } - - if (sr_data) { - bt_sd_len = MP_ARRAY_SIZE(bt_sd_data); - mp_bluetooth_prepare_bt_data(sr_data, sr_data_len, bt_sd_data, &bt_sd_len); - } - - struct bt_le_adv_param param = { - .id = 0, - .sid = 0, - .secondary_max_skip = 0, - .options = (connectable ? BT_LE_ADV_OPT_CONNECTABLE : 0) - | BT_LE_ADV_OPT_ONE_TIME - | BT_LE_ADV_OPT_USE_IDENTITY - | BT_LE_ADV_OPT_SCANNABLE, - .interval_min = interval_us / 625, - .interval_max = interval_us / 625 + 1, // min/max cannot be the same value - .peer = NULL, - }; - - // pre-allocate a new connection structure as we cannot allocate this inside the connection callback - mp_bt_zephyr_next_conn = m_new0(mp_bt_zephyr_conn_t, 1); - mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(mp_bt_zephyr_next_conn)); - - return bt_err_to_errno(bt_le_adv_start(¶m, bt_ad_data, bt_ad_len, bt_sd_data, bt_sd_len)); -} - -void mp_bluetooth_gap_advertise_stop(void) { - // Note: bt_le_adv_stop returns 0 if adv is already stopped. - int ret = bt_le_adv_stop(); - if (ret != 0) { - mp_raise_OSError(bt_err_to_errno(ret)); - } -} - -int mp_bluetooth_gatts_register_service_begin(bool append) { - #if CONFIG_BT_GATT_DYNAMIC_DB - - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - - if (append) { - // Don't support append yet (modbluetooth.c doesn't support it yet anyway). - return MP_EOPNOTSUPP; - } - - // Unregister and unref any previous service definitions. - for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) { - bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]); - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL; - } - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services = 0; - - // Reset the gatt characteristic value db. - mp_bluetooth_gatts_db_reset(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db); - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL; - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL); - mp_bt_zephyr_next_conn = NULL; - - return 0; - - #else - return MP_EOPNOTSUPP; - #endif -} - -int mp_bluetooth_gatts_register_service_end(void) { - return 0; -} - -int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint16_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint16_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { - #if CONFIG_BT_GATT_DYNAMIC_DB - if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services >= MP_BLUETOOTH_ZEPHYR_MAX_SERVICES) { - return MP_E2BIG; - } - - // first of all allocate the entire memory for all the attributes that this service is composed of - // 1 for the service itself, 2 for each characteristic (the declaration and the value), and one for each descriptor - size_t total_descriptors = 0; - for (size_t i = 0; i < num_characteristics; ++i) { - total_descriptors += num_descriptors[i]; - // we have to add the CCC manually - if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { - total_descriptors += 1; - } - } - size_t total_attributes = 1 + (num_characteristics * 2) + total_descriptors; - - // allocate one extra so that we can know later where the final attribute is - struct bt_gatt_attr *svc_attributes = m_new(struct bt_gatt_attr, total_attributes + 1); - mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(svc_attributes)); - - size_t handle_index = 0; - size_t descriptor_index = 0; - size_t attr_index = 0; - // bitfield of the handles we should ignore, should be more than enough for most applications - uint64_t attrs_to_ignore = 0; - uint64_t attrs_are_chrs = 0; - uint64_t chr_has_ccc = 0; - - add_service(create_zephyr_uuid(service_uuid), &svc_attributes[attr_index]); - attr_index += 1; - - for (size_t i = 0; i < num_characteristics; ++i) { - - struct add_characteristic add_char; - add_char.uuid = create_zephyr_uuid(characteristic_uuids[i]); - add_char.permissions = 0; - add_char.properties = 0; - if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) { - add_char.permissions |= BT_GATT_PERM_READ; - add_char.properties |= BT_GATT_CHRC_READ; - } - if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) { - add_char.properties |= BT_GATT_CHRC_NOTIFY; - } - if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) { - add_char.properties |= BT_GATT_CHRC_INDICATE; - } - if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) { - add_char.permissions |= BT_GATT_PERM_WRITE; - add_char.properties |= (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP); - } - - add_characteristic(&add_char, &svc_attributes[attr_index], &svc_attributes[attr_index + 1]); - - struct bt_gatt_attr *curr_char = &svc_attributes[attr_index]; - attrs_are_chrs |= (1 << attr_index); - if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { - chr_has_ccc |= (1 << attr_index); - } - attr_index += 1; - attrs_to_ignore |= (1 << attr_index); // ignore the value handle - attr_index += 1; - - if (num_descriptors[i] > 0) { - for (size_t j = 0; j < num_descriptors[i]; ++j) { - - struct add_descriptor add_desc; - add_desc.uuid = create_zephyr_uuid(descriptor_uuids[descriptor_index]); - add_desc.permissions = 0; - if (descriptor_flags[descriptor_index] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) { - add_desc.permissions |= BT_GATT_PERM_READ; - } - if (descriptor_flags[descriptor_index] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) { - add_desc.permissions |= BT_GATT_PERM_WRITE; - } - - add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]); - attr_index += 1; - - descriptor_index++; - } - } - - // to support indications and notifications we must add the CCC descriptor manually - if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { - struct add_descriptor add_desc; - mp_obj_bluetooth_uuid_t ccc_uuid; - ccc_uuid.base.type = &mp_type_bluetooth_uuid; - ccc_uuid.data[0] = BT_UUID_GATT_CCC_VAL & 0xff; - ccc_uuid.data[1] = (BT_UUID_GATT_CCC_VAL >> 8) & 0xff; - ccc_uuid.type = MP_BLUETOOTH_UUID_TYPE_16; - add_desc.uuid = create_zephyr_uuid(&ccc_uuid); - add_desc.permissions = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE; - - attrs_to_ignore |= (1 << attr_index); - - add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]); - attr_index += 1; - } - } - - struct bt_gatt_service *service = m_new(struct bt_gatt_service, 1); - mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(service)); - service->attrs = svc_attributes; - service->attr_count = attr_index; - // invalidate the last attribute uuid pointer so that we new this is the end of attributes for this service - svc_attributes[attr_index].uuid = NULL; - - // Note: advertising must be stopped for gatts registration to work - - int err = bt_gatt_service_register(service); - if (err) { - return bt_err_to_errno(err); - } - - // now that the service has been registered, we can assign the handles for the characteristics and the descriptors - // we are not interested in the handle of the service itself, so we start the loop from index 1 - for (int i = 1; i < total_attributes; i++) { - // store all the relevant handles (characteristics and descriptors defined in Python) - if (!((uint64_t)(attrs_to_ignore >> i) & (uint64_t)0x01)) { - if (svc_attributes[i].user_data == NULL) { - mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); - mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle); - svc_attributes[i].user_data = entry->data; - } else if (((uint64_t)(attrs_are_chrs >> i) & (uint64_t)0x01)) { - if (svc_attributes[i + 1].user_data == NULL) { - mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); - mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle); - svc_attributes[i + 1].user_data = entry->data; - - if (((uint64_t)(chr_has_ccc >> i) & (uint64_t)0x01)) { - // create another database entry for the ccc of this characteristic - mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle + 2, 1); - } - } - } - handles[handle_index++] = svc_attributes[i].handle; - } - } - - MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services++] = service; - - return 0; - - #else - return MP_EOPNOTSUPP; - #endif -} - -int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle); - if (connection) { - return bt_conn_disconnect(connection->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); - } - return MP_ENOENT; -} - -int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); -} - -int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - - int err = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); - - if ((err == 0) && send_update) { - struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1); - mp_bluetooth_gatts_db_entry_t *ccc_entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle + 2); - - if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_NOTIFY)) { - err = bt_gatt_notify(NULL, attr_val, value, value_len); - } else if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_INDICATE)) { - struct bt_gatt_indicate_params params = { - .uuid = NULL, - .attr = attr_val, - .func = mp_bt_zephyr_gatt_indicate_done, - .destroy = NULL, - .data = value, - .len = value_len - }; - err = bt_gatt_indicate(NULL, ¶ms); - } - } - return err; -} - -static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err) { - struct bt_conn_info info; - bt_conn_get_info(conn, &info); - uint16_t chr_handle = params->attr->handle - 1; - mp_bluetooth_gatts_on_indicate_complete(info.id, chr_handle, err); -} - -static ssize_t mp_bt_zephyr_gatts_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { - // we receive the value handle, but to look up in the gatts db we need the characteristic handle, and that is is the value handle minus 1 - uint16_t _handle = attr->handle - 1; - - DEBUG_printf("BLE attr read for handle %d\n", attr->handle); - - mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); - if (!entry) { - // it could be a descriptor instead - _handle = attr->handle; - entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); - if (!entry) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE); - } - } - - return bt_gatt_attr_read(conn, attr, buf, len, offset, entry->data, entry->data_len); -} - -static ssize_t mp_bt_zephyr_gatts_attr_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { - struct bt_conn_info info; - bt_conn_get_info(conn, &info); - - DEBUG_printf("BLE attr write for handle %d\n", attr->handle); - - // the characteristic handle is the value handle minus 1 - uint16_t _handle = attr->handle - 1; - - mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); - if (!entry) { - // it could be a descriptor instead - _handle = attr->handle; - entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); - if (!entry) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE); - } - } - - // Don't write anything if prepare flag is set - if (flags & BT_GATT_WRITE_FLAG_PREPARE) { - return 0; - } - - if (offset > entry->data_alloc) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); - } - - if ((offset + len) > entry->data_alloc) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); - } - - if (entry->append) { - offset = entry->data_len; - } - - // copy the data into the buffer in the gatts database - memcpy(&entry->data[offset], buf, len); - entry->data_len = offset + len; - - mp_bluetooth_gatts_on_write(info.id, _handle); - - return len; -} - -static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle) { - for (int i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; i++) { - int j = 0; - while (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].uuid != NULL) { - if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].handle == value_handle) { - return &MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j]; - } - j++; - } - } - return NULL; -} - -int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - - int err = MP_ENOENT; - mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle); - - if (connection) { - struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1); - - if (attr_val) { - switch (gatts_op) { - case MP_BLUETOOTH_GATTS_OP_NOTIFY: { - err = bt_gatt_notify(connection->conn, attr_val, value, value_len); - break; - } - case MP_BLUETOOTH_GATTS_OP_INDICATE: { - struct bt_gatt_indicate_params params = { - .uuid = NULL, - .attr = attr_val, - .func = mp_bt_zephyr_gatt_indicate_done, - .destroy = NULL, - .data = value, - .len = value_len - }; - err = bt_gatt_indicate(connection->conn, ¶ms); - break; - } - } - } - } - - return err; -} - -int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, len, append); -} - -int mp_bluetooth_get_preferred_mtu(void) { - if (!mp_bluetooth_is_active()) { - mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); - } - mp_raise_OSError(MP_EOPNOTSUPP); -} - -int mp_bluetooth_set_preferred_mtu(uint16_t mtu) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - return MP_EOPNOTSUPP; -} - -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - -int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) { - // Stop any ongoing GAP scan. - int ret = mp_bluetooth_gap_scan_stop(); - if (ret) { - return ret; - } - - struct bt_le_scan_param param = { - .type = active_scan ? BT_HCI_LE_SCAN_ACTIVE : BT_HCI_LE_SCAN_PASSIVE, - .options = BT_LE_SCAN_OPT_NONE, - .interval = MAX(BLE_HCI_SCAN_ITVL_MIN, MIN(BLE_HCI_SCAN_ITVL_MAX, interval_us / 625)), - .window = MAX(BLE_HCI_SCAN_WINDOW_MIN, MIN(BLE_HCI_SCAN_WINDOW_MAX, window_us / 625)), - }; - k_timer_start(&mp_bluetooth_zephyr_gap_scan_timer, K_MSEC(duration_ms), K_NO_WAIT); - mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_ACTIVE; - int err = bt_le_scan_start(¶m, NULL); - return bt_err_to_errno(err); -} - -int mp_bluetooth_gap_scan_stop(void) { - DEBUG_printf("mp_bluetooth_gap_scan_stop\n"); - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - if (mp_bluetooth_zephyr_gap_scan_state == MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE) { - // Already stopped. - return 0; - } - mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE; - k_timer_stop(&mp_bluetooth_zephyr_gap_scan_timer); - int err = bt_le_scan_stop(); - if (err == 0) { - mp_bluetooth_gap_on_scan_complete(); - return 0; - } - return bt_err_to_errno(err); -} - -int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms, int32_t min_conn_interval_us, int32_t max_conn_interval_us) { - DEBUG_printf("mp_bluetooth_gap_peripheral_connect\n"); - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - return MP_EOPNOTSUPP; -} - -int mp_bluetooth_gap_peripheral_connect_cancel(void) { - DEBUG_printf("mp_bluetooth_gap_peripheral_connect_cancel\n"); - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - return MP_EOPNOTSUPP; -} - -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - -// Note: modbluetooth UUIDs store their data in LE. -static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid) { - struct bt_uuid *result = (struct bt_uuid *)m_new(union uuid_u, 1); - mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(result)); - if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { - bt_uuid_create(result, uuid->data, 2); - } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_32) { - bt_uuid_create(result, uuid->data, 4); - } else { // MP_BLUETOOTH_UUID_TYPE_128 - bt_uuid_create(result, uuid->data, 16); - } - return result; -} - -static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len) { - const union uuid_u *u = CONTAINER_OF(pattern->uuid, union uuid_u, uuid); - size_t uuid_size = sizeof(u->u16); - - if (u->uuid.type == BT_UUID_TYPE_32) { - uuid_size = sizeof(u->u32); - } else if (u->uuid.type == BT_UUID_TYPE_128) { - uuid_size = sizeof(u->u128); - } - - memcpy(attr, pattern, sizeof(*attr)); - - // Store the UUID. - attr->uuid = (const struct bt_uuid *)m_new(union uuid_u, 1); - mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->uuid)); - memcpy((void *)attr->uuid, &u->uuid, uuid_size); - - // Copy user_data to the buffer. - if (user_data_len) { - attr->user_data = m_new(uint8_t, user_data_len); - mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->user_data)); - memcpy(attr->user_data, pattern->user_data, user_data_len); - } -} - -static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr) { - union uuid_u *uuid = (union uuid_u *)u; - - size_t uuid_size = sizeof(uuid->u16); - - if (uuid->uuid.type == BT_UUID_TYPE_32) { - uuid_size = sizeof(uuid->u32); - } else if (uuid->uuid.type == BT_UUID_TYPE_128) { - uuid_size = sizeof(uuid->u128); - } - - gatt_db_add(&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&uuid->uuid), attr, uuid_size); -} - -static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value) { - struct bt_gatt_chrc *chrc_data; - - // Add Characteristic Declaration - gatt_db_add(&(struct bt_gatt_attr) - BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC, - BT_GATT_PERM_READ, - bt_gatt_attr_read_chrc, NULL, - (&(struct bt_gatt_chrc) {})), attr_chrc, sizeof(*chrc_data)); - - // Allow prepare writes - ch->permissions |= BT_GATT_PERM_PREPARE_WRITE; - - // Add Characteristic Value - gatt_db_add(&(struct bt_gatt_attr) - BT_GATT_ATTRIBUTE(ch->uuid, - ch->permissions & GATT_PERM_MASK, - mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_value, 0); - - chrc_data = attr_chrc->user_data; - chrc_data->properties = ch->properties; - chrc_data->uuid = attr_value->uuid; -} - -static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { - mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, attr->handle); - entry->data[0] = value; -} - -static struct bt_gatt_attr ccc_definition = BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE); - -static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc) { - struct bt_gatt_chrc *chrc = attr->user_data; - - // Check characteristic properties - if (!(chrc->properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) { - mp_raise_OSError(MP_EINVAL); - } - - // Add CCC descriptor to GATT database - gatt_db_add(&ccc_definition, attr_desc, 0); -} - -static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc) { - struct bt_gatt_chrc *chrc = attr_chrc->user_data; - struct bt_gatt_cep cep_value; - - // Extended Properties bit shall be set - if (!(chrc->properties & BT_GATT_CHRC_EXT_PROP)) { - mp_raise_OSError(MP_EINVAL); - } - - cep_value.properties = 0x0000; - - // Add CEP descriptor to GATT database - gatt_db_add(&(struct bt_gatt_attr)BT_GATT_CEP(&cep_value), attr_desc, sizeof(cep_value)); -} - -static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc) { - if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CEP)) { - add_cep(chrc, attr_desc); - } else if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CCC)) { - add_ccc(chrc, attr_desc); - } else { - // Allow prepare writes - d->permissions |= BT_GATT_PERM_PREPARE_WRITE; - - gatt_db_add(&(struct bt_gatt_attr) - BT_GATT_DESCRIPTOR(d->uuid, - d->permissions & GATT_PERM_MASK, - mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_desc, 0); - } -} - -MP_REGISTER_ROOT_POINTER(struct _mp_bluetooth_zephyr_root_pointers_t *bluetooth_zephyr_root_pointers); - -#endif // MICROPY_PY_BLUETOOTH diff --git a/ports/zephyr/modmachine.c b/ports/zephyr/modmachine.c index 89ad12f185daa..b53ab7229b89a 100644 --- a/ports/zephyr/modmachine.c +++ b/ports/zephyr/modmachine.c @@ -60,5 +60,5 @@ static mp_obj_t machine_reset_cause(void) { MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause); static void mp_machine_idle(void) { - k_yield(); + k_msleep(1); } diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index f52b42fc2f01d..63bb8312b933c 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -36,6 +36,12 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES) #endif +// Enable random module (not included at BASIC_FEATURES level). +// Zephyr provides entropy via hardware RNG (device tree) or +// CONFIG_TEST_RANDOM_GENERATOR fallback (prj.conf). +#define MICROPY_PY_RANDOM (1) +#define MICROPY_PY_RANDOM_EXTRA_FUNCS (1) + // Usually passed from Makefile #ifndef MICROPY_HEAP_SIZE #define MICROPY_HEAP_SIZE (16 * 1024) @@ -91,12 +97,25 @@ #define MICROPY_PY_SOCKET (1) #endif #ifdef CONFIG_BT -#define MICROPY_PY_BLUETOOTH (1) +#define MICROPY_PY_BLUETOOTH (1) +#ifndef MICROPY_BLUETOOTH_ZEPHYR +#define MICROPY_BLUETOOTH_ZEPHYR (1) +#endif #ifdef CONFIG_BT_CENTRAL #define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) #endif +#ifdef CONFIG_BT_GATT_CLIENT +#define MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT (1) +#else #define MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT (0) #endif +#ifdef CONFIG_BT_L2CAP_DYNAMIC_CHANNEL +#define MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS (1) +#endif +#ifdef CONFIG_BT_SMP +#define MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING (1) +#endif +#endif #define MICROPY_PY_BINASCII (1) #define MICROPY_PY_HASHLIB (1) #define MICROPY_PY_OS (1) @@ -113,26 +132,6 @@ #define MICROPY_VFS (1) #define MICROPY_READER_VFS (MICROPY_VFS) -#if defined(CONFIG_RISCV_ISA_RV32I) && defined(CONFIG_RISCV_ISA_EXT_M) && defined(CONFIG_RISCV_ISA_EXT_C) - -#ifndef MICROPY_EMIT_RV32 -#define MICROPY_EMIT_RV32 (1) -#endif - -#ifndef MICROPY_EMIT_INLINE_RV32 -#define MICROPY_EMIT_INLINE_RV32 (1) -#endif - -#ifdef CONFIG_RISCV_ISA_EXT_ZBA -#define MICROPY_EMIT_RV32_ZBA (1) -#endif - -#ifdef CONFIG_RISCV_ISA_EXT_ZCMP -#define MICROPY_EMIT_RV32_ZCMP (1) -#endif - -#endif // CONFIG_RISCV_ISA_RV32I - // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (1) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ @@ -181,10 +180,15 @@ typedef long mp_off_t; #define MP_SSIZE_MAX (0x7fffffff) +// extra built in names to add to the global namespace +#define MICROPY_PORT_BUILTINS \ + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + #if MICROPY_PY_THREAD #define MICROPY_EVENT_POLL_HOOK \ do { \ - mp_handle_pending(MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS); \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ MP_THREAD_GIL_EXIT(); \ k_msleep(1); \ MP_THREAD_GIL_ENTER(); \ @@ -192,7 +196,8 @@ typedef long mp_off_t; #else #define MICROPY_EVENT_POLL_HOOK \ do { \ - mp_handle_pending(MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS); \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ k_msleep(1); \ } while (0); #endif diff --git a/ports/zephyr/mpzephyrport_ble.c b/ports/zephyr/mpzephyrport_ble.c new file mode 100644 index 0000000000000..3b701016effad --- /dev/null +++ b/ports/zephyr/mpzephyrport_ble.c @@ -0,0 +1,168 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 MicroPython Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// No-op implementations for HAL/port functions called by modbluetooth_zephyr.c. +// On native Zephyr, the real kernel handles work queues, timers, HCI transport, +// and net_buf pools. + +#include "py/mpconfig.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_ZEPHYR + +#include +#include "extmod/zephyr_ble/hal/zephyr_ble_work.h" +#include "extmod/zephyr_ble/hal/zephyr_ble_port.h" +#include "extmod/zephyr_ble/hal/zephyr_ble_poll.h" + +// --- Globals referenced by zephyr_ble_work.h --- +volatile bool mp_bluetooth_zephyr_in_wait_loop = false; +volatile int mp_bluetooth_zephyr_hci_processing_depth = 0; + +// --- Work queue (Zephyr's own work thread handles processing) --- +void mp_bluetooth_zephyr_work_process(void) { +} + +void mp_bluetooth_zephyr_work_process_init(void) { +} + +void mp_bluetooth_zephyr_init_phase_enter(void) { +} + +void mp_bluetooth_zephyr_init_phase_exit(void) { +} + +bool mp_bluetooth_zephyr_in_init_phase(void) { + return false; +} + +bool mp_bluetooth_zephyr_init_work_pending(void) { + return false; +} + +struct k_work *mp_bluetooth_zephyr_init_work_get(void) { + return NULL; +} + +void mp_bluetooth_zephyr_work_thread_start(void) { +} + +void mp_bluetooth_zephyr_work_thread_stop(void) { +} + +bool mp_bluetooth_zephyr_work_drain(void) { + return false; +} + +void mp_bluetooth_zephyr_work_reset(void) { +} + +void mp_bluetooth_zephyr_work_debug_stats(void) { +} + +// --- Port hooks --- +void mp_bluetooth_zephyr_port_init(void) { +} + +void mp_bluetooth_zephyr_port_deinit(void) { +} + +void mp_bluetooth_zephyr_port_poll_in_ms(uint32_t ms) { + (void)ms; +} + +// --- HCI (Zephyr handles HCI transport internally) --- +void mp_bluetooth_hci_poll(void) { +} + +void mp_bluetooth_hci_poll_now(void) { +} + +void mp_bluetooth_zephyr_hci_uart_wfi(void) { +} + +void mp_bluetooth_zephyr_hci_uart_process(void) { +} + +// --- Polling subsystem (not needed, Zephyr is event-driven) --- +void mp_bluetooth_zephyr_poll(void) { +} + +void mp_bluetooth_zephyr_poll_init(void) { +} + +void mp_bluetooth_zephyr_poll_deinit(void) { +} + +bool mp_bluetooth_zephyr_buffers_available(void) { + return true; +} + +void mp_bluetooth_zephyr_poll_init_timer(void) { +} + +void mp_bluetooth_zephyr_poll_stop_timer(void) { +} + +void mp_bluetooth_zephyr_poll_cleanup(void) { +} + +// --- Net buf pool (Zephyr manages pools) --- +void mp_net_buf_pool_state_reset(void) { +} + +// --- GATT pool (not using bump allocator on native Zephyr) --- +void mp_bluetooth_zephyr_gatt_pool_reset(void) { +} + +// --- Timer processing (Zephyr kernel handles timers) --- +void mp_bluetooth_zephyr_timer_process(void) { +} + +// --- HCI RX task stubs (Zephyr handles HCI reception internally) --- +void mp_bluetooth_zephyr_hci_rx_task_start(void) { +} + +void mp_bluetooth_zephyr_hci_rx_task_stop(void) { +} + +bool mp_bluetooth_zephyr_hci_rx_task_active(void) { + return false; +} + +void mp_bluetooth_zephyr_hci_rx_task_debug(uint32_t *polls, uint32_t *packets) { + if (polls) { + *polls = 0; + } + if (packets) { + *packets = 0; + } +} + +uint32_t mp_bluetooth_zephyr_hci_rx_queue_dropped(void) { + return 0; +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_ZEPHYR From c905fbe72270b243e8816499a625130c1322caea Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 4 Mar 2026 10:57:15 +1100 Subject: [PATCH 2/3] zephyr: Enable sync events and increase UART buffer for BLE. Enable synchronous BLE events and increase UART RX buffer to 512 bytes for reliable raw-paste operation on the nRF52840 DK. Signed-off-by: Andrew Leech --- ports/zephyr/src/zephyr_getchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/src/zephyr_getchar.c b/ports/zephyr/src/zephyr_getchar.c index bf504a97c9b5a..a88ead84e6119 100644 --- a/ports/zephyr/src/zephyr_getchar.c +++ b/ports/zephyr/src/zephyr_getchar.c @@ -24,7 +24,7 @@ extern int mp_interrupt_char; void mp_sched_keyboard_interrupt(void); void mp_hal_signal_event(void); -#define UART_BUFSIZE (512) +#define UART_BUFSIZE (4096) static uint8_t uart_ringbuf[UART_BUFSIZE]; static uint16_t i_get, i_put; From 6492bd202e9983ad6662738e1799bfde4cf55a98 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 20 Feb 2026 06:50:54 +1100 Subject: [PATCH 3/3] zephyr: Switch nRF52840 DK to UART console, fix USB ordering. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nRF52840 DK's USB CDC ACM console was unreliable — device enumeration failures and stalls during raw-paste mode. Switch to UART via JLink OB (uart0 with hw-flow-control) which is always available. Move USB device stack init from mp_task (after console init) to zephyr_start.c main() (before console init) so CDC ACM UART is ready when the console subsystem opens the device. Add DTR wait for CDC ACM console boards so output isn't lost before a host connects. Reduce MICROPY_REPL_STDIN_BUFFER_MAX to 64 (raw-paste window=32 bytes) to avoid overflowing USB-UART bridge buffers at 115200 baud. Signed-off-by: Andrew Leech --- ports/zephyr/boards/nrf52840dk_nrf52840.conf | 7 +---- .../zephyr/boards/nrf52840dk_nrf52840.overlay | 7 +++++ ports/zephyr/main.c | 9 ++---- ports/zephyr/mpconfigport.h | 9 +++--- ports/zephyr/src/usbd.c | 2 +- ports/zephyr/src/zephyr_start.c | 30 +++++++++++++++++++ 6 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 ports/zephyr/boards/nrf52840dk_nrf52840.overlay diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.conf b/ports/zephyr/boards/nrf52840dk_nrf52840.conf index 72cd68a65b859..8ce4e68870348 100644 --- a/ports/zephyr/boards/nrf52840dk_nrf52840.conf +++ b/ports/zephyr/boards/nrf52840dk_nrf52840.conf @@ -1,9 +1,5 @@ CONFIG_NETWORKING=n -# USB CDC ACM console -CONFIG_USB_DEVICE_STACK_NEXT=y -CONFIG_USBD_CDC_ACM_CLASS=y - CONFIG_BT=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y CONFIG_BT_GATT_DYNAMIC_DB=y @@ -43,7 +39,6 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_MICROPY_HEAP_SIZE=98304 CONFIG_MAIN_STACK_SIZE=16384 -# CONFIG_DYNAMIC_THREAD=y CONFIG_THREAD_CUSTOM_DATA=y CONFIG_THREAD_MONITOR=y CONFIG_THREAD_STACK_INFO=y @@ -52,7 +47,7 @@ CONFIG_THREAD_STACK_INFO=y # Full mbedTLS ECP causes DHKey check failure on nRF52840 native controller CONFIG_BT_LONG_WQ_STACK_SIZE=4096 -# SMP/pairing debug via RTT +# RTT logging for debug (keeps log output off the UART console) CONFIG_LOG=y CONFIG_LOG_BACKEND_RTT=y CONFIG_LOG_BACKEND_UART=n diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.overlay b/ports/zephyr/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000000000..388fcad7f67c4 --- /dev/null +++ b/ports/zephyr/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,7 @@ +/* Enable UART hardware flow control (RTS/CTS) for reliable raw REPL. + * Pins already routed on nRF52840 DK: RTS=P0.5, CTS=P0.7. + * JLink OB UART bridge supports hardware flow control. + */ +&uart0 { + hw-flow-control; +}; diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 8d399358849e7..67519518b8288 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -85,10 +85,6 @@ DT_FOREACH_STATUS_OKAY(micropython_heap, MICROPY_HEAP_DEFINE) static __noinit char heap[MICROPY_HEAP_SIZE]; -#if defined(CONFIG_USB_DEVICE_STACK_NEXT) -extern int mp_usbd_init(void); -#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT) - void init_zephyr(void) { // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap // network addresses. @@ -139,9 +135,8 @@ int real_main(void) { usb_enable(NULL); #endif - #ifdef CONFIG_USB_DEVICE_STACK_NEXT - mp_usbd_init(); - #endif + // Note: CONFIG_USB_DEVICE_STACK_NEXT init is in zephyr_start.c main() + // so it runs before the console is initialised (required for CDC ACM). #if MICROPY_VFS && MICROPY_MODULE_FROZEN_MPY // Mount and/or create the filesystem diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index 63bb8312b933c..3cb30a528a0ee 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -59,6 +59,9 @@ #define MICROPY_ENABLE_FINALISER (MICROPY_VFS) #define MICROPY_HELPER_REPL (1) #define MICROPY_REPL_AUTO_INDENT (1) +// Reduce raw-paste window to 32 bytes for UART-bridge connections (JLink OB). +// Default 256 (window=128) overflows USB-UART bridge buffers at 115200 baud. +#define MICROPY_REPL_STDIN_BUFFER_MAX (64) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_KBD_EXCEPTION (1) #define MICROPY_PY_BUILTINS_BYTES_HEX (1) @@ -187,8 +190,7 @@ typedef long mp_off_t; #if MICROPY_PY_THREAD #define MICROPY_EVENT_POLL_HOOK \ do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ + mp_handle_pending(MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS); \ MP_THREAD_GIL_EXIT(); \ k_msleep(1); \ MP_THREAD_GIL_ENTER(); \ @@ -196,8 +198,7 @@ typedef long mp_off_t; #else #define MICROPY_EVENT_POLL_HOOK \ do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ + mp_handle_pending(MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS); \ k_msleep(1); \ } while (0); #endif diff --git a/ports/zephyr/src/usbd.c b/ports/zephyr/src/usbd.c index 118463a0756a5..af3903752a38e 100644 --- a/ports/zephyr/src/usbd.c +++ b/ports/zephyr/src/usbd.c @@ -190,4 +190,4 @@ int mp_usbd_init(void) { return 0; } -#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT) +#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT) && !defined(CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT) diff --git a/ports/zephyr/src/zephyr_start.c b/ports/zephyr/src/zephyr_start.c index c37af8e5b3a87..066aaa9e75225 100644 --- a/ports/zephyr/src/zephyr_start.c +++ b/ports/zephyr/src/zephyr_start.c @@ -24,17 +24,47 @@ * THE SOFTWARE. */ #include +#include +#include #include "zephyr_getchar.h" int real_main(void); int mp_console_init(void); +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) +extern int mp_usbd_init(void); +#endif + +// Check if the console device is a CDC ACM UART (USB serial). +#define MP_CONSOLE_IS_CDC_ACM DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_console), zephyr_cdc_acm_uart) + +#if MP_CONSOLE_IS_CDC_ACM && defined(CONFIG_UART_LINE_CTRL) +static void mp_wait_for_usb_dtr(void) { + const struct device *dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); + uint32_t dtr = 0; + + while (!dtr) { + uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr); + k_msleep(100); + } +} +#endif + int main(void) { + #if defined(CONFIG_USB_DEVICE_STACK_NEXT) + mp_usbd_init(); + #endif + + #if MP_CONSOLE_IS_CDC_ACM && defined(CONFIG_UART_LINE_CTRL) + mp_wait_for_usb_dtr(); + #endif + #ifdef CONFIG_CONSOLE_SUBSYS mp_console_init(); #else zephyr_getchar_init(); #endif + real_main(); return 0;