Skip to content

WIP: esp32: enable BLE synchronous events and BLE pairing/boindng (v2) #7156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions extmod/modbluetooth.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@
typedef struct {
mp_obj_base_t base;
mp_obj_t irq_handler;
mp_obj_t irq_data_tuple;
#if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD
mp_obj_t irq_event;
#endif
#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
bool irq_scheduled;
mp_obj_t irq_data_tuple;
uint8_t irq_data_addr_bytes[6];
uint16_t irq_data_data_alloc;
mp_obj_array_t irq_data_addr;
Expand Down Expand Up @@ -260,10 +263,10 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args,

o->irq_handler = mp_const_none;

#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Pre-allocate the event data tuple to prevent needing to allocate in the IRQ handler.
o->irq_data_tuple = mp_obj_new_tuple(MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN, NULL);

#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Pre-allocated buffers for address, payload and uuid.
mp_obj_memoryview_init(&o->irq_data_addr, 'B', 0, 0, o->irq_data_addr_bytes);
o->irq_data_data_alloc = MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_BYTES_LEN(MICROPY_PY_BLUETOOTH_RINGBUF_SIZE);
Expand All @@ -274,6 +277,10 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args,
ringbuf_alloc(&o->ringbuf, MICROPY_PY_BLUETOOTH_RINGBUF_SIZE);
#endif

#if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD
mp_bluetooth_port_sync_init();
#endif

MP_STATE_VM(bluetooth) = MP_OBJ_FROM_PTR(o);
}
return MP_STATE_VM(bluetooth);
Expand Down Expand Up @@ -1111,6 +1118,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_inv

#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS

#if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD
STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t bluetooth_in) {
mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(bluetooth_in);
o->irq_event = mp_call_function_2(o->irq_handler, o->irq_event, o->irq_data_tuple);
mp_bluetooth_port_sync_signal_done();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_invoke_irq);
#endif

STATIC mp_obj_t invoke_irq_handler(uint16_t event,
const mp_int_t *numeric, size_t n_unsigned, size_t n_signed,
const uint8_t *addr,
Expand All @@ -1125,8 +1142,7 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event,
mp_obj_array_t mv_data[2];
assert(n_data <= 2);

mp_obj_tuple_t *data_tuple = mp_local_alloc(sizeof(mp_obj_tuple_t) + sizeof(mp_obj_t) * MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN);
data_tuple->base.type = &mp_type_tuple;
mp_obj_tuple_t *data_tuple = MP_OBJ_TO_PTR(o->irq_data_tuple);
data_tuple->len = 0;

for (size_t i = 0; i < n_unsigned; ++i) {
Expand Down Expand Up @@ -1154,11 +1170,18 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event,
}
assert(data_tuple->len <= MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN);

mp_obj_t result = mp_call_function_2(o->irq_handler, MP_OBJ_NEW_SMALL_INT(event), MP_OBJ_FROM_PTR(data_tuple));

mp_local_free(data_tuple);
#if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD
if (!mp_bluetooth_port_sync_is_main_thread()) {
o->irq_event = MP_OBJ_NEW_SMALL_INT(event);
while (!mp_sched_schedule(MP_OBJ_FROM_PTR(&bluetooth_ble_invoke_irq_obj), MP_OBJ_FROM_PTR(o))) {
mp_bluetooth_port_sync_yield();
}
mp_bluetooth_port_sync_wait_for_signal();
return o->irq_event;
}
#endif

return result;
return mp_call_function_2(o->irq_handler, MP_OBJ_NEW_SMALL_INT(event), MP_OBJ_FROM_PTR(data_tuple));
}

#define NULL_NUMERIC NULL
Expand Down
8 changes: 8 additions & 0 deletions extmod/modbluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,14 @@ int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf
int mp_bluetooth_hci_cmd(uint16_t ogf, uint16_t ocf, const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_len, uint8_t *status);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD

#if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD
void mp_bluetooth_port_sync_init(void);
bool mp_bluetooth_port_sync_is_main_thread(void);
void mp_bluetooth_port_sync_yield(void);
void mp_bluetooth_port_sync_wait_for_signal(void);
void mp_bluetooth_port_sync_signal_done(void);
#endif

/////////////////////////////////////////////////////////////////////////////
// API implemented by modbluetooth (called by port-specific implementations):

Expand Down
4 changes: 2 additions & 2 deletions extmod/nimble/modbluetooth_nimble.c
Original file line number Diff line number Diff line change
Expand Up @@ -1791,9 +1791,9 @@ STATIC int ble_store_ram_read(int obj_type, const union ble_store_key *key, unio
case BLE_STORE_OBJ_TYPE_OUR_SEC: {
// <type=our,addr,ediv_rand>
// Find our secret for this remote device, matching this ediv/rand key.
assert(ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)); // Must have address.
// TODO: Support cases where key->sec.peer_addr is BLE_ADDR_ANY, and
// key->sec.ediv_rand_present is false.
assert(key->sec.idx == 0);
assert(key->sec.ediv_rand_present);
key_data = (const uint8_t *)&key->sec.peer_addr;
key_data_len = sizeof(ble_addr_t);
break;
Expand Down
6 changes: 5 additions & 1 deletion ports/esp32/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,15 @@ target_compile_options(${MICROPY_TARGET} PUBLIC
-Wno-missing-field-initializers
)

# Additional include directories needed for private NimBLE headers.
target_include_directories(${MICROPY_TARGET} PUBLIC
${IDF_PATH}/components/bt/host/nimble/nimble
)

# Add additional extmod and usermod components.
target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree)
target_link_libraries(${MICROPY_TARGET} usermod)


# Collect all of the include directories and compile definitions for the IDF components.
foreach(comp ${IDF_COMPONENTS})
micropy_gather_target_properties(__idf_${comp})
Expand Down
3 changes: 3 additions & 0 deletions ports/esp32/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@
// extended modules
#ifndef MICROPY_PY_BLUETOOTH
#define MICROPY_PY_BLUETOOTH (1)
#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (1)
#define MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD (1)
#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1)
#define MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING (1)
#define MICROPY_BLUETOOTH_NIMBLE (1)
#define MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY (1)
#endif
Expand Down
25 changes: 25 additions & 0 deletions ports/esp32/mphalport.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,28 @@ void mp_hal_wake_main_task_from_isr(void) {
portYIELD_FROM_ISR();
}
}

/******************************************************************************/
// Bluetooth event synchronisation helpers.

SemaphoreHandle_t mp_bluetooth_port_sync_sem;

void mp_bluetooth_port_sync_init(void) {
mp_bluetooth_port_sync_sem = xSemaphoreCreateBinary();
}

bool mp_bluetooth_port_sync_is_main_thread(void) {
return xTaskGetCurrentTaskHandle() == mp_main_task_handle;
}

void mp_bluetooth_port_sync_yield(void) {
vPortYield();
}

void mp_bluetooth_port_sync_wait_for_signal(void) {
xSemaphoreTake(mp_bluetooth_port_sync_sem, portMAX_DELAY);
}

void mp_bluetooth_port_sync_signal_done(void) {
xSemaphoreGive(mp_bluetooth_port_sync_sem);
}