summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/nrf/boards/make-pins.py208
-rw-r--r--ports/nrf/common-hal/bleio/Central.c335
-rw-r--r--ports/nrf/common-hal/bleio/Central.h4
-rw-r--r--ports/nrf/common-hal/bleio/Characteristic.c66
-rw-r--r--ports/nrf/common-hal/bleio/CharacteristicBuffer.c2
-rw-r--r--ports/nrf/common-hal/bleio/Peripheral.c15
-rw-r--r--ports/nrf/common-hal/bleio/Peripheral.h6
-rw-r--r--ports/nrf/common-hal/bleio/Service.c5
-rw-r--r--ports/nrf/common-hal/bleio/Service.h2
-rw-r--r--ports/nrf/common-hal/bleio/__init__.c365
-rw-r--r--ports/nrf/common-hal/bleio/__init__.h8
-rw-r--r--ports/nrf/nrfx_config.h1
-rw-r--r--ports/nrf/peripherals/nrf/timers.c2
-rw-r--r--shared-bindings/bleio/Central.c77
-rw-r--r--shared-bindings/bleio/Central.h2
-rw-r--r--shared-bindings/bleio/Peripheral.c72
-rw-r--r--shared-bindings/bleio/Peripheral.h2
-rw-r--r--shared-bindings/bleio/Service.c22
-rw-r--r--shared-bindings/bleio/Service.h1
-rw-r--r--shared-bindings/bleio/__init__.h11
-rw-r--r--shared-module/bleio/__init__.h6
21 files changed, 564 insertions, 648 deletions
diff --git a/ports/nrf/boards/make-pins.py b/ports/nrf/boards/make-pins.py
deleted file mode 100644
index d1cfd5b70..000000000
--- a/ports/nrf/boards/make-pins.py
+++ /dev/null
@@ -1,208 +0,0 @@
-#!/usr/bin/env python
-"""Creates the pin file for the nRF5."""
-
-from __future__ import print_function
-
-import argparse
-import sys
-import csv
-
-
-def parse_port_pin(name_str):
- """Parses a string and returns a (port-num, pin-num) tuple."""
- if len(name_str) < 4:
- raise ValueError("Expecting pin name to be at least 5 charcters.")
- if name_str[0] != 'P':
- raise ValueError("Expecting pin name to start with P")
- if name_str[1] not in ('0', '1'):
- raise ValueError("Expecting pin port to be in 0 or 1")
- port = ord(name_str[1]) - ord('0')
- pin_str = name_str[3:]
- if not pin_str.isdigit():
- raise ValueError("Expecting numeric pin number.")
- return (port, int(pin_str))
-
-
-class Pin(object):
- """Holds the information associated with a pin."""
-
- def __init__(self, port, pin):
- self.port = port
- self.pin = pin
- self.adc_channel = '0'
- self.board_pin = False
-
- def cpu_pin_name(self):
- return 'P{:d}_{:02d}'.format(self.port, self.pin)
-
- def is_board_pin(self):
- return self.board_pin
-
- def set_is_board_pin(self):
- self.board_pin = True
-
- def parse_adc(self, adc_str):
- if (adc_str[:3] != 'AIN'):
- return
- self.adc_channel = 'SAADC_CH_PSELP_PSELP_AnalogInput%d' % int(adc_str[3])
-
- def print(self):
- print('const pin_obj_t pin_{:s} = PIN({:s}, {:d}, {:d}, {:s});'.format(
- self.cpu_pin_name(), self.cpu_pin_name(),
- self.port, self.pin, self.adc_channel))
-
- def print_header(self, hdr_file):
- hdr_file.write('extern const pin_obj_t pin_{:s};\n'.
- format(self.cpu_pin_name()))
-
-
-class NamedPin(object):
-
- def __init__(self, name, pin):
- self._name = name
- self._pin = pin
-
- def pin(self):
- return self._pin
-
- def name(self):
- return self._name
-
-
-class Pins(object):
-
- def __init__(self):
- self.cpu_pins = [] # list of NamedPin objects
- self.board_pins = [] # list of NamedPin objects
-
- def find_pin(self, port_num, pin_num):
- for named_pin in self.cpu_pins:
- pin = named_pin.pin()
- if pin.port == port_num and pin.pin == pin_num:
- return pin
-
- def parse_af_file(self, filename):
- with open(filename, 'r') as csvfile:
- rows = csv.reader(csvfile)
- for row in rows:
- try:
- (port_num, pin_num) = parse_port_pin(row[0])
- except:
- continue
- pin = Pin(port_num, pin_num)
- if len(row) > 1:
- pin.parse_adc(row[1])
- self.cpu_pins.append(NamedPin(pin.cpu_pin_name(), pin))
-
- def parse_board_file(self, filename):
- with open(filename, 'r') as csvfile:
- rows = csv.reader(csvfile)
- for row in rows:
- try:
- (port_num, pin_num) = parse_port_pin(row[1])
- except:
- continue
- pin = self.find_pin(port_num, pin_num)
- if pin:
- pin.set_is_board_pin()
- self.board_pins.append(NamedPin(row[0], pin))
-
- def print_named(self, label, named_pins):
- print('')
- print('STATIC const mp_rom_map_elem_t {:s}_table[] = {{'.format(label))
- for named_pin in named_pins:
- pin = named_pin.pin()
- if pin.is_board_pin():
- print(' {{ MP_ROM_QSTR(MP_QSTR_{:s}), MP_ROM_PTR(&pin_{:s}) }},'.format(named_pin.name(), pin.cpu_pin_name()))
- print('};')
- print('MP_DEFINE_CONST_DICT({:s}, {:s}_table);'.format(label, label))
-
- def print(self):
- for named_pin in self.cpu_pins:
- pin = named_pin.pin()
- if pin.is_board_pin():
- pin.print()
- self.print_named('mcu_pin_globals', self.cpu_pins)
- self.print_named('board_module_globals', self.board_pins)
-
- def print_header(self, hdr_filename):
- with open(hdr_filename, 'wt') as hdr_file:
- for named_pin in self.cpu_pins:
- pin = named_pin.pin()
- if pin.is_board_pin():
- pin.print_header(hdr_file)
-
- def print_qstr(self, qstr_filename):
- with open(qstr_filename, 'wt') as qstr_file:
- qstr_set = set([])
- for named_pin in self.cpu_pins:
- pin = named_pin.pin()
- if pin.is_board_pin():
- qstr_set |= set([named_pin.name()])
- for named_pin in self.board_pins:
- qstr_set |= set([named_pin.name()])
- for qstr in sorted(qstr_set):
- print('Q({})'.format(qstr), file=qstr_file)
-
-
-def main():
- parser = argparse.ArgumentParser(
- prog="make-pins.py",
- usage="%(prog)s [options] [command]",
- description="Generate board specific pin file"
- )
- parser.add_argument(
- "-a", "--af",
- dest="af_filename",
- help="Specifies the alternate function file for the chip",
- default="nrf_af.csv"
- )
- parser.add_argument(
- "-b", "--board",
- dest="board_filename",
- help="Specifies the board file",
- )
- parser.add_argument(
- "-p", "--prefix",
- dest="prefix_filename",
- help="Specifies beginning portion of generated pins file",
- default="nrf52_prefix.c"
- )
- parser.add_argument(
- "-q", "--qstr",
- dest="qstr_filename",
- help="Specifies name of generated qstr header file",
- default="build/pins_qstr.h"
- )
- parser.add_argument(
- "-r", "--hdr",
- dest="hdr_filename",
- help="Specifies name of generated pin header file",
- default="build/pins.h"
- )
- args = parser.parse_args(sys.argv[1:])
-
- pins = Pins()
-
- print('// This file was automatically generated by make-pins.py')
- print('//')
- if args.af_filename:
- print('// --af {:s}'.format(args.af_filename))
- pins.parse_af_file(args.af_filename)
-
- if args.board_filename:
- print('// --board {:s}'.format(args.board_filename))
- pins.parse_board_file(args.board_filename)
-
- if args.prefix_filename:
- print('// --prefix {:s}'.format(args.prefix_filename))
- print('')
- with open(args.prefix_filename, 'r') as prefix_file:
- print(prefix_file.read())
- pins.print()
- pins.print_header(args.hdr_filename)
- pins.print_qstr(args.qstr_filename)
-
-
-if __name__ == "__main__":
- main()
diff --git a/ports/nrf/common-hal/bleio/Central.c b/ports/nrf/common-hal/bleio/Central.c
index d90737411..c42a2084a 100644
--- a/ports/nrf/common-hal/bleio/Central.c
+++ b/ports/nrf/common-hal/bleio/Central.c
@@ -35,216 +35,8 @@
#include "py/objstr.h"
#include "py/runtime.h"
#include "shared-bindings/bleio/Adapter.h"
-#include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Central.h"
-#include "shared-bindings/bleio/Descriptor.h"
-#include "shared-bindings/bleio/Service.h"
-#include "shared-bindings/bleio/UUID.h"
-
-static bleio_service_obj_t *m_char_discovery_service;
-static bleio_characteristic_obj_t *m_desc_discovery_characteristic;
-
-static volatile bool m_discovery_in_process;
-static volatile bool m_discovery_successful;
-
-// service_uuid may be NULL, to discover all services.
-STATIC bool discover_next_services(bleio_central_obj_t *self, uint16_t start_handle, ble_uuid_t *service_uuid) {
- m_discovery_successful = false;
- m_discovery_in_process = true;
-
- uint32_t err_code = sd_ble_gattc_primary_services_discover(self->conn_handle, start_handle, service_uuid);
-
- if (err_code != NRF_SUCCESS) {
- mp_raise_OSError_msg(translate("Failed to discover services"));
- }
-
- // Wait for a discovery event.
- while (m_discovery_in_process) {
- MICROPY_VM_HOOK_LOOP;
- }
- return m_discovery_successful;
-}
-
-STATIC bool discover_next_characteristics(bleio_central_obj_t *self, bleio_service_obj_t *service, uint16_t start_handle) {
- m_char_discovery_service = service;
-
- ble_gattc_handle_range_t handle_range;
- handle_range.start_handle = start_handle;
- handle_range.end_handle = service->end_handle;
-
- m_discovery_successful = false;
- m_discovery_in_process = true;
-
- uint32_t err_code = sd_ble_gattc_characteristics_discover(self->conn_handle, &handle_range);
- if (err_code != NRF_SUCCESS) {
- return false;
- }
-
- // Wait for a discovery event.
- while (m_discovery_in_process) {
- MICROPY_VM_HOOK_LOOP;
- }
- return m_discovery_successful;
-}
-
-STATIC bool discover_next_descriptors(bleio_central_obj_t *self, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) {
- m_desc_discovery_characteristic = characteristic;
-
- ble_gattc_handle_range_t handle_range;
- handle_range.start_handle = start_handle;
- handle_range.end_handle = end_handle;
-
- m_discovery_successful = false;
- m_discovery_in_process = true;
-
- uint32_t err_code = sd_ble_gattc_descriptors_discover(self->conn_handle, &handle_range);
- if (err_code != NRF_SUCCESS) {
- return false;
- }
-
- // Wait for a discovery event.
- while (m_discovery_in_process) {
- MICROPY_VM_HOOK_LOOP;
- }
- return m_discovery_successful;
-}
-
-STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, bleio_central_obj_t *central) {
- for (size_t i = 0; i < response->count; ++i) {
- ble_gattc_service_t *gattc_service = &response->services[i];
-
- bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t);
- service->base.type = &bleio_service_type;
-
- // Initialize several fields at once.
- common_hal_bleio_service_construct(service, NULL, mp_obj_new_list(0, NULL), false);
-
- service->device = MP_OBJ_FROM_PTR(central);
- service->start_handle = gattc_service->handle_range.start_handle;
- service->end_handle = gattc_service->handle_range.end_handle;
- service->handle = gattc_service->handle_range.start_handle;
-
- if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
- // Known service UUID.
- bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t);
- uuid->base.type = &bleio_uuid_type;
- bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid);
- service->uuid = uuid;
- service->device = MP_OBJ_FROM_PTR(central);
- } else {
- // The discovery response contained a 128-bit UUID that has not yet been registered with the
- // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
- // For now, just set the UUID to NULL.
- service->uuid = NULL;
- }
-
- mp_obj_list_append(central->service_list, service);
- }
-
- if (response->count > 0) {
- m_discovery_successful = true;
- }
- m_discovery_in_process = false;
-}
-
-STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio_central_obj_t *central) {
- for (size_t i = 0; i < response->count; ++i) {
- ble_gattc_char_t *gattc_char = &response->chars[i];
-
- bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t);
- characteristic->base.type = &bleio_characteristic_type;
-
- characteristic->descriptor_list = mp_obj_new_list(0, NULL);
-
- bleio_uuid_obj_t *uuid = NULL;
-
- if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
- // Known characteristic UUID.
- uuid = m_new_obj(bleio_uuid_obj_t);
- uuid->base.type = &bleio_uuid_type;
- bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid);
- } else {
- // The discovery response contained a 128-bit UUID that has not yet been registered with the
- // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
- // For now, just leave the UUID as NULL.
- }
-
- bleio_characteristic_properties_t props;
-
- props.broadcast = gattc_char->char_props.broadcast;
- props.indicate = gattc_char->char_props.indicate;
- props.notify = gattc_char->char_props.notify;
- props.read = gattc_char->char_props.read;
- props.write = gattc_char->char_props.write;
- props.write_no_response = gattc_char->char_props.write_wo_resp;
-
- // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler.
- common_hal_bleio_characteristic_construct(characteristic, uuid, props, mp_obj_new_list(0, NULL));
- characteristic->handle = gattc_char->handle_value;
- characteristic->service = m_char_discovery_service;
-
- mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic));
- }
-
- if (response->count > 0) {
- m_discovery_successful = true;
- }
- m_discovery_in_process = false;
-}
-
-STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, bleio_central_obj_t *central) {
- for (size_t i = 0; i < response->count; ++i) {
- ble_gattc_desc_t *gattc_desc = &response->descs[i];
-
- // Remember handles for certain well-known descriptors.
- switch (gattc_desc->uuid.uuid) {
- case DESCRIPTOR_UUID_CLIENT_CHARACTERISTIC_CONFIGURATION:
- m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle;
- break;
-
- case DESCRIPTOR_UUID_SERVER_CHARACTERISTIC_CONFIGURATION:
- m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle;
- break;
-
- case DESCRIPTOR_UUID_CHARACTERISTIC_USER_DESCRIPTION:
- m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle;
- break;
-
- default:
- // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors,
- // so ignore those.
- // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors
- break;
- }
-
- bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t);
- descriptor->base.type = &bleio_descriptor_type;
-
- bleio_uuid_obj_t *uuid = NULL;
-
- if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
- // Known descriptor UUID.
- uuid = m_new_obj(bleio_uuid_obj_t);
- uuid->base.type = &bleio_uuid_type;
- bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid);
- } else {
- // The discovery response contained a 128-bit UUID that has not yet been registered with the
- // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
- // For now, just leave the UUID as NULL.
- }
-
- common_hal_bleio_descriptor_construct(descriptor, uuid);
- descriptor->handle = gattc_desc->handle;
- descriptor->characteristic = m_desc_discovery_characteristic;
-
- mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor));
- }
-
- if (response->count > 0) {
- m_discovery_successful = true;
- }
- m_discovery_in_process = false;
-}
+#include "common-hal/bleio/__init__.h"
STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) {
bleio_central_obj_t *central = (bleio_central_obj_t*)central_in;
@@ -262,20 +54,6 @@ STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) {
case BLE_GAP_EVT_DISCONNECTED:
central->conn_handle = BLE_CONN_HANDLE_INVALID;
- m_discovery_successful = false;
- m_discovery_in_process = false;
- break;
-
- case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
- on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, central);
- break;
-
- case BLE_GATTC_EVT_CHAR_DISC_RSP:
- on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, central);
- break;
-
- case BLE_GATTC_EVT_DESC_DISC_RSP:
- on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, central);
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
@@ -288,7 +66,6 @@ STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) {
sd_ble_gap_conn_param_update(central->conn_handle, &request->conn_params);
break;
}
-
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id);
@@ -299,12 +76,11 @@ STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) {
void common_hal_bleio_central_construct(bleio_central_obj_t *self) {
common_hal_bleio_adapter_set_enabled(true);
- self->service_list = mp_obj_new_list(0, NULL);
- self->gatt_role = GATT_ROLE_CLIENT;
+ self->remote_services_list = mp_obj_new_list(0, NULL);
self->conn_handle = BLE_CONN_HANDLE_INVALID;
}
-void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout, mp_obj_t service_uuids) {
+void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) {
common_hal_bleio_adapter_set_enabled(true);
ble_drv_add_event_handler(central_on_ble_evt, self);
@@ -345,109 +121,6 @@ void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_o
if (self->conn_handle == BLE_CONN_HANDLE_INVALID) {
mp_raise_OSError_msg(translate("Failed to connect: timeout"));
}
-
- // Connection successful.
- // Now discover services on the remote peripheral.
-
- if (service_uuids == mp_const_none) {
-
- // List of service UUID's not given, so discover all available services.
-
- uint16_t next_service_start_handle = BLE_GATT_HANDLE_START;
-
- while (discover_next_services(self, next_service_start_handle, MP_OBJ_NULL)) {
- // discover_next_services() appends to service_list.
-
- // Get the most recently discovered service, and then ask for services
- // whose handles start after the last attribute handle inside that service.
- const bleio_service_obj_t *service =
- MP_OBJ_TO_PTR(self->service_list->items[self->service_list->len - 1]);
- next_service_start_handle = service->end_handle + 1;
- }
- } else {
- mp_obj_iter_buf_t iter_buf;
- mp_obj_t iterable = mp_getiter(service_uuids, &iter_buf);
- mp_obj_t uuid_obj;
- while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
- if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) {
- mp_raise_ValueError(translate("non-UUID found in service_uuids"));
- }
- bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj);
-
- ble_uuid_t nrf_uuid;
- bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid);
-
- // Service might or might not be discovered; that's ok. Caller has to check
- // Central.remote_services to find out.
- // We only need to call this once for each service to discover.
- discover_next_services(self, BLE_GATT_HANDLE_START, &nrf_uuid);
- }
- }
-
-
- for (size_t service_idx = 0; service_idx < self->service_list->len; ++service_idx) {
- bleio_service_obj_t *service = MP_OBJ_TO_PTR(self->service_list->items[service_idx]);
-
- // Skip the service if it had an unknown (unregistered) UUID.
- if (service->uuid == NULL) {
- continue;
- }
-
- uint16_t next_char_start_handle = service->start_handle;
-
- // Stop when we go past the end of the range of handles for this service or
- // discovery call returns nothing.
- // discover_next_characteristics() appends to the characteristic_list.
- while (next_char_start_handle <= service->end_handle &&
- discover_next_characteristics(self, service, next_char_start_handle)) {
-
-
- // Get the most recently discovered characteristic, and then ask for characteristics
- // whose handles start after the last attribute handle inside that characteristic.
- const bleio_characteristic_obj_t *characteristic =
- MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]);
- next_char_start_handle = characteristic->handle + 1;
- }
-
- // Got characteristics for this service. Now discover descriptors for each characteristic.
- size_t char_list_len = service->characteristic_list->len;
- for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) {
- bleio_characteristic_obj_t *characteristic =
- MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]);
- const bool last_characteristic = char_idx == char_list_len - 1;
- bleio_characteristic_obj_t *next_characteristic = last_characteristic
- ? NULL
- : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]);
-
- // Skip the characteristic if it had an unknown (unregistered) UUID.
- if (characteristic->uuid == NULL) {
- continue;
- }
-
- uint16_t next_desc_start_handle = characteristic->handle + 1;
-
- // Don't run past the end of this service or the beginning of the next characteristic.
- uint16_t next_desc_end_handle = next_characteristic == NULL
- ? service->end_handle
- : next_characteristic->handle - 1;
-
- // Stop when we go past the end of the range of handles for this service or
- // discovery call returns nothing.
- // discover_next_descriptors() appends to the descriptor_list.
- while (next_desc_start_handle <= service->end_handle &&
- next_desc_start_handle < next_desc_end_handle &&
- discover_next_descriptors(self, characteristic,
- next_desc_start_handle, next_desc_end_handle)) {
-
- // Get the most recently discovered descriptor, and then ask for descriptors
- // whose handles start after that descriptor's handle.
- const bleio_descriptor_obj_t *descriptor =
- MP_OBJ_TO_PTR(characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1]);
- next_desc_start_handle = descriptor->handle + 1;
- }
- }
-
- }
}
void common_hal_bleio_central_disconnect(bleio_central_obj_t *self) {
@@ -459,5 +132,5 @@ bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self) {
}
mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self) {
- return self->service_list;
+ return self->remote_services_list;
}
diff --git a/ports/nrf/common-hal/bleio/Central.h b/ports/nrf/common-hal/bleio/Central.h
index ac1670088..8b0d811bd 100644
--- a/ports/nrf/common-hal/bleio/Central.h
+++ b/ports/nrf/common-hal/bleio/Central.h
@@ -36,10 +36,10 @@
typedef struct {
mp_obj_base_t base;
- gatt_role_t gatt_role;
volatile bool waiting_to_connect;
volatile uint16_t conn_handle;
- mp_obj_list_t *service_list;
+ // Services discovered after connecting to a remote peripheral.
+ mp_obj_list_t *remote_services_list;
} bleio_central_obj_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H
diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c
index 17417a5e6..e9f0225cf 100644
--- a/ports/nrf/common-hal/bleio/Characteristic.c
+++ b/ports/nrf/common-hal/bleio/Characteristic.c
@@ -33,8 +33,10 @@
#include "nrf_soc.h"
#include "py/runtime.h"
-#include "common-hal/bleio/__init__.h"
-#include "common-hal/bleio/Characteristic.h"
+
+#include "shared-bindings/bleio/__init__.h"
+#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/Service.h"
STATIC volatile bleio_characteristic_obj_t *m_read_characteristic;
@@ -225,53 +227,39 @@ mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_charact
}
mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) {
- switch (common_hal_bleio_device_get_gatt_role(self->service->device)) {
- case GATT_ROLE_CLIENT:
+ if (common_hal_bleio_service_get_is_remote(self->service)) {
gattc_read(self);
- break;
-
- case GATT_ROLE_SERVER:
+ } else {
gatts_read(self);
- break;
-
- default:
- mp_raise_RuntimeError(translate("bad GATT role"));
- break;
}
return self->value_data;
}
void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
- bool sent = false;
- uint16_t cccd = 0;
+ if (common_hal_bleio_service_get_is_remote(self->service)) {
+ gattc_write(self, bufinfo);
+ } else {
+ bool sent = false;
+ uint16_t cccd = 0;
- switch (common_hal_bleio_device_get_gatt_role(self->service->device)) {
- case GATT_ROLE_SERVER:
- if (self->props.notify || self->props.indicate) {
+ if (self->props.notify || self->props.indicate) {
cccd = get_cccd(self);
- }
- // It's possible that both notify and indicate are set.
- if (self->props.notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
- gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_NOTIFICATION);
- sent = true;
- }
- if (self->props.indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
- gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_INDICATION);
- sent = true;
- }
- if (!sent) {
- gatts_write(self, bufinfo);
- }
- break;
+ }
- case GATT_ROLE_CLIENT:
- gattc_write(self, bufinfo);
- break;
+ // It's possible that both notify and indicate are set.
+ if (self->props.notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
+ gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_NOTIFICATION);
+ sent = true;
+ }
+ if (self->props.indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
+ gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_INDICATION);
+ sent = true;
+ }
- default:
- mp_raise_RuntimeError(translate("bad GATT role"));
- break;
+ if (!sent) {
+ gatts_write(self, bufinfo);
+ }
}
}
@@ -289,8 +277,8 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self,
mp_raise_ValueError(translate("No CCCD for this Characteristic"));
}
- if (common_hal_bleio_device_get_gatt_role(self->service->device) != GATT_ROLE_CLIENT) {
- mp_raise_ValueError(translate("Can't set CCCD for local Characteristic"));
+ if (!common_hal_bleio_service_get_is_remote(self->service)) {
+ mp_raise_ValueError(translate("Can't set CCCD on local Characteristic"));
}
uint16_t cccd_value =
diff --git a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c
index 59eaaf02b..a6d834d7d 100644
--- a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c
+++ b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c
@@ -37,7 +37,7 @@
#include "tick.h"
-#include "common-hal/bleio/__init__.h"
+#include "shared-bindings/bleio/__init__.h"
#include "common-hal/bleio/CharacteristicBuffer.h"
STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) {
diff --git a/ports/nrf/common-hal/bleio/Peripheral.c b/ports/nrf/common-hal/bleio/Peripheral.c
index 608bd1342..930000534 100644
--- a/ports/nrf/common-hal/bleio/Peripheral.c
+++ b/ports/nrf/common-hal/bleio/Peripheral.c
@@ -120,20 +120,21 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
}
}
-void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *service_list, mp_obj_t name) {
+void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *services_list, mp_obj_t name) {
common_hal_bleio_adapter_set_enabled(true);
- self->service_list = service_list;
+ self->services_list = services_list;
+ // Used only for discovery when acting as a client.
+ self->remote_services_list = mp_obj_new_list(0, NULL);
self->name = name;
- self->gatt_role = GATT_ROLE_SERVER;
self->conn_handle = BLE_CONN_HANDLE_INVALID;
self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
// Add all the services.
- for (size_t service_idx = 0; service_idx < service_list->len; ++service_idx) {
- bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[service_idx]);
+ for (size_t service_idx = 0; service_idx < services_list->len; ++service_idx) {
+ bleio_service_obj_t *service = MP_OBJ_TO_PTR(services_list->items[service_idx]);
service->device = MP_OBJ_FROM_PTR(self);
@@ -156,8 +157,8 @@ void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_
}
-mp_obj_list_t *common_hal_bleio_peripheral_get_service_list(bleio_peripheral_obj_t *self) {
- return self->service_list;
+mp_obj_list_t *common_hal_bleio_peripheral_get_services_list(bleio_peripheral_obj_t *self) {
+ return self->services_list;
}
bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) {
diff --git a/ports/nrf/common-hal/bleio/Peripheral.h b/ports/nrf/common-hal/bleio/Peripheral.h
index 2a1251715..3127a527f 100644
--- a/ports/nrf/common-hal/bleio/Peripheral.h
+++ b/ports/nrf/common-hal/bleio/Peripheral.h
@@ -41,9 +41,11 @@
typedef struct {
mp_obj_base_t base;
mp_obj_t name;
- gatt_role_t gatt_role;
volatile uint16_t conn_handle;
- mp_obj_list_t *service_list;
+ // Services provided by this peripheral.
+ mp_obj_list_t *services_list;
+ // Remote services discovered when this peripheral is acting as a client.
+ mp_obj_list_t *remote_services_list;
// The advertising data and scan response buffers are held by us, not by the SD, so we must
// maintain them and not change it. If we need to change the contents during advertising,
// there are tricks to get the SD to notice (see DevZone - TBS).
diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c
index b6fcde827..02c2ac1f9 100644
--- a/ports/nrf/common-hal/bleio/Service.c
+++ b/ports/nrf/common-hal/bleio/Service.c
@@ -39,6 +39,7 @@ void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_ob
self->handle = 0xFFFF;
self->uuid = uuid;
self->characteristic_list = characteristic_list;
+ self->is_remote = false;
self->is_secondary = is_secondary;
for (size_t characteristic_idx = 0; characteristic_idx < characteristic_list->len; ++characteristic_idx) {
@@ -57,6 +58,10 @@ mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_ob
return self->characteristic_list;
}
+bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self) {
+ return self->is_remote;
+}
+
bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) {
return self->is_secondary;
}
diff --git a/ports/nrf/common-hal/bleio/Service.h b/ports/nrf/common-hal/bleio/Service.h
index 583593fb8..03ac2bca8 100644
--- a/ports/nrf/common-hal/bleio/Service.h
+++ b/ports/nrf/common-hal/bleio/Service.h
@@ -35,6 +35,8 @@ typedef struct {
mp_obj_base_t base;
// Handle for this service.
uint16_t handle;
+ // True if created during discovery.
+ bool is_remote;
bool is_secondary;
bleio_uuid_obj_t *uuid;
// May be a Peripheral, Central, etc.
diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c
index 2dba5780c..a24898b0a 100644
--- a/ports/nrf/common-hal/bleio/__init__.c
+++ b/ports/nrf/common-hal/bleio/__init__.c
@@ -26,12 +26,24 @@
* THE SOFTWARE.
*/
+#include "py/runtime.h"
#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Adapter.h"
#include "shared-bindings/bleio/Central.h"
+#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/Peripheral.h"
+#include "shared-bindings/bleio/Service.h"
+#include "shared-bindings/bleio/UUID.h"
+
#include "common-hal/bleio/__init__.h"
+static volatile bool m_discovery_in_process;
+static volatile bool m_discovery_successful;
+
+static bleio_service_obj_t *m_char_discovery_service;
+static bleio_characteristic_obj_t *m_desc_discovery_characteristic;
+
// Turn off BLE on a reset or reload.
void bleio_reset() {
if (common_hal_bleio_adapter_get_enabled()) {
@@ -47,22 +59,359 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = {
},
};
-gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device) {
+uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) {
if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) {
- return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role;
+ return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
} else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
- return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role;
+ return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
} else {
- return GATT_ROLE_NONE;
+ return 0;
}
}
-uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) {
+mp_obj_list_t *common_hal_bleio_device_get_remote_services_list(mp_obj_t device) {
if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) {
- return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
+ return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->remote_services_list;
} else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
- return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
+ return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->remote_services_list;
} else {
- return 0;
+ return NULL;
+ }
+}
+
+// service_uuid may be NULL, to discover all services.
+STATIC bool discover_next_services(mp_obj_t device, uint16_t start_handle, ble_uuid_t *service_uuid) {
+ m_discovery_successful = false;
+ m_discovery_in_process = true;
+
+ uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device);
+ uint32_t err_code = sd_ble_gattc_primary_services_discover(conn_handle, start_handle, service_uuid);
+
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to discover services"));
+ }
+
+ // Wait for a discovery event.
+ while (m_discovery_in_process) {
+ MICROPY_VM_HOOK_LOOP;
+ }
+ return m_discovery_successful;
+}
+
+STATIC bool discover_next_characteristics(mp_obj_t device, bleio_service_obj_t *service, uint16_t start_handle) {
+ m_char_discovery_service = service;
+
+ ble_gattc_handle_range_t handle_range;
+ handle_range.start_handle = start_handle;
+ handle_range.end_handle = service->end_handle;
+
+ m_discovery_successful = false;
+ m_discovery_in_process = true;
+
+ uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device);
+ uint32_t err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range);
+ if (err_code != NRF_SUCCESS) {
+ return false;
+ }
+
+ // Wait for a discovery event.
+ while (m_discovery_in_process) {
+ MICROPY_VM_HOOK_LOOP;
+ }
+ return m_discovery_successful;
+}
+
+STATIC bool discover_next_descriptors(mp_obj_t device, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) {
+ m_desc_discovery_characteristic = characteristic;
+
+ ble_gattc_handle_range_t handle_range;
+ handle_range.start_handle = start_handle;
+ handle_range.end_handle = end_handle;
+
+ m_discovery_successful = false;
+ m_discovery_in_process = true;
+
+ uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device);
+ uint32_t err_code = sd_ble_gattc_descriptors_discover(conn_handle, &handle_range);
+ if (err_code != NRF_SUCCESS) {
+ return false;
+ }
+
+ // Wait for a discovery event.
+ while (m_discovery_in_process) {
+ MICROPY_VM_HOOK_LOOP;
+ }
+ return m_discovery_successful;
+}
+
+STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, mp_obj_t device) {
+ for (size_t i = 0; i < response->count; ++i) {
+ ble_gattc_service_t *gattc_service = &response->services[i];
+
+ bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t);
+ service->base.type = &bleio_service_type;
+
+ // Initialize several fields at once.
+ common_hal_bleio_service_construct(service, NULL, mp_obj_new_list(0, NULL), false);
+
+ service->device = device;
+ service->is_remote = true;
+ service->start_handle = gattc_service->handle_range.start_handle;
+ service->end_handle = gattc_service->handle_range.end_handle;
+ service->handle = gattc_service->handle_range.start_handle;
+
+ if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
+ // Known service UUID.
+ bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t);
+ uuid->base.type = &bleio_uuid_type;
+ bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid);
+ service->uuid = uuid;
+ } else {
+ // The discovery response contained a 128-bit UUID that has not yet been registered with the
+ // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
+ // For now, just set the UUID to NULL.
+ service->uuid = NULL;
+ }
+
+ mp_obj_list_append(common_hal_bleio_device_get_remote_services_list(device), service);
+ }
+
+ if (response->count > 0) {
+ m_discovery_successful = true;
+ }
+ m_discovery_in_process = false;
+}
+
+STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, mp_obj_t device) {
+ for (size_t i = 0; i < response->count; ++i) {
+ ble_gattc_char_t *gattc_char = &response->chars[i];
+
+ bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t);
+ characteristic->base.type = &bleio_characteristic_type;
+
+ characteristic->descriptor_list = mp_obj_new_list(0, NULL);
+
+ bleio_uuid_obj_t *uuid = NULL;
+
+ if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
+ // Known characteristic UUID.
+ uuid = m_new_obj(bleio_uuid_obj_t);
+ uuid->base.type = &bleio_uuid_type;
+ bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid);
+ } else {
+ // The discovery response contained a 128-bit UUID that has not yet been registered with the
+ // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
+ // For now, just leave the UUID as NULL.
+ }
+
+ bleio_characteristic_properties_t props;
+
+ props.broadcast = gattc_char->char_props.broadcast;
+ props.indicate = gattc_char->char_props.indicate;
+ props.notify = gattc_char->char_props.notify;
+ props.read = gattc_char->char_props.read;
+ props.write = gattc_char->char_props.write;
+ props.write_no_response = gattc_char->char_props.write_wo_resp;
+
+ // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler.
+ common_hal_bleio_characteristic_construct(characteristic, uuid, props, mp_obj_new_list(0, NULL));
+ characteristic->handle = gattc_char->handle_value;
+ characteristic->service = m_char_discovery_service;
+
+ mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic));
+ }
+
+ if (response->count > 0) {
+ m_discovery_successful = true;
}
+ m_discovery_in_process = false;
+}
+
+STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, mp_obj_t device) {
+ for (size_t i = 0; i < response->count; ++i) {
+ ble_gattc_desc_t *gattc_desc = &response->descs[i];
+
+ // Remember handles for certain well-known descriptors.
+ switch (gattc_desc->uuid.uuid) {
+ case DESCRIPTOR_UUID_CLIENT_CHARACTERISTIC_CONFIGURATION:
+ m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle;
+ break;
+
+ case DESCRIPTOR_UUID_SERVER_CHARACTERISTIC_CONFIGURATION:
+ m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle;
+ break;
+
+ case DESCRIPTOR_UUID_CHARACTERISTIC_USER_DESCRIPTION:
+ m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle;
+ break;
+
+ default:
+ // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors,
+ // so ignore those.
+ // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors
+ break;
+ }
+
+ bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t);
+ descriptor->base.type = &bleio_descriptor_type;
+
+ bleio_uuid_obj_t *uuid = NULL;
+
+ if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
+ // Known descriptor UUID.
+ uuid = m_new_obj(bleio_uuid_obj_t);
+ uuid->base.type = &bleio_uuid_type;
+ bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid);
+ } else {
+ // The discovery response contained a 128-bit UUID that has not yet been registered with the
+ // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
+ // For now, just leave the UUID as NULL.
+ }
+
+ common_hal_bleio_descriptor_construct(descriptor, uuid);
+ descriptor->handle = gattc_desc->handle;
+ descriptor->characteristic = m_desc_discovery_characteristic;
+
+ mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor));
+ }
+
+ if (response->count > 0) {
+ m_discovery_successful = true;
+ }
+ m_discovery_in_process = false;
+}
+
+STATIC void discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t device) {
+ switch (ble_evt->header.evt_id) {
+ case BLE_GAP_EVT_DISCONNECTED:
+ m_discovery_successful = false;
+ m_discovery_in_process = false;
+ break;
+
+ case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
+ on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, device);
+ break;
+
+ case BLE_GATTC_EVT_CHAR_DISC_RSP:
+ on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, device);
+ break;
+
+ case BLE_GATTC_EVT_DESC_DISC_RSP:
+ on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, device);
+ break;
+
+ default:
+ // For debugging.
+ // mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id);
+ break;
+ }
+}
+
+
+void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) {
+ mp_obj_list_t *remote_services_list = common_hal_bleio_device_get_remote_services_list(device);
+
+ ble_drv_add_event_handler(discovery_on_ble_evt, device);
+
+ if (service_uuids_whitelist == mp_const_none) {
+ // List of service UUID's not given, so discover all available services.
+
+ uint16_t next_service_start_handle = BLE_GATT_HANDLE_START;
+
+ while (discover_next_services(device, next_service_start_handle, MP_OBJ_NULL)) {
+ // discover_next_services() appends to remote_services_list.
+
+ // Get the most recently discovered service, and then ask for services
+ // whose handles start after the last attribute handle inside that service.
+ const bleio_service_obj_t *service =
+ MP_OBJ_TO_PTR(remote_services_list->items[remote_services_list->len - 1]);
+ next_service_start_handle = service->end_handle + 1;
+ }
+ } else {
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf);
+ mp_obj_t uuid_obj;
+ while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) {
+ mp_raise_ValueError(translate("non-UUID found in service_uuids_whitelist"));
+ }
+ bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj);
+
+ ble_uuid_t nrf_uuid;
+ bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid);
+
+ // Service might or might not be discovered; that's ok. Caller has to check
+ // Central.remote_services to find out.
+ // We only need to call this once for each service to discover.
+ discover_next_services(device, BLE_GATT_HANDLE_START, &nrf_uuid);
+ }
+ }
+
+
+ for (size_t service_idx = 0; service_idx < remote_services_list->len; ++service_idx) {
+ bleio_service_obj_t *service = MP_OBJ_TO_PTR(remote_services_list->items[service_idx]);
+
+ // Skip the service if it had an unknown (unregistered) UUID.
+ if (service->uuid == NULL) {
+ continue;
+ }
+
+ uint16_t next_char_start_handle = service->start_handle;
+
+ // Stop when we go past the end of the range of handles for this service or
+ // discovery call returns nothing.
+ // discover_next_characteristics() appends to the characteristic_list.
+ while (next_char_start_handle <= service->end_handle &&
+ discover_next_characteristics(device, service, next_char_start_handle)) {
+
+
+ // Get the most recently discovered characteristic, and then ask for characteristics
+ // whose handles start after the last attribute handle inside that characteristic.
+ const bleio_characteristic_obj_t *characteristic =
+ MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]);
+ next_char_start_handle = characteristic->handle + 1;
+ }
+
+ // Got characteristics for this service. Now discover descriptors for each characteristic.
+ size_t char_list_len = service->characteristic_list->len;
+ for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) {
+ bleio_characteristic_obj_t *characteristic =
+ MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]);
+ const bool last_characteristic = char_idx == char_list_len - 1;
+ bleio_characteristic_obj_t *next_characteristic = last_characteristic
+ ? NULL
+ : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]);
+
+ // Skip the characteristic if it had an unknown (unregistered) UUID.
+ if (characteristic->uuid == NULL) {
+ continue;
+ }
+
+ uint16_t next_desc_start_handle = characteristic->handle + 1;
+
+ // Don't run past the end of this service or the beginning of the next characteristic.
+ uint16_t next_desc_end_handle = next_characteristic == NULL
+ ? service->end_handle
+ : next_characteristic->handle - 1;
+
+ // Stop when we go past the end of the range of handles for this service or
+ // discovery call returns nothing.
+ // discover_next_descriptors() appends to the descriptor_list.
+ while (next_desc_start_handle <= service->end_handle &&
+ next_desc_start_handle < next_desc_end_handle &&
+ discover_next_descriptors(device, characteristic,
+ next_desc_start_handle, next_desc_end_handle)) {
+
+ // Get the most recently discovered descriptor, and then ask for descriptors
+ // whose handles start after that descriptor's handle.
+ const bleio_descriptor_obj_t *descriptor =
+ MP_OBJ_TO_PTR(characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1]);
+ next_desc_start_handle = descriptor->handle + 1;
+ }
+ }
+ }
+
+ // This event handler is no longer needed.
+ ble_drv_remove_event_handler(discovery_on_ble_evt, device);
+
}
diff --git a/ports/nrf/common-hal/bleio/__init__.h b/ports/nrf/common-hal/bleio/__init__.h
index 0c46b631f..c0bba3799 100644
--- a/ports/nrf/common-hal/bleio/__init__.h
+++ b/ports/nrf/common-hal/bleio/__init__.h
@@ -27,16 +27,10 @@
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H
-#include "shared-bindings/bleio/__init__.h"
-#include "shared-bindings/bleio/Adapter.h"
-
-#include "shared-module/bleio/__init__.h"
+void bleio_reset(void);
// We assume variable length data.
// 20 bytes max (23 - 3).
#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3)
-gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device);
-uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device);
-
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H
diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h
index a2b48065d..8fa6721e2 100644
--- a/ports/nrf/nrfx_config.h
+++ b/ports/nrf/nrfx_config.h
@@ -78,6 +78,7 @@
// TIMERS
#define NRFX_TIMER_ENABLED 1
// Don't enable TIMER0: it's used by the SoftDevice.
+#define NRFX_TIMER0_ENABLED 0
#define NRFX_TIMER1_ENABLED 1
#define NRFX_TIMER2_ENABLED 1
diff --git a/ports/nrf/peripherals/nrf/timers.c b/ports/nrf/peripherals/nrf/timers.c
index 7f7a003d3..88f3dd468 100644
--- a/ports/nrf/peripherals/nrf/timers.c
+++ b/ports/nrf/peripherals/nrf/timers.c
@@ -36,7 +36,7 @@
STATIC nrfx_timer_t nrfx_timers[] = {
#if NRFX_CHECK(NRFX_TIMER0_ENABLED)
- // Note that TIMER0 is reserved for use by the SoftDevice, so it should not usually be enabled.
+ #error NRFX_TIMER0_ENABLED should not be on: TIMER0 is used by the SoftDevice
NRFX_TIMER_INSTANCE(0),
#endif
#if NRFX_CHECK(NRFX_TIMER1_ENABLED)
diff --git a/shared-bindings/bleio/Central.c b/shared-bindings/bleio/Central.c
index fc00bce5c..70b270c65 100644
--- a/shared-bindings/bleio/Central.c
+++ b/shared-bindings/bleio/Central.c
@@ -34,6 +34,7 @@
#include "py/objproperty.h"
#include "py/objstr.h"
#include "py/runtime.h"
+#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Adapter.h"
#include "shared-bindings/bleio/Address.h"
#include "shared-bindings/bleio/Characteristic.h"
@@ -56,12 +57,16 @@
//|
//| my_entry = None
//| for entry in entries:
-//| if entry.name is not None and entry.name == 'MyPeripheral':
+//| if entry.name is not None and entry.name == 'InterestingPeripheral':
//| my_entry = entry
//| break
//|
-//| central = bleio.Central(my_entry.address)
-//| central.connect(10.0) # timeout after 10 seconds
+//| if not my_entry:
+//| raise Exception("'InterestingPeripheral' not found")
+//|
+//| central = bleio.Central()
+//| central.connect(my_entry.address, 10) # timeout after 10 seconds
+//| central.discover_remote_services()
//|
//| .. class:: Central()
@@ -79,24 +84,11 @@ STATIC mp_obj_t bleio_central_make_new(const mp_obj_type_t *type, size_t n_args,
return MP_OBJ_FROM_PTR(self);
}
-//| .. method:: connect(address, timeout, *, service_uuids=None)
-//| Attempts a connection to the remote peripheral. If the connection is successful,
-//| Do BLE discovery for the listed services, to find their handles and characteristics.
-//| The attribute `remote_services` will contain a list of all discovered services.
+//| .. method:: connect(address, timeout, *, service_uuids_whitelist=None)
+//| Attempts a connection to the remote peripheral.
//|
//| :param bleio.Address address: The address of the peripheral to connect to
//| :param float/int timeout: Try to connect for timeout seconds.
-//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
-//| provided by the peripheral that you want to use.
-//| The peripheral may provide more services, but services not listed are ignored.
-//| If a service in service_uuids is not found during discovery, it will not
-//| appear in `remote_services`.
-//|
-//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
-//|
-//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
-//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
-//| service or characteristic to be discovered. (This restriction may be lifted in the future.)
//|
STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
@@ -105,7 +97,6 @@ STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ },
- { MP_QSTR_service_uuids_whitelist, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@@ -119,7 +110,7 @@ STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args
mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
// common_hal_bleio_central_connect() will validate that services is an iterable or None.
- common_hal_bleio_central_connect(self, address, timeout, args[ARG_service_uuids_whitelist].u_obj);
+ common_hal_bleio_central_connect(self, address, timeout);
return mp_const_none;
}
@@ -139,6 +130,47 @@ STATIC mp_obj_t bleio_central_disconnect(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_disconnect_obj, bleio_central_disconnect);
+//| .. method:: discover_remote_services(service_uuids_whitelist=None)
+//| Do BLE discovery for all services or for the given service UUIDS,
+//| to find their handles and characteristics.
+//| The attribute `remote_services` will contain a list of all discovered services.
+//| `Central.connected` must be True.
+//|
+//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
+//| provided by the peripheral that you want to use.
+//| The peripheral may provide more services, but services not listed are ignored.
+//| If a service in service_uuids_whitelist is not found during discovery, it will not
+//| appear in `remote_services`.
+//|
+//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
+//|
+//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
+//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
+//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered
+//| for use. (This restriction may be lifted in the future.)
+//|
+STATIC mp_obj_t bleio_central_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+
+ enum { ARG_service_uuids_whitelist };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if (!common_hal_bleio_central_get_connected(self)) {
+ mp_raise_ValueError(translate("Not connected"));
+ }
+
+ common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self),
+ args[ARG_service_uuids_whitelist].u_obj);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_discover_remote_services_obj, 1, bleio_central_discover_remote_services);
+
//| .. attribute:: connected
//|
//| True if connected to a remove peripheral.
@@ -181,8 +213,9 @@ const mp_obj_property_t bleio_central_remote_services_obj = {
STATIC const mp_rom_map_elem_t bleio_central_locals_dict_table[] = {
// Methods
- { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) },
- { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_central_discover_remote_services_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_central_connected_obj) },
diff --git a/shared-bindings/bleio/Central.h b/shared-bindings/bleio/Central.h
index 0e84037f9..1d7fb6248 100644
--- a/shared-bindings/bleio/Central.h
+++ b/shared-bindings/bleio/Central.h
@@ -34,7 +34,7 @@
extern const mp_obj_type_t bleio_central_type;
extern void common_hal_bleio_central_construct(bleio_central_obj_t *self);
-extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout, mp_obj_t service_uuids);
+extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout);
extern void common_hal_bleio_central_disconnect(bleio_central_obj_t *self);
extern bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self);
diff --git a/shared-bindings/bleio/Peripheral.c b/shared-bindings/bleio/Peripheral.c
index 1302c9652..69bc72d92 100644
--- a/shared-bindings/bleio/Peripheral.c
+++ b/shared-bindings/bleio/Peripheral.c
@@ -35,6 +35,7 @@
#include "py/objstr.h"
#include "py/runtime.h"
+#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Adapter.h"
#include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Peripheral.h"
@@ -107,15 +108,15 @@ STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_ar
self->base.type = &bleio_peripheral_type;
// Copy the services list and validate its items.
- mp_obj_t service_list_obj = mp_obj_new_list(0, NULL);
- mp_obj_list_t *service_list = MP_OBJ_FROM_PTR(service_list_obj);
+ mp_obj_t services_list_obj = mp_obj_new_list(0, NULL);
+ mp_obj_list_t *services_list = MP_OBJ_FROM_PTR(services_list_obj);
mp_obj_t service;
while ((service = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if (!MP_OBJ_IS_TYPE(service, &bleio_service_type)) {
mp_raise_ValueError(translate("non-Service found in services"));
}
- mp_obj_list_append(service_list, service);
+ mp_obj_list_append(services_list, service);
}
const mp_obj_t name = args[ARG_name].u_obj;
@@ -128,7 +129,7 @@ STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_ar
mp_raise_ValueError(translate("name must be a string"));
}
- common_hal_bleio_peripheral_construct(self, service_list, name_str);
+ common_hal_bleio_peripheral_construct(self, services_list, name_str);
return MP_OBJ_FROM_PTR(self);
}
@@ -158,8 +159,8 @@ const mp_obj_property_t bleio_peripheral_connected_obj = {
STATIC mp_obj_t bleio_peripheral_get_services(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Return list as a tuple so user won't be able to change it.
- mp_obj_list_t *service_list = common_hal_bleio_peripheral_get_service_list(self);
- return mp_obj_new_tuple(service_list->len, service_list->items);
+ mp_obj_list_t *services_list = common_hal_bleio_peripheral_get_services_list(self);
+ return mp_obj_new_tuple(services_list->len, services_list->items);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services);
@@ -265,16 +266,63 @@ STATIC mp_obj_t bleio_peripheral_disconnect(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_disconnect_obj, bleio_peripheral_disconnect);
+//| .. method:: discover_remote_services(service_uuids_whitelist=None)
+//| Do BLE discovery for all services or for the given service UUIDS,
+//| to find their handles and characteristics.
+//| The attribute `remote_services` will contain a list of all discovered services.
+//| `Peripheral.connected` must be True.
+//|
+//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
+//| provided by the peripheral that you want to use.
+//| The peripheral may provide more services, but services not listed are ignored.
+//| If a service in service_uuids_whitelist is not found during discovery, it will not
+//| appear in `remote_services`.
+//|
+//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
+//|
+//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
+//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
+//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered
+//| for use. (This restriction may be lifted in the future.)
+//|
+//| Thought it is unusual for a peripheral to act as a BLE client, it can do so, and
+//| needs to be able to do discovery on its peer (a central).
+//| Examples include a peripheral accessing a central that provides Current Time Service,
+//| Apple Notification Center Service, or Battery Service.
+//|
+STATIC mp_obj_t bleio_peripheral_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+
+ enum { ARG_service_uuids_whitelist };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if (!common_hal_bleio_peripheral_get_connected(self)) {
+ mp_raise_ValueError(translate("Not connected"));
+ }
+
+ common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self),
+ args[ARG_service_uuids_whitelist].u_obj);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_discover_remote_services_obj, 1, bleio_peripheral_discover_remote_services);
+
STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = {
// Methods
- { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) },
- { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) },
- { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) },
+ { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) },
+ { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_peripheral_discover_remote_services_obj) },
// Properties
- { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) },
- { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) },
- { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) },
+ { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) },
+ { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) },
+ { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table);
diff --git a/shared-bindings/bleio/Peripheral.h b/shared-bindings/bleio/Peripheral.h
index 846250a5f..7dad0bccd 100644
--- a/shared-bindings/bleio/Peripheral.h
+++ b/shared-bindings/bleio/Peripheral.h
@@ -33,7 +33,7 @@
extern const mp_obj_type_t bleio_peripheral_type;
extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *service_list, mp_obj_t name);
-extern mp_obj_list_t *common_hal_bleio_peripheral_get_service_list(bleio_peripheral_obj_t *self);
+extern mp_obj_list_t *common_hal_bleio_peripheral_get_services_list(bleio_peripheral_obj_t *self);
extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self);
extern mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self);
extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, float interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c
index db0606991..b4aeab2bd 100644
--- a/shared-bindings/bleio/Service.c
+++ b/shared-bindings/bleio/Service.c
@@ -43,12 +43,16 @@
//| .. class:: Service(uuid, characteristics, *, secondary=False)
//|
//| Create a new Service object identified by the specified UUID.
+//|
//| To mark the service as secondary, pass `True` as :py:data:`secondary`.
//|
//| :param bleio.UUID uuid: The uuid of the service
//| :param iterable characteristics: the Characteristic objects for this service
//| :param bool secondary: If the service is a secondary one
//|
+//| A Service may be remote (:py:data:`remote` is ``True``), but a remote Service
+//| cannot be constructed directly. It is created by `Central.discover_remote_services()`
+//| or `Peripheral.discover_remote_services()`.
STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_uuid, ARG_characteristics, ARG_secondary };
@@ -119,6 +123,24 @@ const mp_obj_property_t bleio_service_characteristics_obj = {
(mp_obj_t)&mp_const_none_obj },
};
+//| .. attribute:: remote
+//|
+//| True if this is a service provided by a remote device. (read-only)
+//|
+STATIC mp_obj_t bleio_service_get_remote(mp_obj_t self_in) {
+ bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ return mp_obj_new_bool(common_hal_bleio_service_get_is_remote(self));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_remote_obj, bleio_service_get_remote);
+
+const mp_obj_property_t bleio_service_remote_obj = {
+ .base.type = &mp_type_property,
+ .proxy = { (mp_obj_t)&bleio_service_get_remote_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj },
+};
+
//| .. attribute:: secondary
//|
//| True if this is a secondary service. (read-only)
diff --git a/shared-bindings/bleio/Service.h b/shared-bindings/bleio/Service.h
index f646c81a9..716c2e8a9 100644
--- a/shared-bindings/bleio/Service.h
+++ b/shared-bindings/bleio/Service.h
@@ -35,6 +35,7 @@ const mp_obj_type_t bleio_service_type;
extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, mp_obj_list_t *characteristic_list, bool is_secondary);
extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self);
+extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self);
extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self);
extern void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self);
diff --git a/shared-bindings/bleio/__init__.h b/shared-bindings/bleio/__init__.h
index 4aa6dd45b..dd6e55204 100644
--- a/shared-bindings/bleio/__init__.h
+++ b/shared-bindings/bleio/__init__.h
@@ -29,8 +29,19 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H
+#include "py/objlist.h"
+
+#include "shared-bindings/bleio/__init__.h"
+#include "shared-bindings/bleio/Adapter.h"
+
+#include "shared-module/bleio/__init__.h"
#include "common-hal/bleio/Adapter.h"
extern const super_adapter_obj_t common_hal_bleio_adapter_obj;
+extern uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device);
+extern mp_obj_list_t *common_hal_bleio_device_get_remote_services_list(mp_obj_t device);
+extern void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist);
+
+
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H
diff --git a/shared-module/bleio/__init__.h b/shared-module/bleio/__init__.h
index 07d148594..f8f0e0660 100644
--- a/shared-module/bleio/__init__.h
+++ b/shared-module/bleio/__init__.h
@@ -27,12 +27,6 @@
#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H
#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H
-typedef enum {
- GATT_ROLE_NONE,
- GATT_ROLE_SERVER,
- GATT_ROLE_CLIENT,
-} gatt_role_t;
-
extern void bleio_reset(void);
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H