8000 extmod/modbluetooth: Add support for characteristics. · andrewleech/micropython@346e5a7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 346e5a7

Browse files
aykevldpgeorge
authored andcommitted
extmod/modbluetooth: Add support for characteristics.
Add support for adding characteristics to services. They can be read and written on both the esp32 and the nrf. Events of any kind (notifications etc.) haven't been implemented yet.
1 parent 3558d22 commit 346e5a7

File tree

6 files changed

+400
-29
lines changed

6 files changed

+400
-29
lines changed

extmod/modbluetooth.c

Lines changed: 144 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
STATIC const mp_obj_type_t bluetooth_type;
3535
STATIC const mp_obj_type_t service_type;
36+
STATIC const mp_obj_type_t characteristic_type;
3637

3738
typedef struct _mp_obj_bluetooth_t {
3839
mp_obj_base_t base;
@@ -53,7 +54,7 @@ STATIC mp_obj_t bluetooth_handle_errno(int errno_) {
5354
}
5455

5556
// Parse string UUIDs, which are probably 128-bit UUIDs.
56-
void bluetooth_parse_uuid_str(mp_obj_t obj, uint8_t *uuid) {
57+
void mp_bt_parse_uuid_str(mp_obj_t obj, uint8_t *uuid) {
5758
GET_STR_DATA_LEN(obj, str_data, str_len);
5859
int uuid_i = 32;
5960
for (int i = 0; i < str_len; i++) {
@@ -87,7 +88,36 @@ void bluetooth_parse_uuid_str(mp_obj_t obj, uint8_t *uuid) {
8788
}
8889
}
8990

90-
STATIC mp_obj_t bluetooth_make_new() {
91+
// Format string UUID. Example output:
92+
// '6e400001-b5a3-f393-e0a9-e50e24dcca9e'
93+
mp_obj_t mp_bt_format_uuid_str(uint8_t *uuid) {
94+
char str[36];
95+
char *s = str;
96+
for (int i = 15; i >= 0; i--) {
97+
char nibble = uuid[i] >> 4;
98+
if (nibble >= 10) {
99+
nibble += 'a' - 10;
100+
} else {
101+
nibble += '0';
102+
}
103+
*(s++) = nibble;
104+
105+
nibble = uuid[i] & 0xf;
106+
if (nibble >= 10) {
107+
nibble += 'a' - 10;
108+
} else {
109+
nibble += '0';
110+
}
111+
*(s++) = nibble;
112+
113+
if (i == 12 || i == 10 || i == 8 || i == 6) {
114+
*(s++) = '-';
115+
}
116+
}
117+
return mp_obj_new_str(str, MP_ARRAY_SIZE(str));
118+
}
119+
120+
STATIC mp_obj_t bluetooth_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
91121
return MP_OBJ_FROM_PTR(&bluetooth_obj);
92122
}
93123

@@ -204,32 +234,130 @@ STATIC mp_obj_t bluetooth_advertise_raw(size_t n_args, const mp_obj_t *pos_args,
204234
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bluetooth_advertise_raw_obj, 1, bluetooth_advertise_raw);
205235

206236
STATIC mp_obj_t bluetooth_add_service(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
207-
enum { ARG_interval, ARG_adv_data, ARG_sr_data, ARG_connectable };
237+
enum { ARG_uuid, ARG_characteristics };
208238
static const mp_arg_t allowed_args[] = {
209-
{ MP_QSTR_uuid, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = mp_const_none } },
239+
{ MP_QSTR_uuid, MP_ARG_OBJ | MP_ARG_REQUIRED },
240+
{ MP_QSTR_characteristics, MP_ARG_OBJ | MP_ARG_REQUIRED },
210241
};
211242
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
212243
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
213244

245+
mp_obj_list_t *characteristics = args[ARG_characteristics].u_obj;
246+
if (characteristics == NULL || !mp_obj_is_type(args[ARG_characteristics].u_obj, &mp_type_list)) {
247+
mp_raise_ValueError("characteristics must be a list");
248+
}
249+
for (int i = 0; i < characteristics->len; i++) {
250+
mp_obj_t characteristic = characteristics->items[i];
251+
if (characteristic == NULL || !mp_obj_is_type(characteristic, &characteristic_type)) {
252+
mp_raise_ValueError("not a Characteristic");
253+
}
254+
if (((mp_bt_characteristic_t*)characteristic)->service != NULL) {
255+
mp_raise_ValueError("Characteristic already added to Service");
256+
}
257+
}
258+
214259
mp_bt_service_t *service = m_new_obj(mp_bt_service_t);
215260
service->base.type = &service_type;
216-
bluetooth_parse_uuid(args[0].u_obj, &service->uuid);
217-
int errno_ = mp_bt_add_service(service);
261+
mp_bt_parse_uuid(args[ARG_uuid].u_obj, &service->uuid);
262+
int errno_ = mp_bt_add_service(service, characteristics->len, (mp_bt_characteristic_t**)characteristics->items);
218263
bluetooth_handle_errno(errno_);
219264
return service;
220265
}
221266
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bluetooth_add_service_obj, 1, bluetooth_add_service);
222267

268+
STATIC mp_obj_t service_uuid(mp_obj_t self_in) {
269+
mp_bt_service_t *service = self_in;
270+
return mp_bt_format_uuid(&service->uuid);
271+
}
272+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(service_uuid_obj, service_uuid);
273+
274+
STATIC const mp_rom_map_elem_t service_locals_dict_table[] = {
275+
{ MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&service_uuid_obj) },
276+
};
277+
STATIC MP_DEFINE_CONST_DICT(service_locals_dict, service_locals_dict_table);
278+
223279
STATIC const mp_obj_type_t service_type = {
224280
{ &mp_type_type },
225281
.name = MP_QSTR_Service,
282+
.locals_dict = (void*)&service_locals_dict,
283+
};
284+
285+
STATIC mp_obj_t characteristic_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
286+
enum { ARG_uuid, ARG_flags };
287+
static const mp_arg_t allowed_args[] = {
288+
{ MP_QSTR_uuid, MP_ARG_OBJ | MP_ARG_REQUIRED },
289+
{ MP_QSTR_flags, MP_ARG_INT | MP_ARG_REQUIRED },
290+
};
291+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
292+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
293+
294+
if ((uint8_t)(args[ARG_flags].u_int) != args[ARG_flags].u_int) {
295+
// Flags don't fit in 8 bits.
296+
mp_raise_ValueError("invalid flags");
297+
}
298+
299+
mp_bt_characteristic_t *characteristic = m_new_obj(mp_bt_characteristic_t);
300+
characteristic->base.type = &characteristic_type;
301+
mp_bt_parse_uuid(args[0].u_obj, &characteristic->uuid);
302+
characteristic->flags = (uint8_t)(args[ARG_flags].u_int);
303+
return characteristic;
304+
}
305+
306+
STATIC mp_obj_t characteristic_service(mp_obj_t self_in) {
307+
mp_bt_characteristic_t *characteristic = self_in;
308+
if (characteristic->service == NULL) {
309+
return mp_const_none;
310+
}
311+
return characteristic->service;
312+
}
313+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(characteristic_service_obj, characteristic_service);
314+
315+
STATIC mp_obj_t characteristic_uuid(mp_obj_t self_in) {
316+
mp_bt_characteristic_t *characteristic = self_in;
317+
return mp_bt_format_uuid(&characteristic->uuid);
318+
}
319+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(characteristic_uuid_obj, characteristic_uuid);
320+
321+
STATIC mp_obj_t characteristic_write(mp_obj_t self_in, mp_obj_t value_in) {
322+
mp_bt_characteristic_t *characteristic = self_in;
323+
GET_STR_DATA_LEN(value_in, str_data, str_len);
324+
int errno_ = mp_bt_characteristic_value_set(characteristic->value_handle, str_data, str_len);
325+
return bluetooth_handle_errno(errno_);
326+
}
327+
STATIC MP_DEFINE_CONST_FUN_OBJ_2(characteristic_write_obj, characteristic_write);
328+
329+
STATIC mp_obj_t characteristic_read(mp_obj_t self_in) {
330+
mp_bt_characteristic_t *characteristic = self_in;
331+
uint8_t data[MP_BT_MAX_ATTR_SIZE];
332+
size_t value_len = MP_BT_MAX_ATTR_SIZE;
333+
int errno_ = mp_bt_characteristic_value_get(characteristic->value_handle, data, &value_len);
334+
if (errno_ != 0) {
335+
mp_raise_OSError(errno_);
336+
}
337+
return mp_obj_new_bytes(data, value_len);
338+
}
339+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(characteristic_read_obj, characteristic_read);
340+
341+
STATIC const mp_rom_map_elem_t characteristic_locals_dict_table[] = {
342+
{ MP_ROM_QSTR(MP_QSTR_service), MP_ROM_PTR(&characteristic_service_obj) },
343+
{ MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&characteristic_uuid_obj) },
344+
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&characteristic_write_obj) },
345+
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&characteristic_read_obj) },
346+
};
347+
STATIC MP_DEFINE_CONST_DICT(characteristic_locals_dict, characteristic_locals_dict_table);
348+
349+
STATIC const mp_obj_type_t characteristic_type = {
350+
{ &mp_type_type },
351+
.name = MP_QSTR_Characteristic,
352+
.make_new = characteristic_make_new,
353+
.locals_dict = (void*)&characteristic_locals_dict,
226354
};
227355

228356
STATIC const mp_rom_map_elem_t bluetooth_locals_dict_table[] = {
229-
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&bluetooth_active_obj) },
230-
{ MP_ROM_QSTR(MP_QSTR_advertise), MP_ROM_PTR(&bluetooth_advertise_obj) },
357+
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&bluetooth_active_obj) },
358+
{ MP_ROM_QSTR(MP_QSTR_advertise), MP_ROM_PTR(&bluetooth_advertise_obj) },
231359
{ MP_ROM_QSTR(MP_QSTR_advertise_raw), MP_ROM_PTR(&bluetooth_advertise_raw_obj) },
232-
{ MP_ROM_QSTR(MP_QSTR_add_service), MP_ROM_PTR(&bluetooth_add_service_obj) },
360+
{ MP_ROM_QSTR(MP_QSTR_add_service), MP_ROM_PTR(&bluetooth_add_service_obj) },
233361
};
234362
STATIC MP_DEFINE_CONST_DICT(bluetooth_locals_dict, bluetooth_locals_dict_table);
235363

@@ -241,9 +369,13 @@ STATIC const mp_obj_type_t bluetooth_type = {
241369
};
242370

243371
STATIC const mp_rom_map_elem_t mp_module_bluetooth_globals_table[] = {
244-
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bluetooth) },
245-
{ MP_ROM_QSTR(MP_QSTR_Bluetooth), MP_ROM_PTR(&bluetooth_type) },
246-
{ MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&service_type) },
372+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bluetooth) },
373+
{ MP_ROM_QSTR(MP_QSTR_Bluetooth), MP_ROM_PTR(&bluetooth_type) },
374+
{ MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&service_type) },
375+
{ MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&characteristic_type) },
376+
{ MP_ROM_QSTR(MP_QSTR_FLAG_READ), MP_ROM_INT(MP_BLE_FLAG_READ) },
377+
{ MP_ROM_QSTR(MP_QSTR_FLAG_WRITE), MP_ROM_INT(MP_BLE_FLAG_WRITE) },
378+
{ MP_ROM_QSTR(MP_QSTR_FLAG_NOTIFY), MP_ROM_INT(MP_BLE_FLAG_NOTIFY) },
247379
};
248380
STATIC MP_DEFINE_CONST_DICT(mp_module_bluetooth_globals, mp_module_bluetooth_globals_table);
249381

extmod/modbluetooth.h

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ typedef struct {
3636
mp_bt_service_handle_t handle;
3737
} mp_bt_service_t;
3838

39+
// A characteristic.
40+
// Object fits in 4 words (1 GC object), with 1 byte unused at the end.
41+
typedef struct {
42+
mp_obj_base_t base;
43+
mp_bt_uuid_t uuid;
44+
mp_bt_service_t *service;
45+
mp_bt_characteristic_handle_t value_handle;
46+
uint8_t flags;
47+
} mp_bt_characteristic_t;
48+
3949
// Enables the Bluetooth stack. Returns errno on failure.
4050
int mp_bt_enable(void);
4151

@@ -52,16 +62,31 @@ int mp_bt_advertise_start(mp_bt_adv_type_t type, uint16_t interval, const uint8_
5262
// Stop advertisement. No-op when already stopped.
5363
void mp_bt_advertise_stop(void);
5464

55-
int mp_bt_add_service(mp_bt_service_t *service);
65+
// Add a service with the given list of characteristics.
66+
int mp_bt_add_service(mp_bt_service_t *service, size_t num_characteristics, mp_bt_characteristic_t **characteristics);
67+
68+
// Set the given characteristic to the given value.
69+
int mp_bt_characteristic_value_set(mp_bt_characteristic_handle_t handle, const void *value, size_t value_len);
70+
71+
// Read the characteristic value. The size of the buffer must be given in
72+
// value_len, which will be updated with the actual value.
73+
int mp_bt_characteristic_value_get(mp_bt_characteristic_handle_t handle, void *value, size_t *value_len);
5674

5775
// Parse an UUID object from the caller and stores the result in the uuid
5876
// parameter. Must accept both strings and integers for 128-bit and 16-bit
5977
// UUIDs.
60-
void bluetooth_parse_uuid(mp_obj_t obj, mp_bt_uuid_t *uuid);
78+
void mp_bt_parse_uuid(mp_obj_t obj, mp_bt_uuid_t *uuid);
79+
80+
// Format an UUID object to be returned from a .uuid() call. May result in
81+
// a small int or a string.
82+
mp_obj_t mp_bt_format_uuid(mp_bt_uuid_t *uuid);
6183

6284
// Parse a string UUID object into the 16-byte buffer. The string must be
6385
// the correct size, otherwise this function will throw an error.
64-
void bluetooth_parse_uuid_str(mp_obj_t obj, uint8_t *uuid);
86+
void mp_bt_parse_uuid_str(mp_obj_t obj, uint8_t *uuid);
87+
88+
// Format a 128-bit UUID from the 16-byte buffer as a string.
89+
mp_obj_t mp_bt_format_uuid_str(uint8_t *uuid);
6590

6691
// Data types of advertisement packet.
6792
#define MP_BLE_GAP_AD_TYPE_FLAG (0x01)
@@ -71,3 +96,7 @@ void bluetooth_parse_uuid_str(mp_obj_t obj, uint8_t *uuid);
7196
#define MP_BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE (0x02) // discoverable for everyone
7297
#define MP_BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED (0x04) // BLE only - no classic BT supported
7398
#define MP_BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE (MP_BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | MP_BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED)
99+
100+
#define MP_BLE_FLAG_READ (1 << 1)
101+
#define MP_BLE_FLAG_WRITE (1 << 3)
102+
#define MP_BLE_FLAG_NOTIFY (1 << 4)

0 commit comments

Comments
 (0)
0