extmod/modbluetooth: Re-instate optional no-ringbuf modbluetooth.

This requires that the event handlers are called from non-interrupt context
(i.e. the MicroPython scheduler).

This will allow the BLE stack (e.g. NimBLE) to run from the scheduler
rather than an IRQ like PENDSV, and therefore be able to invoke Python
callbacks directly/synchronously.  This allows writing Python BLE handlers
for events that require immediate response such as _IRQ_READ_REQUEST (which
was previous a hard IRQ) and future events relating to pairing/bonding.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
bound-method-equality
Jim Mussared 2020-10-28 13:06:45 +11:00 committed by Damien George
parent 6d9fdff8d0
commit 81e92d3d6e
6 changed files with 189 additions and 54 deletions

View File

@ -838,15 +838,15 @@ STATIC uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t a
return 0;
}
#if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK
// Allow Python code to override value (by using gatts_write), or deny (by returning false) the read.
// Note this will be a no-op if the ringbuffer implementation is being used, as the Python callback cannot
// be executed synchronously. This is currently always the case for btstack.
if ((buffer == NULL) && (buffer_size == 0)) {
if (!mp_bluetooth_gatts_on_read_request(connection_handle, att_handle)) {
DEBUG_printf("att_read_callback: read request denied\n");
return 0;
}
}
#endif
uint16_t ret = att_read_callback_handle_blob(entry->data, entry->data_len, offset, buffer, buffer_size);
return ret;

View File

@ -47,15 +47,18 @@
#define MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN 5
#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// This formula is intended to allow queuing the data of a large characteristic
// while still leaving room for a couple of normal (small, fixed size) events.
#define MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_BYTES_LEN(ringbuf_size) (MAX((int)((ringbuf_size) / 2), (int)(ringbuf_size) - 64))
#endif
// bluetooth.BLE type. This is currently a singleton, however in the future
// this could allow having multiple BLE interfaces on different UARTs.
typedef struct {
mp_obj_base_t base;
mp_obj_t irq_handler;
#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
bool irq_scheduled;
mp_obj_t irq_data_tuple;
uint8_t irq_data_addr_bytes[6];
@ -64,8 +67,6 @@ typedef struct {
mp_obj_array_t irq_data_data;
mp_obj_bluetooth_uuid_t irq_data_uuid;
ringbuf_t ringbuf;
#if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK
mp_obj_t irq_read_request_data_tuple;
#endif
} mp_obj_bluetooth_ble_t;
@ -206,8 +207,7 @@ STATIC mp_int_t bluetooth_uuid_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bu
return 0;
}
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS && MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
STATIC void ringbuf_put_uuid(ringbuf_t *ringbuf, mp_obj_bluetooth_uuid_t *uuid) {
assert(ringbuf_free(ringbuf) >= (size_t)uuid->type + 1);
ringbuf_put(ringbuf, uuid->type);
@ -252,14 +252,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_GATTS_ON_READ_CALLBACK
// Pre-allocate a separate data tuple for the read request "hard" irq.
o->irq_read_request_data_tuple = mp_obj_new_tuple(2, NULL);
#endif
// 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);
@ -268,6 +264,7 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args,
// Allocate the default ringbuf.
ringbuf_alloc(&o->ringbuf, MICROPY_PY_BLUETOOTH_RINGBUF_SIZE);
#endif
MP_STATE_VM(bluetooth) = MP_OBJ_FROM_PTR(o);
}
@ -290,8 +287,6 @@ STATIC mp_obj_t bluetooth_ble_active(size_t n_args, const mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_active_obj, 1, 2, bluetooth_ble_active);
STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
mp_obj_bluetooth_ble_t *self = MP_OBJ_TO_PTR(args[0]);
if (kwargs->used == 0) {
// Get config value
if (n_args != 2) {
@ -311,8 +306,12 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
mp_obj_t items[] = { MP_OBJ_NEW_SMALL_INT(addr_type), mp_obj_new_bytes(addr, MP_ARRAY_SIZE(addr)) };
return mp_obj_new_tuple(2, items);
}
case MP_QSTR_rxbuf:
#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
case MP_QSTR_rxbuf: {
mp_obj_bluetooth_ble_t *self = MP_OBJ_TO_PTR(args[0]);
return mp_obj_new_int(self->ringbuf.size);
}
#endif
case MP_QSTR_mtu:
return mp_obj_new_int(mp_bluetooth_get_preferred_mtu());
default:
@ -334,6 +333,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
bluetooth_handle_errno(mp_bluetooth_gap_set_device_name(bufinfo.buf, bufinfo.len));
break;
}
#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
case MP_QSTR_rxbuf: {
// Determine new buffer sizes
mp_int_t ringbuf_alloc = mp_obj_get_int(e->value);
@ -347,6 +347,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
uint8_t *irq_data = m_new(uint8_t, irq_data_alloc);
// Get old buffer sizes and pointers
mp_obj_bluetooth_ble_t *self = MP_OBJ_TO_PTR(args[0]);
uint8_t *old_ringbuf_buf = self->ringbuf.buf;
size_t old_ringbuf_alloc = self->ringbuf.size;
uint8_t *old_irq_data_buf = (uint8_t *)self->irq_data_data.items;
@ -367,6 +368,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
m_del(uint8_t, old_irq_data_buf, old_irq_data_alloc);
break;
}
#endif
case MP_QSTR_mtu: {
mp_int_t mtu = mp_obj_get_int(e->value);
bluetooth_handle_errno(mp_bluetooth_set_preferred_mtu(mtu));
@ -845,8 +847,7 @@ const mp_obj_module_t mp_module_ubluetooth = {
// Helpers
#include <stdio.h>
#if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size_t n_u16, size_t n_u8, mp_obj_array_t *bytes_addr, size_t n_i8, mp_obj_bluetooth_uuid_t *uuid, mp_obj_array_t *bytes_data) {
assert(ringbuf_avail(ringbuf) >= n_u16 * 2 + n_u8 + (bytes_addr ? 6 : 0) + n_i8 + (uuid ? 1 : 0) + (bytes_data ? 1 : 0));
size_t j = 0;
@ -854,7 +855,7 @@ STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size
for (size_t i = 0; i < n_u16; ++i) {
data_tuple->items[j++] = MP_OBJ_NEW_SMALL_INT(ringbuf_get16(ringbuf));
}
if (n_u8) {
for (size_t i = 0; i < n_u8; ++i) {
data_tuple->items[j++] = MP_OBJ_NEW_SMALL_INT(ringbuf_get(ringbuf));
}
if (bytes_addr) {
@ -960,11 +961,166 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) {
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_invoke_irq);
#endif // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// ----------------------------------------------------------------------------
// Port API
// ----------------------------------------------------------------------------
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
STATIC mp_obj_t invoke_irq_handler(uint16_t event,
const uint16_t *u16, size_t n_u16,
const uint8_t *u8, size_t n_u8,
const uint8_t *addr,
const int8_t *i8, size_t n_i8,
const mp_obj_bluetooth_uuid_t *uuid,
const uint8_t *data, size_t data_len) {
mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth));
if (o->irq_handler == mp_const_none) {
return mp_const_none;
}
mp_obj_array_t mv_addr;
mp_obj_array_t mv_data;
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;
data_tuple->len = 0;
for (size_t i = 0; i < n_u16; ++i) {
data_tuple->items[data_tuple->len++] = MP_OBJ_NEW_SMALL_INT(u16[i]);
}
for (size_t i = 0; i < n_u8; ++i) {
data_tuple->items[data_tuple->len++] = MP_OBJ_NEW_SMALL_INT(u8[i]);
}
if (addr) {
mp_obj_memoryview_init(&mv_addr, 'B', 0, 6, (void *)addr);
data_tuple->items[data_tuple->len++] = MP_OBJ_FROM_PTR(&mv_addr);
}
for (size_t i = 0; i < n_i8; ++i) {
data_tuple->items[data_tuple->len++] = MP_OBJ_NEW_SMALL_INT(i8[i]);
}
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
if (uuid) {
data_tuple->items[data_tuple->len++] = MP_OBJ_FROM_PTR(uuid);
}
#endif
if (data) {
mp_obj_memoryview_init(&mv_data, 'B', 0, data_len, (void *)data);
data_tuple->items[data_tuple->len++] = MP_OBJ_FROM_PTR(&mv_data);
}
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);
return result;
}
#define NULL_U16 NULL
#define NULL_U8 NULL
#define NULL_ADDR NULL
#define NULL_I8 NULL
#define NULL_UUID NULL
#define NULL_DATA NULL
void mp_bluetooth_gap_on_connected_disconnected(uint8_t event, uint16_t conn_handle, uint8_t addr_type, const uint8_t *addr) {
invoke_irq_handler(event, &conn_handle, 1, &addr_type, 1, addr, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
}
void mp_bluetooth_gatts_on_write(uint16_t conn_handle, uint16_t value_handle) {
uint16_t args[] = {conn_handle, value_handle};
invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTS_WRITE, args, 2, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
}
void mp_bluetooth_gatts_on_indicate_complete(uint16_t conn_handle, uint16_t value_handle, uint8_t status) {
uint16_t args[] = {conn_handle, value_handle};
invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE, args, 2, &status, 1, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
}
bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle) {
uint16_t args[] = {conn_handle, value_handle};
mp_obj_t result = invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTS_READ_REQUEST, args, 2, NULL, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
return result == mp_const_none || mp_obj_is_true(result);
}
void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) {
uint16_t args[] = {conn_handle, value};
invoke_irq_handler(MP_BLUETOOTH_IRQ_MTU_EXCHANGED, args, 2, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
}
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
void mp_bluetooth_gap_on_scan_complete(void) {
invoke_irq_handler(MP_BLUETOOTH_IRQ_SCAN_DONE, NULL_U16, 0, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
}
void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uint8_t adv_type, const int8_t rssi, const uint8_t *data, size_t data_len) {
int8_t args[] = {adv_type, rssi};
invoke_irq_handler(MP_BLUETOOTH_IRQ_SCAN_RESULT, NULL_U16, 0, &addr_type, 1, addr, args, 2, NULL_UUID, data, data_len);
}
void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) {
uint16_t args[] = {conn_handle, start_handle, end_handle};
invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT, args, 3, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, service_uuid, NULL_DATA, 0);
}
void mp_bluetooth_gattc_on_characteristic_result(uint16_t conn_handle, uint16_t def_handle, uint16_t value_handle, uint8_t properties, mp_obj_bluetooth_uuid_t *characteristic_uuid) {
uint16_t args[] = {conn_handle, def_handle, value_handle};
invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_RESULT, args, 3, &properties, 1, NULL_ADDR, NULL_I8, 0, characteristic_uuid, NULL_DATA, 0);
}
void mp_bluetooth_gattc_on_descriptor_result(uint16_t conn_handle, uint16_t handle, mp_obj_bluetooth_uuid_t *descriptor_uuid) {
uint16_t args[] = {conn_handle, handle};
invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_RESULT, args, 2, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, descriptor_uuid, NULL_DATA, 0);
}
void mp_bluetooth_gattc_on_discover_complete(uint8_t event, uint16_t conn_handle, uint16_t status) {
uint16_t args[] = {conn_handle, status};
invoke_irq_handler(event, args, 2, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
}
void mp_bluetooth_gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const uint8_t **data, uint16_t *data_len, size_t num) {
const uint8_t *combined_data;
uint16_t total_len;
if (num > 1) {
// Fragmented buffer, need to combine into a new heap-allocated buffer
// in order to pass to Python.
total_len = 0;
for (size_t i = 0; i < num; ++i) {
total_len += data_len[i];
}
uint8_t *buf = m_new(uint8_t, total_len);
uint8_t *p = buf;
for (size_t i = 0; i < num; ++i) {
memcpy(p, data[i], data_len[i]);
p += data_len[i];
}
combined_data = buf;
} else {
// Single buffer, use directly.
combined_data = *data;
total_len = *data_len;
}
uint16_t args[] = {conn_handle, value_handle};
invoke_irq_handler(event, args, 2, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, combined_data, total_len);
if (num > 1) {
m_del(uint8_t, (uint8_t *)combined_data, total_len);
}
}
void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle, uint16_t value_handle, uint16_t status) {
uint16_t args[] = {conn_handle, value_handle, status};
invoke_irq_handler(event, args, 3, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0);
}
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Callbacks are called in interrupt context (i.e. can't allocate), so we need to push the data
// into the ringbuf and schedule the callback via mp_sched_schedule.
@ -1169,36 +1325,12 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK
// This can only be enabled when the thread invoking this is a MicroPython thread.
// On ESP32, for example, this is not the case.
bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle) {
mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth));
if (o->irq_handler != mp_const_none) {
// When executing code within a handler we must lock the scheduler to
// prevent any scheduled callbacks from running, and lock the GC to
// prevent any memory allocations.
mp_sched_lock();
gc_lock();
// Use pre-allocated tuple distinct to the one used by the "soft" IRQs.
mp_obj_tuple_t *data = MP_OBJ_TO_PTR(o->irq_read_request_data_tuple);
data->items[0] = MP_OBJ_NEW_SMALL_INT(conn_handle);
data->items[1] = MP_OBJ_NEW_SMALL_INT(value_handle);
data->len = 2;
mp_obj_t irq_ret = mp_call_function_2_protected(o->irq_handler, MP_OBJ_NEW_SMALL_INT(MP_BLUETOOTH_IRQ_GATTS_READ_REQUEST), o->irq_read_request_data_tuple);
gc_unlock();
mp_sched_unlock();
// If the IRQ handler explicitly returned false, then deny the read. Otherwise if it returns None/True, allow it.
return irq_ret != MP_OBJ_NULL && (irq_ret == mp_const_none || mp_obj_is_true(irq_ret));
} else {
// No IRQ handler, allow the read.
return true;
}
(void)conn_handle;
(void)value_handle;
// This must be handled synchronously and therefore cannot implemented with the ringbuffer.
return true;
}
#endif
void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) {
MICROPY_PY_BLUETOOTH_ENTER
@ -1210,6 +1342,12 @@ void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) {
schedule_ringbuf(atomic_state);
}
#endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// ----------------------------------------------------------------------------
// GATTS DB
// ----------------------------------------------------------------------------
void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len) {
mp_map_elem_t *elem = mp_map_lookup(db, MP_OBJ_NEW_SMALL_INT(handle), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
mp_bluetooth_gatts_db_entry_t *entry = m_new(mp_bluetooth_gatts_db_entry_t, 1);

View File

@ -43,8 +43,10 @@
#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (0)
#endif
#ifndef MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK
#define MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK (0)
#ifndef MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// This can be enabled if the BLE stack runs entirely in scheduler context
// and therefore is able to call directly into the VM to run Python callbacks.
#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (0)
#endif
// This is used to protect the ringbuffer.
@ -261,10 +263,8 @@ void mp_bluetooth_gatts_on_write(uint16_t conn_handle, uint16_t value_handle);
// Call this when an acknowledgment is received for an indication.
void mp_bluetooth_gatts_on_indicate_complete(uint16_t conn_handle, uint16_t value_handle, uint8_t status);
#if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK
// Call this when a characteristic is read from. Return false to deny the read.
bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle);
#endif
// Call this when an MTU exchange completes.
void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value);

View File

@ -586,12 +586,12 @@ static int characteristic_access_cb(uint16_t conn_handle, uint16_t value_handle,
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
case BLE_GATT_ACCESS_OP_READ_DSC:
#if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK
// Allow Python code to override (by using gatts_write), or deny (by returning false) the read.
// Note this will be a no-op if the ringbuffer implementation is being used (i.e. the stack isn't
// run in the scheduler). The ringbuffer is not used on STM32 and Unix-H4 only.
if (!mp_bluetooth_gatts_on_read_request(conn_handle, value_handle)) {
return BLE_ATT_ERR_READ_NOT_PERMITTED;
}
#endif
entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle);
if (!entry) {

View File

@ -430,7 +430,6 @@ USBDEV_SRC_C += $(addprefix $(USBDEV_DIR)/,\
ifeq ($(MICROPY_PY_BLUETOOTH),1)
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=1
endif
ifeq ($(MICROPY_PY_NETWORK_CYW43),1)

View File

@ -156,8 +156,6 @@ endif
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1
# Runs in a thread, cannot make calls into the VM.
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=0
ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1)