summaryrefslogtreecommitdiff
path: root/ports
diff options
context:
space:
mode:
authorJeff Epler <jepler@gmail.com>2019-08-18 21:30:14 -0500
committerJeff Epler <jepler@gmail.com>2019-08-18 21:30:14 -0500
commit26a05d01dcec68077474e2283afe50da9e092304 (patch)
tree94a58abee12b32939d9399309c61ec7a325476d0 /ports
parent388dd5e1c4430745dc35bdcd4da25faad2fca5f2 (diff)
parent3f7321af0074456f11c7614d8c29d91b787411f6 (diff)
Merge remote-tracking branch 'origin/master' into nrf-pdm-audioin
Diffstat (limited to 'ports')
-rwxr-xr-xports/nrf/Makefile3
-rw-r--r--ports/nrf/boards/make-pins.py208
-rw-r--r--ports/nrf/common-hal/bleio/Adapter.c41
-rw-r--r--ports/nrf/common-hal/bleio/Attribute.c60
-rw-r--r--ports/nrf/common-hal/bleio/Attribute.h32
-rw-r--r--ports/nrf/common-hal/bleio/Central.c344
-rw-r--r--ports/nrf/common-hal/bleio/Central.h5
-rw-r--r--ports/nrf/common-hal/bleio/Characteristic.c264
-rw-r--r--ports/nrf/common-hal/bleio/Characteristic.h10
-rw-r--r--ports/nrf/common-hal/bleio/CharacteristicBuffer.c2
-rw-r--r--ports/nrf/common-hal/bleio/Descriptor.c111
-rw-r--r--ports/nrf/common-hal/bleio/Descriptor.h11
-rw-r--r--ports/nrf/common-hal/bleio/Peripheral.c106
-rw-r--r--ports/nrf/common-hal/bleio/Peripheral.h15
-rw-r--r--ports/nrf/common-hal/bleio/Service.c101
-rw-r--r--ports/nrf/common-hal/bleio/Service.h2
-rw-r--r--ports/nrf/common-hal/bleio/__init__.c451
-rw-r--r--ports/nrf/common-hal/bleio/__init__.h8
-rw-r--r--ports/nrf/device/nrf52/startup_nrf52840.c2
-rw-r--r--ports/nrf/nrfx_config.h1
-rw-r--r--ports/nrf/peripherals/nrf/timers.c2
21 files changed, 991 insertions, 788 deletions
diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile
index 9da2c498d..fad127514 100755
--- a/ports/nrf/Makefile
+++ b/ports/nrf/Makefile
@@ -95,8 +95,7 @@ ifeq ($(DEBUG), 1)
CFLAGS += -fno-inline -fno-ipa-sra
else
CFLAGS += -Os -DNDEBUG
- # TODO: Test with -flto
- ### CFLAGS += -flto
+ CFLAGS += -flto -flto-partition=none
endif
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/Adapter.c b/ports/nrf/common-hal/bleio/Adapter.c
index 9d0912930..936dfcb9b 100644
--- a/ports/nrf/common-hal/bleio/Adapter.c
+++ b/ports/nrf/common-hal/bleio/Adapter.c
@@ -34,10 +34,11 @@
#include "nrfx_power.h"
#include "nrf_nvic.h"
#include "nrf_sdm.h"
+#include "py/objstr.h"
#include "py/runtime.h"
-#include "shared-bindings/bleio/Adapter.h"
-
#include "supervisor/usb.h"
+#include "shared-bindings/bleio/Adapter.h"
+#include "shared-bindings/bleio/Address.h"
STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) {
mp_raise_msg_varg(&mp_type_AssertionError,
@@ -132,20 +133,42 @@ bool common_hal_bleio_adapter_get_enabled(void) {
return is_enabled;
}
-void common_hal_bleio_adapter_get_address(bleio_address_obj_t *address) {
- ble_gap_addr_t local_address;
+void get_address(ble_gap_addr_t *address) {
uint32_t err_code;
common_hal_bleio_adapter_set_enabled(true);
- err_code = sd_ble_gap_addr_get(&local_address);
+ err_code = sd_ble_gap_addr_get(address);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to get local address"));
}
+}
+
+bleio_address_obj_t *common_hal_bleio_adapter_get_address(void) {
+ common_hal_bleio_adapter_set_enabled(true);
+
+ ble_gap_addr_t local_address;
+ get_address(&local_address);
+
+ bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t);
+ address->base.type = &bleio_address_type;
+
+ common_hal_bleio_address_construct(address, local_address.addr, local_address.addr_type);
+ return address;
+}
+
+mp_obj_t common_hal_bleio_adapter_get_default_name(void) {
+ common_hal_bleio_adapter_set_enabled(true);
+
+ ble_gap_addr_t local_address;
+ get_address(&local_address);
+
+ char name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0 };
- address->type = local_address.addr_type;
+ name[sizeof(name) - 4] = nibble_to_hex_lower[local_address.addr[1] >> 4 & 0xf];
+ name[sizeof(name) - 3] = nibble_to_hex_lower[local_address.addr[1] & 0xf];
+ name[sizeof(name) - 2] = nibble_to_hex_lower[local_address.addr[0] >> 4 & 0xf];
+ name[sizeof(name) - 1] = nibble_to_hex_lower[local_address.addr[0] & 0xf];
- mp_buffer_info_t buf_info;
- mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ);
- memcpy(address->bytes, buf_info.buf, NUM_BLEIO_ADDRESS_BYTES);
+ return mp_obj_new_str(name, sizeof(name));
}
diff --git a/ports/nrf/common-hal/bleio/Attribute.c b/ports/nrf/common-hal/bleio/Attribute.c
new file mode 100644
index 000000000..fd1e7538d
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/Attribute.c
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "shared-bindings/bleio/Attribute.h"
+
+// Convert a bleio security mode to a ble_gap_conn_sec_mode_t setting.
+void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode) {
+ switch (security_mode) {
+ case SECURITY_MODE_NO_ACCESS:
+ BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(perm);
+ break;
+
+ case SECURITY_MODE_OPEN:
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(perm);
+ break;
+
+ case SECURITY_MODE_ENC_NO_MITM:
+ BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(perm);
+ break;
+
+ case SECURITY_MODE_ENC_WITH_MITM:
+ BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(perm);
+ break;
+
+ case SECURITY_MODE_LESC_ENC_WITH_MITM:
+ BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(perm);
+ break;
+
+ case SECURITY_MODE_SIGNED_NO_MITM:
+ BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(perm);
+ break;
+
+ case SECURITY_MODE_SIGNED_WITH_MITM:
+ BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(perm);
+ break;
+ }
+}
diff --git a/ports/nrf/common-hal/bleio/Attribute.h b/ports/nrf/common-hal/bleio/Attribute.h
new file mode 100644
index 000000000..44c2fdfa1
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/Attribute.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
+#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
+
+// Nothing yet.
+
+#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
diff --git a/ports/nrf/common-hal/bleio/Central.c b/ports/nrf/common-hal/bleio/Central.c
index d90737411..d771437d6 100644
--- a/ports/nrf/common-hal/bleio/Central.c
+++ b/ports/nrf/common-hal/bleio/Central.c
@@ -34,217 +34,9 @@
#include "nrf_soc.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/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;
-}
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) {
@@ -458,6 +131,15 @@ bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self) {
return self->conn_handle != BLE_CONN_HANDLE_INVALID;
}
+mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist) {
+ common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist);
+ // Convert to a tuple and then clear the list so the callee will take ownership.
+ mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_services_list->len,
+ self->remote_services_list->items);
+ mp_obj_list_clear(self->remote_services_list);
+ return services_tuple;
+}
+
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..b7a9050b8 100644
--- a/ports/nrf/common-hal/bleio/Central.h
+++ b/ports/nrf/common-hal/bleio/Central.h
@@ -31,15 +31,14 @@
#include <stdbool.h>
#include "py/objlist.h"
-#include "shared-module/bleio/__init__.h"
#include "shared-module/bleio/Address.h"
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..f8b6ce06c 100644
--- a/ports/nrf/common-hal/bleio/Characteristic.c
+++ b/ports/nrf/common-hal/bleio/Characteristic.c
@@ -25,29 +25,23 @@
* THE SOFTWARE.
*/
-#include <string.h>
-#include <stdio.h>
-
-#include "ble_drv.h"
-#include "ble_gatts.h"
-#include "nrf_soc.h"
-
#include "py/runtime.h"
-#include "common-hal/bleio/__init__.h"
-#include "common-hal/bleio/Characteristic.h"
-STATIC volatile bleio_characteristic_obj_t *m_read_characteristic;
+#include "shared-bindings/bleio/__init__.h"
+#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/Descriptor.h"
+#include "shared-bindings/bleio/Service.h"
-STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) {
- const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
+static volatile bleio_characteristic_obj_t *m_read_characteristic;
+
+STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) {
uint16_t cccd;
ble_gatts_value_t value = {
.p_value = (uint8_t*) &cccd,
.len = 2,
};
- const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->cccd_handle, &value);
-
+ const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, cccd_handle, &value);
if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) {
// CCCD is not set, so say that neither Notify nor Indicate is enabled.
@@ -59,64 +53,39 @@ STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) {
return cccd;
}
-STATIC void gatts_read(bleio_characteristic_obj_t *characteristic) {
- // This might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because
- // we can still read and write the local value.
- const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
-
- mp_buffer_info_t bufinfo;
- ble_gatts_value_t gatts_value = {
- .p_value = NULL,
- .len = 0,
- };
-
- // Read once to find out what size buffer we need, then read again to fill buffer.
-
- uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value);
- if (err_code == NRF_SUCCESS) {
- characteristic->value_data = mp_obj_new_bytearray_of_zeros(gatts_value.len);
- mp_get_buffer_raise(characteristic->value_data, &bufinfo, MP_BUFFER_WRITE);
- gatts_value.p_value = bufinfo.buf;
-
- // Read again, with the correct size of buffer.
- err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value);
- }
-
- if (err_code != NRF_SUCCESS) {
- mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code);
- }
-}
-
+STATIC void characteristic_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
+ switch (ble_evt->header.evt_id) {
-STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) {
- // This might be BLE_CONN_HANDLE_INVALID if we're not conected, but that's OK, because
- // we can still read and write the local value.
- const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
+ // More events may be handled later, so keep this as a switch.
- ble_gatts_value_t gatts_value = {
- .p_value = bufinfo->buf,
- .len = bufinfo->len,
- };
+ case BLE_GATTC_EVT_READ_RSP: {
+ ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
+ if (m_read_characteristic) {
+ m_read_characteristic->value = mp_obj_new_bytearray(response->len, response->data);
+ }
+ // Indicate to busy-wait loop that we've read the attribute value.
+ m_read_characteristic = NULL;
+ break;
+ }
- const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, characteristic->handle, &gatts_value);
- if (err_code != NRF_SUCCESS) {
- mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code);
+ default:
+ // For debugging.
+ // mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
+ break;
}
}
-STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
+STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
uint16_t hvx_len = bufinfo->len;
ble_gatts_hvx_params_t hvx_params = {
- .handle = characteristic->handle,
+ .handle = handle,
.type = hvx_type,
.offset = 0,
.p_len = &hvx_len,
.p_data = bufinfo->buf,
};
- const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
-
while (1) {
const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
if (err_code == NRF_SUCCESS) {
@@ -132,21 +101,17 @@ STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp
// Some real error has occurred.
mp_raise_OSError_msg_varg(translate("Failed to notify or indicate attribute value, err 0x%04x"), err_code);
}
-
-}
-
-STATIC void check_connected(uint16_t conn_handle) {
- if (conn_handle == BLE_CONN_HANDLE_INVALID) {
- mp_raise_OSError_msg(translate("Not connected"));
- }
}
-STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) {
+STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
- check_connected(conn_handle);
+ common_hal_bleio_check_connected(conn_handle);
+ // Set to NULL in event loop after event.
m_read_characteristic = characteristic;
+ ble_drv_add_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic);
+
const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code);
@@ -155,126 +120,102 @@ STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) {
while (m_read_characteristic != NULL) {
MICROPY_VM_HOOK_LOOP;
}
-}
-
-STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) {
- const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
- check_connected(conn_handle);
-
- ble_gattc_write_params_t write_params = {
- .write_op = characteristic->props.write_no_response ? BLE_GATT_OP_WRITE_CMD : BLE_GATT_OP_WRITE_REQ,
- .handle = characteristic->handle,
- .p_value = bufinfo->buf,
- .len = bufinfo->len,
- };
-
- while (1) {
- uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params);
- if (err_code == NRF_SUCCESS) {
- break;
- }
-
- // Write with response will return NRF_ERROR_BUSY if the response has not been received.
- // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending.
- if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) {
- // We could wait for an event indicating the write is complete, but just retrying is easier.
- MICROPY_VM_HOOK_LOOP;
- continue;
- }
-
- // Some real error occurred.
- mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code);
- }
+ ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic);
}
-STATIC void characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) {
- switch (ble_evt->header.evt_id) {
-
- // More events may be handled later, so keep this as a switch.
-
- case BLE_GATTC_EVT_READ_RSP: {
- ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
- m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data);
- // Indicate to busy-wait loop that we've read the characteristic.
- m_read_characteristic = NULL;
- break;
- }
-
- // For debugging.
- default:
- // mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
- break;
- }
-
-}
-
-void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, mp_obj_list_t *descriptor_list) {
- self->service = mp_const_none;
+void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_obj_list_t *descriptor_list) {
+ self->service = MP_OBJ_NULL;
self->uuid = uuid;
- self->value_data = mp_const_none;
+ self->value = mp_const_empty_bytes;
+ self->handle = BLE_GATT_HANDLE_INVALID;
self->props = props;
+ self->read_perm = read_perm;
+ self->write_perm = write_perm;
self->descriptor_list = descriptor_list;
- self->handle = BLE_GATT_HANDLE_INVALID;
- ble_drv_add_event_handler(characteristic_on_ble_evt, self);
+ const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX;
+ if (max_length < 0 || max_length > max_length_max) {
+ mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"),
+ max_length_max, fixed_length ? "True" : "False");
+ }
+ self->max_length = max_length;
+ self->fixed_length = fixed_length;
+
+ for (size_t descriptor_idx = 0; descriptor_idx < descriptor_list->len; ++descriptor_idx) {
+ bleio_descriptor_obj_t *descriptor =
+ MP_OBJ_TO_PTR(descriptor_list->items[descriptor_idx]);
+ descriptor->characteristic = self;
+ }
}
mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) {
return self->descriptor_list;
}
+bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) {
+ return self->service;
+}
+
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:
- gattc_read(self);
- break;
-
- case GATT_ROLE_SERVER:
- gatts_read(self);
- break;
-
- default:
- mp_raise_RuntimeError(translate("bad GATT role"));
- break;
+ // Do GATT operations only if this characteristic has been added to a registered service.
+ if (self->handle != BLE_GATT_HANDLE_INVALID) {
+ uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
+ if (common_hal_bleio_service_get_is_remote(self->service)) {
+ // self->value is set by evt handler.
+ characteristic_gattc_read(self);
+ } else {
+ self->value = common_hal_bleio_gatts_read(self->handle, conn_handle);
+ }
}
- return self->value_data;
+ return self->value;
}
void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
- bool sent = false;
- uint16_t cccd = 0;
+ // Do GATT operations only if this characteristic has been added to a registered service.
+ if (self->handle != BLE_GATT_HANDLE_INVALID) {
+ uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
+
+ if (common_hal_bleio_service_get_is_remote(self->service)) {
+ // Last argument is true if write-no-reponse desired.
+ common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo,
+ (self->props & CHAR_PROP_WRITE_NO_RESPONSE));
+ } else {
+ if (self->fixed_length && bufinfo->len != self->max_length) {
+ mp_raise_ValueError(translate("Value length required fixed length"));
+ }
+ if (bufinfo->len > self->max_length) {
+ mp_raise_ValueError(translate("Value length > max_length"));
+ }
+
+ 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);
+ const bool notify = self->props & CHAR_PROP_NOTIFY;
+ const bool indicate = self->props & CHAR_PROP_INDICATE;
+ if (notify | indicate) {
+ cccd = characteristic_get_cccd(self->cccd_handle, conn_handle);
}
+
// 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);
+ if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
+ characteristic_gatts_notify_indicate(self->handle, conn_handle, 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);
+ if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
+ characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION);
sent = true;
}
+
if (!sent) {
- gatts_write(self, bufinfo);
+ common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo);
}
- break;
-
- case GATT_ROLE_CLIENT:
- gattc_write(self, bufinfo);
- break;
-
- default:
- mp_raise_RuntimeError(translate("bad GATT role"));
- break;
+ }
}
-}
+ self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len);
+}
bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) {
return self->uuid;
@@ -289,18 +230,17 @@ 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"));
}
+ const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
+ common_hal_bleio_check_connected(conn_handle);
+
uint16_t cccd_value =
(notify ? BLE_GATT_HVX_NOTIFICATION : 0) |
(indicate ? BLE_GATT_HVX_INDICATION : 0);
- const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
- check_connected(conn_handle);
-
-
ble_gattc_write_params_t write_params = {
.write_op = BLE_GATT_OP_WRITE_REQ,
.handle = self->cccd_handle,
diff --git a/ports/nrf/common-hal/bleio/Characteristic.h b/ports/nrf/common-hal/bleio/Characteristic.h
index 7cb227596..fb6a4c6ce 100644
--- a/ports/nrf/common-hal/bleio/Characteristic.h
+++ b/ports/nrf/common-hal/bleio/Characteristic.h
@@ -28,17 +28,23 @@
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H
+#include "shared-bindings/bleio/Attribute.h"
+#include "shared-module/bleio/Characteristic.h"
#include "common-hal/bleio/Service.h"
#include "common-hal/bleio/UUID.h"
-#include "shared-module/bleio/Characteristic.h"
typedef struct {
mp_obj_base_t base;
+ // Will be MP_OBJ_NULL before being assigned to a Service.
bleio_service_obj_t *service;
bleio_uuid_obj_t *uuid;
- volatile mp_obj_t value_data;
+ mp_obj_t value;
+ uint16_t max_length;
+ bool fixed_length;
uint16_t handle;
bleio_characteristic_properties_t props;
+ bleio_attribute_security_mode_t read_perm;
+ bleio_attribute_security_mode_t write_perm;
mp_obj_list_t *descriptor_list;
uint16_t user_desc_handle;
uint16_t cccd_handle;
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/Descriptor.c b/ports/nrf/common-hal/bleio/Descriptor.c
index 005af6eaa..2a2019277 100644
--- a/ports/nrf/common-hal/bleio/Descriptor.c
+++ b/ports/nrf/common-hal/bleio/Descriptor.c
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2016 Glenn Ruben Bakke
*
@@ -26,17 +26,116 @@
* THE SOFTWARE.
*/
-#include "common-hal/bleio/Descriptor.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/bleio/__init__.h"
+#include "shared-bindings/bleio/Descriptor.h"
+#include "shared-bindings/bleio/Service.h"
#include "shared-bindings/bleio/UUID.h"
-void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid) {
+static volatile bleio_descriptor_obj_t *m_read_descriptor;
+
+void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length) {
+ self->characteristic = MP_OBJ_NULL;
self->uuid = uuid;
-}
+ self->value = mp_const_empty_bytes;
+ self->handle = BLE_GATT_HANDLE_INVALID;
+ self->read_perm = read_perm;
+ self->write_perm = write_perm;
-mp_int_t common_hal_bleio_descriptor_get_handle(bleio_descriptor_obj_t *self) {
- return self->handle;
+ const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX;
+ if (max_length < 0 || max_length > max_length_max) {
+ mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"),
+ max_length_max, fixed_length ? "True" : "False");
+ }
+ self->max_length = max_length;
+ self->fixed_length = fixed_length;
}
bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) {
return self->uuid;
}
+
+bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) {
+ return self->characteristic;
+}
+
+STATIC void descriptor_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
+ switch (ble_evt->header.evt_id) {
+
+ // More events may be handled later, so keep this as a switch.
+
+ case BLE_GATTC_EVT_READ_RSP: {
+ ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
+ if (m_read_descriptor) {
+ m_read_descriptor->value = mp_obj_new_bytearray(response->len, response->data);
+ }
+ // Indicate to busy-wait loop that we've read the attribute value.
+ m_read_descriptor = NULL;
+ break;
+ }
+
+ default:
+ // For debugging.
+ // mp_printf(&mp_plat_print, "Unhandled descriptor event: 0x%04x\n", ble_evt->header.evt_id);
+ break;
+ }
+}
+
+STATIC void descriptor_gattc_read(bleio_descriptor_obj_t *descriptor) {
+ const uint16_t conn_handle =
+ common_hal_bleio_device_get_conn_handle(descriptor->characteristic->service->device);
+ common_hal_bleio_check_connected(conn_handle);
+
+ // Set to NULL in event loop after event.
+ m_read_descriptor = descriptor;
+
+ ble_drv_add_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor);
+
+ const uint32_t err_code = sd_ble_gattc_read(conn_handle, descriptor->handle, 0);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code);
+ }
+
+ while (m_read_descriptor != NULL) {
+ MICROPY_VM_HOOK_LOOP;
+ }
+
+ ble_drv_remove_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor);
+}
+
+mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self) {
+ // Do GATT operations only if this descriptor has been registered
+ if (self->handle != BLE_GATT_HANDLE_INVALID) {
+ if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
+ descriptor_gattc_read(self);
+ } else {
+ self->value = common_hal_bleio_gatts_read(
+ self->handle, common_hal_bleio_device_get_conn_handle(self->characteristic->service->device));
+ }
+ }
+
+ return self->value;
+}
+
+void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) {
+ // Do GATT operations only if this descriptor has been registered.
+ if (self->handle != BLE_GATT_HANDLE_INVALID) {
+ uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->characteristic->service->device);
+ if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
+ // false means WRITE_REQ, not write-no-response
+ common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false);
+ } else {
+ if (self->fixed_length && bufinfo->len != self->max_length) {
+ mp_raise_ValueError(translate("Value length != required fixed length"));
+ }
+ if (bufinfo->len > self->max_length) {
+ mp_raise_ValueError(translate("Value length > max_length"));
+ }
+
+ common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo);
+ }
+ }
+
+ self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len);
+}
diff --git a/ports/nrf/common-hal/bleio/Descriptor.h b/ports/nrf/common-hal/bleio/Descriptor.h
index b7af6a42f..6db4d7be3 100644
--- a/ports/nrf/common-hal/bleio/Descriptor.h
+++ b/ports/nrf/common-hal/bleio/Descriptor.h
@@ -30,14 +30,21 @@
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H
#include "py/obj.h"
-#include "common-hal/bleio/Characteristic.h"
+
+#include "shared-bindings/bleio/Characteristic.h"
#include "common-hal/bleio/UUID.h"
typedef struct {
mp_obj_base_t base;
- uint16_t handle;
+ // Will be MP_OBJ_NULL before being assigned to a Characteristic.
bleio_characteristic_obj_t *characteristic;
bleio_uuid_obj_t *uuid;
+ mp_obj_t value;
+ uint16_t max_length;
+ bool fixed_length;
+ uint16_t handle;
+ bleio_attribute_security_mode_t read_perm;
+ bleio_attribute_security_mode_t write_perm;
} bleio_descriptor_obj_t;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H
diff --git a/ports/nrf/common-hal/bleio/Peripheral.c b/ports/nrf/common-hal/bleio/Peripheral.c
index 608bd1342..ed35516fc 100644
--- a/ports/nrf/common-hal/bleio/Peripheral.c
+++ b/ports/nrf/common-hal/bleio/Peripheral.c
@@ -36,12 +36,12 @@
#include "py/objlist.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/Characteristic.h"
#include "shared-bindings/bleio/Peripheral.h"
#include "shared-bindings/bleio/Service.h"
#include "shared-bindings/bleio/UUID.h"
-#include "common-hal/bleio/Service.h"
#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS)
@@ -52,6 +52,19 @@
#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
+static const ble_gap_sec_params_t pairing_sec_params = {
+ .bond = 0, // TODO: add bonding
+ .mitm = 0,
+ .lesc = 0,
+ .keypress = 0,
+ .oob = 0,
+ .io_caps = BLE_GAP_IO_CAPS_NONE,
+ .min_key_size = 7,
+ .max_key_size = 16,
+ .kdist_own = { .enc = 1, .id = 1},
+ .kdist_peer = { .enc = 1, .id = 1},
+};
+
STATIC void check_data_fit(size_t data_len) {
if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) {
mp_raise_ValueError(translate("Data too large for advertisement packet"));
@@ -61,6 +74,9 @@ STATIC void check_data_fit(size_t data_len) {
STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
bleio_peripheral_obj_t *self = (bleio_peripheral_obj_t*)self_in;
+ // For debugging.
+ // mp_printf(&mp_plat_print, "Peripheral event: 0x%04x\n", ble_evt->header.evt_id);
+
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED: {
// Central has connected.
@@ -89,10 +105,6 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
// Someday may handle timeouts or limit reached.
break;
- case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
- sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
- break;
-
case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: {
ble_gap_evt_conn_param_update_request_t *request =
&ble_evt->evt.gap_evt.params.conn_param_update_request;
@@ -113,6 +125,47 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
break;
+ case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
+ sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &pairing_sec_params, NULL);
+ break;
+
+ case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
+ // TODO for LESC pairing:
+ // sd_ble_gap_lesc_dhkey_reply(...);
+ break;
+
+ case BLE_GAP_EVT_AUTH_STATUS: {
+ // Pairing process completed
+ ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status;
+ if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) {
+ // mp_printf(&mp_plat_print, "Pairing succeeded, status: 0x%04x\n", status->auth_status);
+ self->pair_status = PAIR_PAIRED;
+ } else {
+ // mp_printf(&mp_plat_print, "Pairing failed, status: 0x%04x\n", status->auth_status);
+ self->pair_status = PAIR_NOT_PAIRED;
+ }
+ break;
+ }
+
+ case BLE_GAP_EVT_CONN_SEC_UPDATE: {
+ ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec;
+ if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) {
+ // Security setup did not succeed:
+ // mode 0, level 0 means no access
+ // mode 1, level 1 means open link
+ // mode >=1 and/or level >=1 means encryption is set up
+ self->pair_status = PAIR_NOT_PAIRED;
+ } else {
+ // TODO: see Bluefruit lib
+ // if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) {
+ // sd_ble_gatts_sys_attr_set(_conn_hdl, NULL, 0, 0);
+ // }
+ self->pair_status = PAIR_PAIRED;
+ }
+ break;
+ }
+
+
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
@@ -120,20 +173,22 @@ 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;
+ self->pair_status = PAIR_NOT_PAIRED;
// 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 +211,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(bleio_peripheral_obj_t *self) {
+ return self->services_list;
}
bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) {
@@ -247,3 +302,30 @@ void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *self)
void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *self) {
sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
}
+
+mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist) {
+ common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist);
+ // Convert to a tuple and then clear the list so the callee will take ownership.
+ mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_services_list->len,
+ self->remote_services_list->items);
+ mp_obj_list_clear(self->remote_services_list);
+ return services_tuple;
+}
+
+void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *self) {
+ self->pair_status = PAIR_WAITING;
+
+ uint32_t err_code = sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params);
+
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to start pairing, error 0x%04x"), err_code);
+ }
+
+ while (self->pair_status == PAIR_WAITING) {
+ MICROPY_VM_HOOK_LOOP;
+ }
+
+ if (self->pair_status == PAIR_NOT_PAIRED) {
+ mp_raise_OSError_msg(translate("Failed to pair"));
+ }
+}
diff --git a/ports/nrf/common-hal/bleio/Peripheral.h b/ports/nrf/common-hal/bleio/Peripheral.h
index 2a1251715..e75a1641a 100644
--- a/ports/nrf/common-hal/bleio/Peripheral.h
+++ b/ports/nrf/common-hal/bleio/Peripheral.h
@@ -35,22 +35,29 @@
#include "py/obj.h"
#include "py/objlist.h"
-#include "shared-module/bleio/__init__.h"
#include "shared-module/bleio/Address.h"
+typedef enum {
+ PAIR_NOT_PAIRED,
+ PAIR_WAITING,
+ PAIR_PAIRED,
+} pair_status_t;
+
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).
uint8_t* advertising_data;
uint8_t* scan_response_data;
uint8_t adv_handle;
-
+ pair_status_t pair_status;
} bleio_peripheral_obj_t;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H
diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c
index b6fcde827..a7a3a6db0 100644
--- a/ports/nrf/common-hal/bleio/Service.c
+++ b/ports/nrf/common-hal/bleio/Service.c
@@ -29,8 +29,8 @@
#include "ble.h"
#include "py/runtime.h"
#include "common-hal/bleio/__init__.h"
-#include "common-hal/bleio/Characteristic.h"
#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/Service.h"
#include "shared-bindings/bleio/Adapter.h"
@@ -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) {
@@ -46,7 +47,6 @@ void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_ob
MP_OBJ_TO_PTR(characteristic_list->items[characteristic_idx]);
characteristic->service = self;
}
-
}
bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) {
@@ -57,6 +57,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;
}
@@ -68,13 +72,17 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self)
bleio_characteristic_obj_t *characteristic =
MP_OBJ_TO_PTR(self->characteristic_list->items[characteristic_idx]);
+ if (characteristic->handle != BLE_GATT_HANDLE_INVALID) {
+ mp_raise_ValueError(translate("Characteristic already in use by another Service."));
+ }
+
ble_gatts_char_md_t char_md = {
- .char_props.broadcast = characteristic->props.broadcast,
- .char_props.read = characteristic->props.read,
- .char_props.write_wo_resp = characteristic->props.write_no_response,
- .char_props.write = characteristic->props.write,
- .char_props.notify = characteristic->props.notify,
- .char_props.indicate = characteristic->props.indicate,
+ .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0,
+ .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0,
+ .char_props.write_wo_resp = (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) ? 1 : 0,
+ .char_props.write = (characteristic->props & CHAR_PROP_WRITE) ? 1 : 0,
+ .char_props.notify = (characteristic->props & CHAR_PROP_NOTIFY) ? 1 : 0,
+ .char_props.indicate = (characteristic->props & CHAR_PROP_INDICATE) ? 1 : 0,
};
ble_gatts_attr_md_t cccd_md = {
@@ -88,39 +96,74 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self)
char_md.p_cccd_md = &cccd_md;
}
- ble_uuid_t uuid;
- bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &uuid);
+ ble_uuid_t char_uuid;
+ bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &char_uuid);
- ble_gatts_attr_md_t attr_md = {
+ ble_gatts_attr_md_t char_attr_md = {
.vloc = BLE_GATTS_VLOC_STACK,
- .vlen = 1,
+ .vlen = !characteristic->fixed_length,
};
- BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
- BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_attr_md.read_perm);
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_attr_md.write_perm);
+
+ mp_buffer_info_t char_value_bufinfo;
+ mp_get_buffer_raise(characteristic->value, &char_value_bufinfo, MP_BUFFER_READ);
- ble_gatts_attr_t attr_char_value = {
- .p_uuid = &uuid,
- .p_attr_md = &attr_md,
- .init_len = sizeof(uint8_t),
- .max_len = GATT_MAX_DATA_LENGTH,
+ ble_gatts_attr_t char_attr = {
+ .p_uuid = &char_uuid,
+ .p_attr_md = &char_attr_md,
+ .init_len = char_value_bufinfo.len,
+ .p_value = char_value_bufinfo.buf,
+ .init_offs = 0,
+ .max_len = characteristic->max_length,
};
- ble_gatts_char_handles_t handles;
+ ble_gatts_char_handles_t char_handles;
uint32_t err_code;
- err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &attr_char_value, &handles);
+ err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &char_attr, &char_handles);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code);
}
- if (characteristic->handle != BLE_GATT_HANDLE_INVALID) {
- mp_raise_ValueError(translate("Characteristic already in use by another Service."));
- }
+ characteristic->user_desc_handle = char_handles.user_desc_handle;
+ characteristic->cccd_handle = char_handles.cccd_handle;
+ characteristic->sccd_handle = char_handles.sccd_handle;
+ characteristic->handle = char_handles.value_handle;
- characteristic->user_desc_handle = handles.user_desc_handle;
- characteristic->cccd_handle = handles.cccd_handle;
- characteristic->sccd_handle = handles.sccd_handle;
- characteristic->handle = handles.value_handle;
- }
+ // Add the descriptors for this characteristic.
+ for (size_t descriptor_idx = 0; descriptor_idx < characteristic->descriptor_list->len; ++descriptor_idx) {
+ bleio_descriptor_obj_t *descriptor =
+ MP_OBJ_TO_PTR(characteristic->descriptor_list->items[descriptor_idx]);
+
+ ble_uuid_t desc_uuid;
+ bleio_uuid_convert_to_nrf_ble_uuid(descriptor->uuid, &desc_uuid);
+
+ ble_gatts_attr_md_t desc_attr_md = {
+ // Data passed is not in a permanent location and should be copied.
+ .vloc = BLE_GATTS_VLOC_STACK,
+ .vlen = !descriptor->fixed_length,
+ };
+
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_attr_md.read_perm);
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_attr_md.write_perm);
+
+ mp_buffer_info_t desc_value_bufinfo;
+ mp_get_buffer_raise(descriptor->value, &desc_value_bufinfo, MP_BUFFER_READ);
+
+ ble_gatts_attr_t desc_attr = {
+ .p_uuid = &desc_uuid,
+ .p_attr_md = &desc_attr_md,
+ .init_len = desc_value_bufinfo.len,
+ .p_value = desc_value_bufinfo.buf,
+ .init_offs = 0,
+ .max_len = descriptor->max_length,
+ };
+
+ err_code = sd_ble_gatts_descriptor_add(characteristic->handle, &desc_attr, &descriptor->handle);
+
+ } // loop over descriptors
+
+ } // loop over characteristics
}
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..2e26ac07d 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,13 +59,9 @@ 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;
+void common_hal_bleio_check_connected(uint16_t conn_handle) {
+ if (conn_handle == BLE_CONN_HANDLE_INVALID) {
+ mp_raise_OSError_msg(translate("Not connected"));
}
}
@@ -63,6 +71,433 @@ uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) {
} else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
} else {
- return 0;
+ return BLE_CONN_HANDLE_INVALID;
+ }
+}
+
+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;
+
+ 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 =
+ (gattc_char->char_props.broadcast ? CHAR_PROP_BROADCAST : 0) |
+ (gattc_char->char_props.indicate ? CHAR_PROP_INDICATE : 0) |
+ (gattc_char->char_props.notify ? CHAR_PROP_NOTIFY : 0) |
+ (gattc_char->char_props.read ? CHAR_PROP_READ : 0) |
+ (gattc_char->char_props.write ? CHAR_PROP_WRITE : 0) |
+ (gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0);
+
+ // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler.
+ common_hal_bleio_characteristic_construct(
+ characteristic, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
+ GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc
+ 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 BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG:
+ m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle;
+ break;
+
+ case BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG:
+ m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle;
+ break;
+
+ case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC:
+ 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, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
+ GATT_MAX_DATA_LENGTH, false);
+ 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);
+
+ // Start over with an empty list.
+ mp_obj_list_clear(MP_OBJ_FROM_PTR(common_hal_bleio_device_get_remote_services_list(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);
+
+}
+
+// GATTS read of a Characteristic or Descriptor.
+mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle) {
+ // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because
+ // we can still read and write the local value.
+
+ mp_buffer_info_t bufinfo;
+ ble_gatts_value_t gatts_value = {
+ .p_value = NULL,
+ .len = 0,
+ };
+
+ // Read once to find out what size buffer we need, then read again to fill buffer.
+
+ mp_obj_t value = mp_const_none;
+ uint32_t err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value);
+ if (err_code == NRF_SUCCESS) {
+ value = mp_obj_new_bytearray_of_zeros(gatts_value.len);
+ mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_WRITE);
+ gatts_value.p_value = bufinfo.buf;
+
+ // Read again, with the correct size of buffer.
+ err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value);
+ }
+
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code);
+ }
+
+ return value;
+}
+
+void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) {
+ // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because
+ // we can still read and write the local value.
+
+ ble_gatts_value_t gatts_value = {
+ .p_value = bufinfo->buf,
+ .len = bufinfo->len,
+ };
+
+ const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code);
+ }
+}
+
+void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) {
+ common_hal_bleio_check_connected(conn_handle);
+
+ ble_gattc_write_params_t write_params = {
+ .write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ,
+ .handle = handle,
+ .p_value = bufinfo->buf,
+ .len = bufinfo->len,
+ };
+
+ while (1) {
+ uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params);
+ if (err_code == NRF_SUCCESS) {
+ break;
+ }
+
+ // Write with response will return NRF_ERROR_BUSY if the response has not been received.
+ // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending.
+ if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) {
+ // We could wait for an event indicating the write is complete, but just retrying is easier.
+ MICROPY_VM_HOOK_LOOP;
+ continue;
+ }
+
+ // Some real error occurred.
+ mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code);
+ }
+
}
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/device/nrf52/startup_nrf52840.c b/ports/nrf/device/nrf52/startup_nrf52840.c
index 30634a1b5..8e1c36012 100644
--- a/ports/nrf/device/nrf52/startup_nrf52840.c
+++ b/ports/nrf/device/nrf52/startup_nrf52840.c
@@ -116,7 +116,7 @@ void CRYPTOCELL_IRQHandler (void) __attribute__ ((weak, alias("Default_Han
void SPIM3_IRQHandler (void) __attribute__ ((weak, alias("Default_Handler")));
void PWM3_IRQHandler (void) __attribute__ ((weak, alias("Default_Handler")));
-const func __Vectors[] __attribute__ ((section(".isr_vector"))) = {
+const func __Vectors[] __attribute__ ((used, section(".isr_vector"))) = {
(func)&_estack,
Reset_Handler,
NMI_Handler,
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)