@@ -39,6 +39,39 @@ STATIC const mp_obj_type_t characteristic_type;
39
39
40
40
STATIC volatile uint16_t active_connections [MP_BT_MAX_CONNECTED_DEVICES ];
41
41
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
+
42
75
typedef struct _mp_obj_bluetooth_t {
43
76
mp_obj_base_t base ;
44
77
} mp_obj_bluetooth_t ;
@@ -141,6 +174,59 @@ void mp_bt_disconnected(uint16_t conn_handle) {
141
174
}
142
175
}
143
176
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
+
144
230
// Call the registered callback for this characteristic, if one has been
145
231
// registered.
146
232
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
149
235
mp_bt_characteristic_callback_t * item = MP_STATE_PORT (bt_characteristic_callbacks );
150
236
while (item != NULL ) {
151
237
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 ;
158
263
}
159
264
item = item -> next ;
160
265
}
0 commit comments