8000 extmod/modbluetooth: Run write callbacks on the scheduler. · andrewleech/micropython@47165a0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 47165a0

Browse files
aykevldpgeorge
authored andcommitted
extmod/modbluetooth: Run write callbacks on the scheduler.
Running write callbacks on the main thread using the scheduler has the advantage that you can do heap allocations. This is especially useful for the ESP32 which cannot easily run some code in a separate thread.
1 parent 727ed76 commit 47165a0

File tree

2 files changed

+114
-6
lines changed

2 files changed

+114
-6
lines changed

extmod/modbluetooth.c

Lines changed: 111 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,39 @@ STATIC const mp_obj_type_t characteristic_type;
3939

4040
STATIC volatile uint16_t active_connections[MP_BT_MAX_CONNECTED_DEVICES];
4141

42+
// This is a circular buffer of incoming packets.
43+
// Packets are length-prefixed chunks of data in the circular buffer. The
44+
// length is a single byte with the size of the packet excluding the
45+
// length byte itself.
46+
// The head/tail indices are like a normal ring buffer, except that they
47+
// do not wrap around at UPDATE_BUF_SIZE but instead continue to increase
48+
// in size. This means that the head/tail indices rely on unsigned
49+
// wraparound. That is, if there is no data in the buffer head equals
50+
// tail. If there is data in the queue, head is always ahead of tail
51+
// modulo 2**16.
52+
// If there is data in the buffer, this is the number of bytes currently
53+
// in the buffer:
54+
// head - tail
55+
// and this is the size of the first packet:
56+
// data[tail % UPDATE_BUF_SIZE]
57+
// Similarly, head always points to the first unused byte (or the same
58+
// byte if the buffer is exactly filled).
59+
//
60+
// Additionally, there is a counter of dropped packets. When packets are
61+
// dropped, it are always the oldest packets. So by incrementing the count
62+
// of dropped packets when the oldest packet is dropped, the next event
63+
// that is handled knows that its packet was dropped due to a buffer
64+
// overrun. This ensures that it is known exactly how many packets are
65+
// dropped and the buffer can keep on accepting new packets.
66+
//
67+
#define UPDATE_BUF_SIZE 32
68+
STATIC struct {
69+
volatile uint16_t head;
70+
volatile uint16_t tail;
71+
volatile uint16_t dropped_packets;
72+
volatile uin 10000 t8_t data[UPDATE_BUF_SIZE];
73+
} update_buf;
74+
4275
typedef struct _mp_obj_bluetooth_t {
4376
mp_obj_base_t base;
4477
} mp_obj_bluetooth_t;
@@ -141,6 +174,59 @@ void mp_bt_disconnected(uint16_t conn_handle) {
141174
}
142175
}
143176

177+
STATIC mp_obj_t bluetooth_write_callback(mp_obj_t char_in) {
178+
mp_bt_characteristic_t *characteristic = char_in;
179+
180+
// Copy the incoming buffer.
181+
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
182+
uint8_t value[20]; // maximum BLE packet size
183+
uint16_t tail = update_buf.tail;
184+
size_t value_len;
185+
if (update_buf.dropped_packets) {
186+
// Handle dropped packet.
187+
update_buf.dropped_packets--;
188+
value_len = (size_t)-1;
189+
} else {
190+
// Copy regular incoming packet.
191+
value_len = update_buf.data[tail++ % UPDATE_BUF_SIZE];
192+
if (value_len > sizeof(value)) {
193+
update_buf.tail += value_len + 1; // skip this packet
194+
mp_raise_ValueError("incoming BLE packet too big");
195+
}
196+
for (size_t i = 0; i < value_len; i++) {
197+
value[i] = update_buf.data[tail++ % UPDATE_BUF_SIZE];
198+
}
199+
update_buf.tail = tail;
200+
}
201+
MICROPY_END_ATOMIC_SECTION(atomic_state);
202+
203+
// Look for the callback in the linked list of callbacks.
204+
mp_bt_characteristic_callback_t *item = MP_STATE_PORT(bt_characteristic_callbacks);
205+
while (item != NULL && item->characteristic->value_handle != characteristic->value_handle) {
206+
item = item->next;
207+
}
208+
if (item == NULL) {
209+
// Callback has been removed?
210+
// This can happen when the callback is removed between the
211+
// interrupt and handling the interrupt.
212+
return mp_const_none;
213+
}
214+
215+
if (value_len == (size_t)-1) {
216+
// Unfortunately, there was a dropped packet.
217+
// Report this event by passing None.
218+
mp_call_function_2_protected(item->callback, MP_OBJ_FROM_PTR(item->characteristic), mp_const_none);
219+
} else {
220+
// Pass the written data directly as a bytearray to the callback.
221+
// WARNING: this array must not be modified by the callee.
222+
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, value_len, value};
223+
mp_call_function_2_protected(item->callback, MP_OBJ_FROM_PTR(item->characteristic), MP_OBJ_FROM_PTR(&ar));
224+
}
225+
226+
return mp_const_none;
227+
}
228+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_write_callback_obj, bluetooth_write_callback);
229+
144230
// Call the registered callback for this characteristic, if one has been
145231
// registered.
146232
void mp_bt_characteristic_on_write(uint16_t value_handle, const void *value, size_t value_len) {
@@ -149,12 +235,31 @@ void mp_bt_characteristic_on_write(uint16_t value_handle, const void *value, siz
149235
mp_bt_characteristic_callback_t *item = MP_STATE_PORT(bt_characteristic_callbacks);
150236
while (item != NULL) {
151237
if (item->characteristic->value_handle == value_handle) {
152-
// Pass the written data directly as a bytearray to the
153-
// callback.
154-
// WARNING: this array must not be modified by the callee.
155-
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, value_len, (void*)value};
156-
mp_call_function_2_protected(item->callback, MP_OBJ_FROM_PTR(item->characteristic), MP_OBJ_FROM_PTR(&ar));
157-
break;
238+
// Queue callback.
239+
if (!mp_sched_schedule(MP_OBJ_FROM_PTR(&bluetooth_write_callback_obj), item->characteristic)) {
240+
// Failed to schedule a callback: the queue is full.
241+
// There's not much we can do now.
242+
return;
243+
}
244+
245+
// Insert packet into queue.
246+
uint16_t head = update_buf.head;
247+
uint16_t tail = update_buf.tail;
248+
size_t bytes_left = ((uint16_t)UPDATE_BUF_SIZE - (head - tail));
249+
while (bytes_left < value_len + 1) {
250+
// Drop oldest packet.
251+
uint8_t packet_len = update_buf.data[tail % UPDATE_BUF_SIZE];
252+
tail += packet_len + 1;
253+
update_buf.tail = tail;
254+
bytes_left = ((uint16_t)UPDATE_BUF_SIZE - (head - tail));
255+
update_buf.dropped_packets++;
256+
}
257+
update_buf.data[head++ % UPDATE_BUF_SIZE] = (uint8_t)value_len;
258+
for (size_t i=0; i<value_len; i++) {
259+
update_buf.data[head++ % UPDATE_BUF_SIZE] = ((uint8_t*)value)[i];
260+
}
261+
update_buf.head = head;
262+
return;
158263
}
159264
item = item->next;
160265
}

ports/nrf/mpconfigport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
#define MICROPY_READER_VFS (MICROPY_VFS)
4444
#define MICROPY_ENABLE_GC (1)
4545
#define MICROPY_ENABLE_FINALISER (1)
46+
#if BLUETOOTH_SD
47+
#define MICROPY_ENABLE_SCHEDULER (1)
48+
#endif
4649
#define MICROPY_STACK_CHECK (1)
4750
#define MICROPY_HELPER_REPL (1)
4851
#define MICROPY_REPL_EMACS_KEYS (0)

0 commit comments

Comments
 (0)
0