Merge remote-tracking branch 'origin/master' into nrf-pdm-audioin

crypto-aes
Jeff Epler 4 years ago
commit 26a05d01dc

@ -1 +1 @@
Subproject commit 1ee9ef4f2b7c6acfab6c398a4f57ca22036958f7
Subproject commit 96d96a94b887bc1b648a175c28b377dba76a9068

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -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

@ -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()

@ -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));
}

@ -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;
}
}

@ -24,15 +24,9 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H
#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H
typedef enum {
GATT_ROLE_NONE,
GATT_ROLE_SERVER,
GATT_ROLE_CLIENT,
} gatt_role_t;
// Nothing yet.
extern void bleio_reset(void);
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H

@ -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;
}

@ -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

@ -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,

@ -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;

@ -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) {

@ -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);
}

@ -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

@ -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)