Skip to content

Commit 3b6c911

Browse files
committed
extmod/modbluetooth: Add support for changing the GAP device name.
This commit allows the user to set/get the GAP device name used by service 0x1800, characteristic 0x2a00. The usage is: BLE.config(gap_name="myname") print(BLE.config("gap_name")) As part of this change the compile-time setting MICROPY_PY_BLUETOOTH_DEFAULT_NAME is renamed to MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME to emphasise its link to GAP and this new "gap_name" config value. And the default value of this for the NimBLE bindings is changed from "PYBD" to "MPY NIMBLE" to be more generic.
1 parent f385b7b commit 3b6c911

File tree

8 files changed

+219
-5
lines changed

8 files changed

+219
-5
lines changed

docs/library/ubluetooth.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ Configuration
5151
Note: on some ports, accessing this value requires that the interface is
5252
active (so that the MAC address can be queried from the controller).
5353

54+
- ``'gap_name'``: Get/set the GAP device name used by service 0x1800,
55+
characteristic 0x2a00. This can be set at any time and changed multiple
56+
times.
57+
5458
- ``'rxbuf'``: Get/set the size in bytes of the internal buffer used to store
5559
incoming events. This buffer is global to the entire BLE driver and so
5660
handles incoming data for all events, including all characteristics.

extmod/btstack/modbluetooth_btstack.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,20 @@
3737

3838
#define DEBUG_EVENT_printf(...) //printf(__VA_ARGS__)
3939

40+
#ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME
41+
#define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY BTSTACK"
42+
#endif
43+
4044
// How long to wait for a controller to init/deinit.
4145
// Some controllers can take up to 5-6 seconds in normal operation.
4246
STATIC const uint32_t BTSTACK_INIT_DEINIT_TIMEOUT_MS = 15000;
4347

48+
// We need to know the attribute handle for the GAP device name (see GAP_DEVICE_NAME_UUID)
49+
// so it can be put into the gatts_db before registering the services, and accessed
50+
// efficiently when requesting an attribute in att_read_callback. Because this is the
51+
// first characteristic of the first service, it always has a handle value of 3.
52+
STATIC const uint16_t BTSTACK_GAP_DEVICE_NAME_HANDLE = 3;
53+
4454
volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF;
4555

4656
STATIC btstack_packet_callback_registration_t hci_event_callback_registration;
@@ -250,6 +260,12 @@ int mp_bluetooth_init(void) {
250260
MP_STATE_PORT(bluetooth_btstack_root_pointers) = m_new0(mp_bluetooth_btstack_root_pointers_t, 1);
251261
mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db);
252262

263+
// Set the default GAP device name.
264+
const char *gap_name = MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME;
265+
size_t gap_len = strlen(gap_name);
266+
mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, gap_len);
267+
mp_bluetooth_gap_set_device_name((const uint8_t *)gap_name, gap_len);
268+
253269
mp_bluetooth_btstack_port_init();
254270
mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_STARTING;
255271

@@ -344,6 +360,19 @@ void mp_bluetooth_get_device_addr(uint8_t *addr) {
344360
mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr);
345361
}
346362

363+
size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) {
364+
uint8_t *value = NULL;
365+
size_t value_len = 0;
366+
mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, &value, &value_len);
367+
*buf = value;
368+
return value_len;
369+
}
370+
371+
int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) {
372+
mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, buf, len);
373+
return 0;
374+
}
375+
347376
int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) {
348377
DEBUG_EVENT_printf("mp_bluetooth_gap_advertise_start\n");
349378
uint16_t adv_int_min = interval_us / 625;
@@ -396,7 +425,9 @@ int mp_bluetooth_gatts_register_service_begin(bool append) {
396425
att_db_util_init();
397426

398427
att_db_util_add_service_uuid16(GAP_SERVICE_UUID);
399-
att_db_util_add_characteristic_uuid16(GAP_DEVICE_NAME_UUID, ATT_PROPERTY_READ, ATT_SECURITY_NONE, ATT_SECURITY_NONE, (uint8_t *)"MPY BTSTACK", 11);
428+
uint16_t handle = att_db_util_add_characteristic_uuid16(GAP_DEVICE_NAME_UUID, ATT_PROPERTY_READ | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0);
429+
assert(handle == BTSTACK_GAP_DEVICE_NAME_HANDLE);
430+
(void)handle;
400431

401432
att_db_util_add_service_uuid16(0x1801);
402433
att_db_util_add_characteristic_uuid16(0x2a05, ATT_PROPERTY_READ, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0);

extmod/modbluetooth.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
295295
}
296296

297297
switch (mp_obj_str_get_qstr(args[1])) {
298+
case MP_QSTR_gap_name: {
299+
const uint8_t *buf;
300+
size_t len = mp_bluetooth_gap_get_device_name(&buf);
301+
return mp_obj_new_bytes(buf, len);
302+
}
298303
case MP_QSTR_mac: {
299304
uint8_t addr[6];
300305
mp_bluetooth_get_device_addr(addr);
@@ -315,6 +320,13 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map
315320
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
316321
mp_map_elem_t *e = &kwargs->table[i];
317322
switch (mp_obj_str_get_qstr(e->key)) {
323+
case MP_QSTR_gap_name: {
324+
mp_buffer_info_t bufinfo;
325+
mp_get_buffer_raise(e->value, &bufinfo, MP_BUFFER_READ);
326+
int ret = mp_bluetooth_gap_set_device_name(bufinfo.buf, bufinfo.len);
327+
bluetooth_handle_errno(ret);
328+
break;
329+
}
318330
case MP_QSTR_rxbuf: {
319331
// Determine new buffer sizes
320332
mp_int_t ringbuf_alloc = mp_obj_get_int(e->value);

extmod/modbluetooth.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ bool mp_bluetooth_is_active(void);
171171
// Gets the MAC addr of this device in big-endian format.
172172
void mp_bluetooth_get_device_addr(uint8_t *addr);
173173

174+
// Get or set the GAP device name that will be used by service 0x1800, characteristic 0x2a00.
175+
size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf);
176+
int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len);
177+
174178
// Start advertisement. Will re-start advertisement when already enabled.
175179
// Returns errno on failure.
176180
int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len);

extmod/nimble/modbluetooth_nimble.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040
#include "nimble/nimble_port.h"
4141
#include "services/gap/ble_svc_gap.h"
4242

43-
#ifndef MICROPY_PY_BLUETOOTH_DEFAULT_NAME
44-
#define MICROPY_PY_BLUETOOTH_DEFAULT_NAME "PYBD"
43+
#ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME
44+
#define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY NIMBLE"
4545
#endif
4646

4747
#define DEBUG_EVENT_printf(...) //printf(__VA_ARGS__)
@@ -191,7 +191,7 @@ STATIC void sync_cb(void) {
191191
assert(rc == 0);
192192
}
193193

194-
ble_svc_gap_device_name_set(MICROPY_PY_BLUETOOTH_DEFAULT_NAME);
194+
ble_svc_gap_device_name_set(MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME);
195195

196196
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE;
197197
}
@@ -352,6 +352,22 @@ void mp_bluetooth_get_device_addr(uint8_t *addr) {
352352
#endif
353353
}
354354

355+
size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) {
356+
const char *name = ble_svc_gap_device_name();
357+
*buf = (const uint8_t *)name;
358+
return strlen(name);
359+
}
360+
361+
int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) {
362+
char tmp_buf[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH) + 1];
363+
if (len + 1 > sizeof(tmp_buf)) {
364+
return MP_EINVAL;
365+
}
366+
memcpy(tmp_buf, buf, len);
367+
tmp_buf[len] = '\0';
368+
return ble_hs_err_to_errno(ble_svc_gap_device_name_set(tmp_buf));
369+
}
370+
355371
int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) {
356372
if (!mp_bluetooth_is_active()) {
357373
return ERRNO_BLUETOOTH_NOT_ACTIVE;

ports/esp32/mpconfigport.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
#define MICROPY_PY_FRAMEBUF (1)
162162
#define MICROPY_PY_USOCKET_EVENTS (MICROPY_PY_WEBREPL)
163163
#define MICROPY_PY_BLUETOOTH_RANDOM_ADDR (1)
164-
#define MICROPY_PY_BLUETOOTH_DEFAULT_NAME ("ESP32")
164+
#define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME ("ESP32")
165165

166166
// fatfs configuration
167167
#define MICROPY_FATFS_ENABLE_LFN (1)
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Test BLE GAP device name get/set
2+
3+
from micropython import const
4+
import time, machine, bluetooth
5+
6+
TIMEOUT_MS = 5000
7+
8+
_IRQ_CENTRAL_CONNECT = const(1 << 0)
9+
_IRQ_CENTRAL_DISCONNECT = const(1 << 1)
10+
_IRQ_PERIPHERAL_CONNECT = const(1 << 6)
11+
_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7)
12+
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9)
13+
_IRQ_GATTC_READ_RESULT = const(1 << 11)
14+
15+
GAP_DEVICE_NAME_UUID = bluetooth.UUID(0x2A00)
16+
17+
last_event = None
18+
last_data = None
19+
value_handle = 0
20+
21+
22+
def irq(event, data):
23+
global last_event, last_data, value_handle
24+
last_event = event
25+
last_data = data
26+
if event == _IRQ_CENTRAL_CONNECT:
27+
print("_IRQ_CENTRAL_CONNECT")
28+
elif event == _IRQ_CENTRAL_DISCONNECT:
29+
print("_IRQ_CENTRAL_DISCONNECT")
30+
elif event == _IRQ_PERIPHERAL_CONNECT:
31+
print("_IRQ_PERIPHERAL_CONNECT")
32+
elif event == _IRQ_PERIPHERAL_DISCONNECT:
33+
print("_IRQ_PERIPHERAL_DISCONNECT")
34+
elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
35+
if data[-1] == GAP_DEVICE_NAME_UUID:
36+
print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1])
37+
value_handle = data[2]
38+
elif event == _IRQ_GATTC_READ_RESULT:
39+
print("_IRQ_GATTC_READ_RESULT", data[-1])
40+
41+
42+
def wait_for_event(event, timeout_ms):
43+
t0 = time.ticks_ms()
44+
while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms:
45+
if isinstance(event, int):
46+
if last_event == event:
47+
break
48+
elif event():
49+
break
50+
machine.idle()
51+
52+
53+
# Acting in peripheral role.
54+
def instance0():
55+
multitest.globals(BDADDR=ble.config("mac"))
56+
57+
# Test setting and getting the GAP device name before registering services.
58+
ble.config(gap_name="GAP_NAME")
59+
print(ble.config("gap_name"))
60+
61+
# Create an empty service and start advertising.
62+
ble.gatts_register_services([])
63+
print("gap_advertise")
64+
multitest.next()
65+
66+
try:
67+
# Do multiple iterations to test changing the name.
68+
for iteration in range(2):
69+
# Set the GAP device name and start advertising.
70+
ble.config(gap_name="GAP_NAME{}".format(iteration))
71+
print(ble.config("gap_name"))
72+
ble.gap_advertise(20_000)
73+
74+
# Wait for central to connect, then wait for it to disconnect.
75+
wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS)
76+
wait_for_event(_IRQ_CENTRAL_DISCONNECT, 4 * TIMEOUT_MS)
77+
if last_event != _IRQ_CENTRAL_DISCONNECT:
78+
return
79+
finally:
80+
ble.active(0)
81+
82+
83+
# Acting in central role.
84+
def instance1():
85+
multitest.next()
86+
try:
87+
for iteration in range(2):
88+
# Wait for peripheral to start advertising.
89+
time.sleep_ms(500)
90+
91+
# Connect to peripheral.
92+
print("gap_connect")
93+
ble.gap_connect(0, BDADDR)
94+
wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS)
95+
if last_event != _IRQ_PERIPHERAL_CONNECT:
96+
return
97+
conn_handle, _, _ = last_data
98+
99+
if iteration == 0:
100+
print("gattc_discover_characteristics")
101+
ble.gattc_discover_characteristics(conn_handle, 1, 65535)
102+
wait_for_event(lambda: value_handle, TIMEOUT_MS)
103+
104+
# Wait for all characteristic results to come in (there should be an event for it).
105+
time.sleep_ms(500)
106+
107+
# Read the peripheral's GAP device name.
108+
print("gattc_read")
109+
ble.gattc_read(conn_handle, value_handle)
110+
wait_for_event(_IRQ_GATTC_READ_RESULT, TIMEOUT_MS)
111+
112+
# Disconnect from peripheral.
113+
print("gap_disconnect:", ble.gap_disconnect(conn_handle))
114+
wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS)
115+
if last_event != _IRQ_PERIPHERAL_DISCONNECT:
116+
return
117+
finally:
118+
ble.active(0)
119+
120+
121+
ble = bluetooth.BLE()
122+
ble.active(1)
123+
ble.irq(irq)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--- instance0 ---
2+
b'GAP_NAME'
3+
gap_advertise
4+
b'GAP_NAME0'
5+
_IRQ_CENTRAL_CONNECT
6+
_IRQ_CENTRAL_DISCONNECT
7+
b'GAP_NAME1'
8+
_IRQ_CENTRAL_CONNECT
9+
_IRQ_CENTRAL_DISCONNECT
10+
--- instance1 ---
11+
gap_connect
12+
_IRQ_PERIPHERAL_CONNECT
13+
gattc_discover_characteristics
14+
_IRQ_GATTC_CHARACTERISTIC_RESULT UUID16(0x2a00)
15+
gattc_read
16+
_IRQ_GATTC_READ_RESULT b'GAP_NAME0'
17+
gap_disconnect: True
18+
_IRQ_PERIPHERAL_DISCONNECT
19+
gap_connect
20+
_IRQ_PERIPHERAL_CONNECT
21+
gattc_read
22+
_IRQ_GATTC_READ_RESULT b'GAP_NAME1'
23+
gap_disconnect: True
24+
_IRQ_PERIPHERAL_DISCONNECT

0 commit comments

Comments
 (0)