Skip to content

Commit ca3cba5

Browse files
committed
bleio: adapter: add advertising timeout and status
Add a field to allow specifying a timeout when initiating advertising. As part of this, add a new property to determine if the device is still advertising. Additionally, have the `anonymous` property require a timeout, and set the timeout to the maximum possible value if no timeout is specified. Signed-off-by: Sean Cross <[email protected]>
1 parent 971b541 commit ca3cba5

File tree

4 files changed

+87
-12
lines changed

4 files changed

+87
-12
lines changed

ports/nrf/common-hal/_bleio/Adapter.c

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* THE SOFTWARE.
2727
*/
2828

29+
#include <math.h>
2930
#include <stdint.h>
3031
#include <stdio.h>
3132
#include <string.h>
@@ -594,7 +595,26 @@ STATIC void check_data_fit(size_t data_len, bool connectable) {
594595
}
595596
}
596597

597-
uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len) {
598+
STATIC bool advertising_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
599+
bleio_adapter_obj_t *self = (bleio_adapter_obj_t*)self_in;
600+
601+
switch (ble_evt->header.evt_id) {
602+
case BLE_GAP_EVT_ADV_SET_TERMINATED:
603+
mp_printf(&mp_plat_print, "Advertising set terminated - %d advertising events were completed - reason: %d\n", ble_evt->evt.gap_evt.params.adv_set_terminated.num_completed_adv_events, ble_evt->evt.gap_evt.params.adv_set_terminated.reason);
604+
common_hal_bleio_adapter_stop_advertising(self);
605+
ble_drv_remove_event_handler(advertising_on_ble_evt, self_in);
606+
break;
607+
608+
default:
609+
// For debugging.
610+
mp_printf(&mp_plat_print, "Unhandled Advertising etvent: 0x%04x\n", ble_evt->header.evt_id);
611+
return false;
612+
break;
613+
}
614+
return true;
615+
}
616+
617+
uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, uint32_t timeout, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len) {
598618
if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) {
599619
return NRF_ERROR_BUSY;
600620
}
@@ -630,7 +650,11 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self,
630650
ble_gap_privacy_params_t privacy = {
631651
.privacy_mode = BLE_GAP_PRIVACY_MODE_DEVICE_PRIVACY,
632652
.private_addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE,
633-
.private_addr_cycle_s = 0,
653+
// Rotate the keys one second after we're scheduled to stop
654+
// advertising. This prevents a potential race condition where we
655+
// fire off a beacon with the same advertising data but a new MAC
656+
// address just as we tear down the connection.
657+
.private_addr_cycle_s = timeout + 1,
634658
.p_device_irk = NULL,
635659
};
636660
err_code = sd_ble_gap_privacy_set(&privacy);
@@ -650,7 +674,7 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self,
650674
ble_gap_adv_params_t adv_params = {
651675
.interval = SEC_TO_UNITS(interval, UNIT_0_625_MS),
652676
.properties.type = adv_type,
653-
.duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED,
677+
.duration = SEC_TO_UNITS(timeout, UNIT_10_MS),
654678
.filter_policy = BLE_GAP_ADV_FP_ANY,
655679
.primary_phy = BLE_GAP_PHY_1MBPS,
656680
};
@@ -667,6 +691,8 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self,
667691
return err_code;
668692
}
669693

694+
ble_drv_add_event_handler(advertising_on_ble_evt, self);
695+
670696
vm_used_ble = true;
671697
err_code = sd_ble_gap_adv_start(adv_handle, BLE_CONN_CFG_TAG_CUSTOM);
672698
if (err_code != NRF_SUCCESS) {
@@ -677,7 +703,7 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self,
677703
}
678704

679705

680-
void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo) {
706+
void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo) {
681707
if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) {
682708
mp_raise_bleio_BluetoothError(translate("Already advertising."));
683709
}
@@ -689,6 +715,27 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool
689715
if (advertising_data_bufinfo->len > 31 && scan_response_data_bufinfo->len > 0) {
690716
mp_raise_bleio_BluetoothError(translate("Extended advertisements with scan response not supported."));
691717
}
718+
719+
// Anonymous mode requires a timeout so that we don't continue to broadcast
720+
// the same data while cycling the MAC address -- otherwise, what's the
721+
// point of randomizing the MAC address?
722+
if (!timeout) {
723+
if (anonymous) {
724+
// The Nordic macro is in units of 10ms. Convert to seconds.
725+
uint32_t adv_timeout_max_secs = BLE_GAP_ADV_TIMEOUT_LIMITED_MAX / 100;
726+
uint32_t rotate_timeout_max_secs = BLE_GAP_DEFAULT_PRIVATE_ADDR_CYCLE_INTERVAL_S;
727+
timeout = MIN(adv_timeout_max_secs, rotate_timeout_max_secs);
728+
}
729+
else {
730+
timeout = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED;
731+
}
732+
} else {
733+
if (SEC_TO_UNITS(timeout, UNIT_10_MS) > BLE_GAP_ADV_TIMEOUT_LIMITED_MAX) {
734+
mp_raise_bleio_BluetoothError(translate("Timeout is too long: Maximum timeout length is %d seconds"),
735+
BLE_GAP_ADV_TIMEOUT_LIMITED_MAX / 100);
736+
}
737+
}
738+
692739
// The advertising data buffers must not move, because the SoftDevice depends on them.
693740
// So make them long-lived and reuse them onwards.
694741
if (self->advertising_data == NULL) {
@@ -701,7 +748,7 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool
701748
memcpy(self->advertising_data, advertising_data_bufinfo->buf, advertising_data_bufinfo->len);
702749
memcpy(self->scan_response_data, scan_response_data_bufinfo->buf, scan_response_data_bufinfo->len);
703750

704-
check_nrf_error(_common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, interval,
751+
check_nrf_error(_common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, timeout, interval,
705752
self->advertising_data,
706753
advertising_data_bufinfo->len,
707754
self->scan_response_data,
@@ -721,6 +768,10 @@ void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) {
721768
}
722769
}
723770

771+
bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) {
772+
return self->current_advertising_data != NULL;
773+
}
774+
724775
bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) {
725776
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
726777
bleio_connection_internal_t *connection = &bleio_connections[i];

shared-bindings/_bleio/Adapter.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,29 +135,34 @@ const mp_obj_property_t bleio_adapter_name_obj = {
135135
(mp_obj_t)&mp_const_none_obj },
136136
};
137137

138-
//| def start_advertising(self, data: buf, *, scan_response: buf = None, connectable: bool = True, interval: float = 0.1) -> Any:
138+
//| def start_advertising(self, data: buf, *, scan_response: buf = None, connectable: bool = True, anonymous: bool = False, timeout: int = 0, interval: float = 0.1) -> Any:
139139
//| """Starts advertising until `stop_advertising` is called or if connectable, another device
140140
//| connects to us.
141141
//|
142142
//| .. warning: If data is longer than 31 bytes, then this will automatically advertise as an
143143
//| extended advertisement that older BLE 4.x clients won't be able to scan for.
144144
//|
145+
//| .. note: If you set ``anonymous=True``, then a timeout must be specified. If no timeout is
146+
//| specified, then the maximum allowed timeout will be selected automatically.
147+
//|
145148
//| :param buf data: advertising data packet bytes
146149
//| :param buf scan_response: scan response data packet bytes. ``None`` if no scan response is needed.
147150
//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral.
148-
//| :param bool anonymous: If `True` then this device's MAC address is randomized regularly.
151+
//| :param bool anonymous: If `True` then this device's MAC address is randomized before advertising.
152+
//| :param int timeout: If set, we will only advertise for this many seconds.
149153
//| :param float interval: advertising interval, in seconds"""
150154
//| ...
151155
//|
152156
STATIC mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
153157
bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
154158

155-
enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_anonymous, ARG_interval };
159+
enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_anonymous, ARG_timeout, ARG_interval };
156160
static const mp_arg_t allowed_args[] = {
157161
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ },
158162
{ MP_QSTR_scan_response, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
159163
{ MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
160164
{ MP_QSTR_anonymous, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
165+
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
161166
{ MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
162167
};
163168

@@ -185,11 +190,12 @@ STATIC mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t
185190

186191
bool connectable = args[ARG_connectable].u_bool;
187192
bool anonymous = args[ARG_anonymous].u_bool;
193+
uint32_t timeout = args[ARG_timeout].u_int;
188194
if (data_bufinfo.len > 31 && connectable && scan_response_bufinfo.len > 0) {
189195
mp_raise_bleio_BluetoothError(translate("Cannot have scan responses for extended, connectable advertisements."));
190196
}
191197

192-
common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, interval,
198+
common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, timeout, interval,
193199
&data_bufinfo, &scan_response_bufinfo);
194200

195201
return mp_const_none;
@@ -295,6 +301,22 @@ STATIC mp_obj_t bleio_adapter_stop_scan(mp_obj_t self_in) {
295301
}
296302
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_stop_scan_obj, bleio_adapter_stop_scan);
297303

304+
//| advertising: Any = ...
305+
//| """True when the adapter is currently advertising. (read-only)"""
306+
//|
307+
STATIC mp_obj_t bleio_adapter_get_advertising(mp_obj_t self) {
308+
return mp_obj_new_bool(common_hal_bleio_adapter_get_advertising(self));
309+
310+
}
311+
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_advertising_obj, bleio_adapter_get_advertising);
312+
313+
const mp_obj_property_t bleio_adapter_advertising_obj = {
314+
.base.type = &mp_type_property,
315+
.proxy = { (mp_obj_t)&bleio_adapter_get_advertising_obj,
316+
(mp_obj_t)&mp_const_none_obj,
317+
(mp_obj_t)&mp_const_none_obj },
318+
};
319+
298320
//| connected: Any = ...
299321
//| """True when the adapter is connected to another device regardless of who initiated the
300322
//| connection. (read-only)"""
@@ -378,6 +400,7 @@ STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = {
378400

379401
{ MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_adapter_start_advertising_obj) },
380402
{ MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_adapter_stop_advertising_obj) },
403+
{ MP_ROM_QSTR(MP_QSTR_advertising), MP_ROM_PTR(&bleio_adapter_advertising_obj) },
381404

382405
{ MP_ROM_QSTR(MP_QSTR_start_scan), MP_ROM_PTR(&bleio_adapter_start_scan_obj) },
383406
{ MP_ROM_QSTR(MP_QSTR_stop_scan), MP_ROM_PTR(&bleio_adapter_stop_scan_obj) },

shared-bindings/_bleio/Adapter.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
const mp_obj_type_t bleio_adapter_type;
3939

40+
extern bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self);
4041
extern bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self);
4142
extern void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled);
4243
extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self);
@@ -45,9 +46,9 @@ extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_o
4546
extern mp_obj_str_t* common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self);
4647
extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char* name);
4748

48-
extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len);
49+
extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, uint32_t timeout, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len);
4950

50-
extern void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
51+
extern void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
5152
extern void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self);
5253

5354
extern mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t* prefixes, size_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active);

supervisor/shared/bluetooth.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ void supervisor_bluetooth_start_advertising(void) {
7474
// TODO: switch to Adafruit short UUID for the advertisement and add manufacturing data to distinguish ourselves from arduino.
7575
_common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj,
7676
true,
77-
false,
77+
false, 0,
7878
1.0,
7979
circuitpython_advertising_data,
8080
sizeof(circuitpython_advertising_data),

0 commit comments

Comments
 (0)