allow discovery from central or peripheral
This commit is contained in:
parent
d99d3bd471
commit
28ca05ccdc
@ -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()
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
switch (common_hal_bleio_device_get_gatt_role(self->service->device)) {
|
||||
case GATT_ROLE_SERVER:
|
||||
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:
|
||||
if (common_hal_bleio_service_get_is_remote(self->service)) {
|
||||
gattc_write(self, bufinfo);
|
||||
break;
|
||||
} else {
|
||||
bool sent = false;
|
||||
uint16_t cccd = 0;
|
||||
|
||||
default:
|
||||
mp_raise_RuntimeError(translate("bad GATT role"));
|
||||
break;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 =
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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).
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,16 +59,6 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = {
|
||||
},
|
||||
};
|
||||
|
||||
gatt_role_t common_hal_bleio_device_get_gatt_role(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;
|
||||
} else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
|
||||
return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role;
|
||||
} else {
|
||||
return GATT_ROLE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
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))->conn_handle;
|
||||
@ -66,3 +68,350 @@ uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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))->remote_services_list;
|
||||
} else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
|
||||
return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->remote_services_list;
|
||||
} else {
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) },
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user