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

This commit is contained in:
Jeff Epler 2019-08-18 21:30:14 -05:00
commit 26a05d01dc
61 changed files with 25476 additions and 11304 deletions

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

View File

@ -95,8 +95,7 @@ ifeq ($(DEBUG), 1)
CFLAGS += -fno-inline -fno-ipa-sra CFLAGS += -fno-inline -fno-ipa-sra
else else
CFLAGS += -Os -DNDEBUG CFLAGS += -Os -DNDEBUG
# TODO: Test with -flto CFLAGS += -flto -flto-partition=none
### CFLAGS += -flto
endif endif

View File

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

View File

@ -34,10 +34,11 @@
#include "nrfx_power.h" #include "nrfx_power.h"
#include "nrf_nvic.h" #include "nrf_nvic.h"
#include "nrf_sdm.h" #include "nrf_sdm.h"
#include "py/objstr.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared-bindings/bleio/Adapter.h"
#include "supervisor/usb.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) { STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) {
mp_raise_msg_varg(&mp_type_AssertionError, mp_raise_msg_varg(&mp_type_AssertionError,
@ -132,20 +133,42 @@ bool common_hal_bleio_adapter_get_enabled(void) {
return is_enabled; return is_enabled;
} }
void common_hal_bleio_adapter_get_address(bleio_address_obj_t *address) { void get_address(ble_gap_addr_t *address) {
ble_gap_addr_t local_address;
uint32_t err_code; uint32_t err_code;
common_hal_bleio_adapter_set_enabled(true); 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) { if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to get local address")); mp_raise_OSError_msg(translate("Failed to get local address"));
} }
}
address->type = local_address.addr_type;
bleio_address_obj_t *common_hal_bleio_adapter_get_address(void) {
mp_buffer_info_t buf_info; common_hal_bleio_adapter_set_enabled(true);
mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ);
memcpy(address->bytes, buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); 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 };
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];
return mp_obj_new_str(name, sizeof(name));
} }

View File

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

View File

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

View File

@ -34,217 +34,9 @@
#include "nrf_soc.h" #include "nrf_soc.h"
#include "py/objstr.h" #include "py/objstr.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Adapter.h"
#include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Central.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) { 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; 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: case BLE_GAP_EVT_DISCONNECTED:
central->conn_handle = BLE_CONN_HANDLE_INVALID; 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; break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST: 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); sd_ble_gap_conn_param_update(central->conn_handle, &request->conn_params);
break; break;
} }
default: default:
// For debugging. // For debugging.
// mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id); // 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) { void common_hal_bleio_central_construct(bleio_central_obj_t *self) {
common_hal_bleio_adapter_set_enabled(true); common_hal_bleio_adapter_set_enabled(true);
self->service_list = mp_obj_new_list(0, NULL); self->remote_services_list = mp_obj_new_list(0, NULL);
self->gatt_role = GATT_ROLE_CLIENT;
self->conn_handle = BLE_CONN_HANDLE_INVALID; 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); common_hal_bleio_adapter_set_enabled(true);
ble_drv_add_event_handler(central_on_ble_evt, self); 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) { if (self->conn_handle == BLE_CONN_HANDLE_INVALID) {
mp_raise_OSError_msg(translate("Failed to connect: timeout")); 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) { 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; return self->conn_handle != BLE_CONN_HANDLE_INVALID;
} }
mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self) { mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist) {
return self->service_list; 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->remote_services_list;
} }

View File

@ -31,15 +31,14 @@
#include <stdbool.h> #include <stdbool.h>
#include "py/objlist.h" #include "py/objlist.h"
#include "shared-module/bleio/__init__.h"
#include "shared-module/bleio/Address.h" #include "shared-module/bleio/Address.h"
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
gatt_role_t gatt_role;
volatile bool waiting_to_connect; volatile bool waiting_to_connect;
volatile uint16_t conn_handle; 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; } bleio_central_obj_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H

View File

@ -25,29 +25,23 @@
* THE SOFTWARE. * 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 "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) { static volatile bleio_characteristic_obj_t *m_read_characteristic;
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) {
uint16_t cccd; uint16_t cccd;
ble_gatts_value_t value = { ble_gatts_value_t value = {
.p_value = (uint8_t*) &cccd, .p_value = (uint8_t*) &cccd,
.len = 2, .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) { if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) {
// CCCD is not set, so say that neither Notify nor Indicate is enabled. // 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; return cccd;
} }
STATIC void gatts_read(bleio_characteristic_obj_t *characteristic) { STATIC void characteristic_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
// This might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because switch (ble_evt->header.evt_id) {
// 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; // More events may be handled later, so keep this as a switch.
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. 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;
}
uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value); default:
if (err_code == NRF_SUCCESS) { // For debugging.
characteristic->value_data = mp_obj_new_bytearray_of_zeros(gatts_value.len); // mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
mp_get_buffer_raise(characteristic->value_data, &bufinfo, MP_BUFFER_WRITE); break;
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_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
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);
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, 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);
}
}
STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
uint16_t hvx_len = bufinfo->len; uint16_t hvx_len = bufinfo->len;
ble_gatts_hvx_params_t hvx_params = { ble_gatts_hvx_params_t hvx_params = {
.handle = characteristic->handle, .handle = handle,
.type = hvx_type, .type = hvx_type,
.offset = 0, .offset = 0,
.p_len = &hvx_len, .p_len = &hvx_len,
.p_data = bufinfo->buf, .p_data = bufinfo->buf,
}; };
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
while (1) { while (1) {
const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params); const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
if (err_code == NRF_SUCCESS) { 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. // Some real error has occurred.
mp_raise_OSError_msg_varg(translate("Failed to notify or indicate attribute value, err 0x%04x"), err_code); 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) { STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic) {
if (conn_handle == BLE_CONN_HANDLE_INVALID) {
mp_raise_OSError_msg(translate("Not connected"));
}
}
STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); 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; 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); const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0);
if (err_code != NRF_SUCCESS) { if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code); 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) { while (m_read_characteristic != NULL) {
MICROPY_VM_HOOK_LOOP; MICROPY_VM_HOOK_LOOP;
} }
ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic);
} }
STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { 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) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); self->service = MP_OBJ_NULL;
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);
}
}
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;
self->uuid = uuid; self->uuid = uuid;
self->value_data = mp_const_none; self->value = mp_const_empty_bytes;
self->props = props;
self->descriptor_list = descriptor_list;
self->handle = BLE_GATT_HANDLE_INVALID; self->handle = BLE_GATT_HANDLE_INVALID;
self->props = props;
self->read_perm = read_perm;
self->write_perm = write_perm;
self->descriptor_list = descriptor_list;
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) { mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) {
return self->descriptor_list; 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) { 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)) { // Do GATT operations only if this characteristic has been added to a registered service.
case GATT_ROLE_CLIENT: if (self->handle != BLE_GATT_HANDLE_INVALID) {
gattc_read(self); uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
break; if (common_hal_bleio_service_get_is_remote(self->service)) {
// self->value is set by evt handler.
case GATT_ROLE_SERVER: characteristic_gattc_read(self);
gatts_read(self); } else {
break; self->value = common_hal_bleio_gatts_read(self->handle, conn_handle);
}
default:
mp_raise_RuntimeError(translate("bad GATT role"));
break;
} }
return self->value_data; return self->value;
} }
void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
bool sent = false; // Do GATT operations only if this characteristic has been added to a registered service.
uint16_t cccd = 0; if (self->handle != BLE_GATT_HANDLE_INVALID) {
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
switch (common_hal_bleio_device_get_gatt_role(self->service->device)) { if (common_hal_bleio_service_get_is_remote(self->service)) {
case GATT_ROLE_SERVER: // Last argument is true if write-no-reponse desired.
if (self->props.notify || self->props.indicate) { common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo,
cccd = get_cccd(self); (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;
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. // It's possible that both notify and indicate are set.
if (self->props.notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) { if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_NOTIFICATION); characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION);
sent = true; sent = true;
} }
if (self->props.indicate && (cccd & BLE_GATT_HVX_INDICATION)) { if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_INDICATION); characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION);
sent = true; sent = true;
} }
if (!sent) { 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) { bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) {
return self->uuid; 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")); mp_raise_ValueError(translate("No CCCD for this Characteristic"));
} }
if (common_hal_bleio_device_get_gatt_role(self->service->device) != GATT_ROLE_CLIENT) { if (!common_hal_bleio_service_get_is_remote(self->service)) {
mp_raise_ValueError(translate("Can't set CCCD for local Characteristic")); 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 = uint16_t cccd_value =
(notify ? BLE_GATT_HVX_NOTIFICATION : 0) | (notify ? BLE_GATT_HVX_NOTIFICATION : 0) |
(indicate ? BLE_GATT_HVX_INDICATION : 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 = { ble_gattc_write_params_t write_params = {
.write_op = BLE_GATT_OP_WRITE_REQ, .write_op = BLE_GATT_OP_WRITE_REQ,
.handle = self->cccd_handle, .handle = self->cccd_handle,

View File

@ -28,17 +28,23 @@
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H #ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H
#define 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/Service.h"
#include "common-hal/bleio/UUID.h" #include "common-hal/bleio/UUID.h"
#include "shared-module/bleio/Characteristic.h"
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
// Will be MP_OBJ_NULL before being assigned to a Service.
bleio_service_obj_t *service; bleio_service_obj_t *service;
bleio_uuid_obj_t *uuid; 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; uint16_t handle;
bleio_characteristic_properties_t props; 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; mp_obj_list_t *descriptor_list;
uint16_t user_desc_handle; uint16_t user_desc_handle;
uint16_t cccd_handle; uint16_t cccd_handle;

View File

@ -37,7 +37,7 @@
#include "tick.h" #include "tick.h"
#include "common-hal/bleio/__init__.h" #include "shared-bindings/bleio/__init__.h"
#include "common-hal/bleio/CharacteristicBuffer.h" #include "common-hal/bleio/CharacteristicBuffer.h"
STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) { STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) {

View File

@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * 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) 2018 Artur Pacholec
* Copyright (c) 2016 Glenn Ruben Bakke * Copyright (c) 2016 Glenn Ruben Bakke
* *
@ -26,17 +26,116 @@
* THE SOFTWARE. * 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" #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;
self->uuid = uuid;
}
mp_int_t common_hal_bleio_descriptor_get_handle(bleio_descriptor_obj_t *self) { 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) {
return self->handle; 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;
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) { bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) {
return self->uuid; 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);
}

View File

@ -30,14 +30,21 @@
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H #define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H
#include "py/obj.h" #include "py/obj.h"
#include "common-hal/bleio/Characteristic.h"
#include "shared-bindings/bleio/Characteristic.h"
#include "common-hal/bleio/UUID.h" #include "common-hal/bleio/UUID.h"
typedef struct { typedef struct {
mp_obj_base_t base; 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_characteristic_obj_t *characteristic;
bleio_uuid_obj_t *uuid; 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; } bleio_descriptor_obj_t;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H

View File

@ -36,12 +36,12 @@
#include "py/objlist.h" #include "py/objlist.h"
#include "py/objstr.h" #include "py/objstr.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Adapter.h"
#include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Peripheral.h" #include "shared-bindings/bleio/Peripheral.h"
#include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/Service.h"
#include "shared-bindings/bleio/UUID.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_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, 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_ADV_AD_TYPE_FIELD_SIZE 1
#define BLE_AD_TYPE_FLAGS_DATA_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) { STATIC void check_data_fit(size_t data_len) {
if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) { if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) {
mp_raise_ValueError(translate("Data too large for advertisement packet")); 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) { 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; 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) { switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED: { case BLE_GAP_EVT_CONNECTED: {
// Central has 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. // Someday may handle timeouts or limit reached.
break; 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: { case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: {
ble_gap_evt_conn_param_update_request_t *request = ble_gap_evt_conn_param_update_request_t *request =
&ble_evt->evt.gap_evt.params.conn_param_update_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); sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
break; 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: default:
// For debugging. // For debugging.
// mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id); // 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); 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->name = name;
self->gatt_role = GATT_ROLE_SERVER;
self->conn_handle = BLE_CONN_HANDLE_INVALID; self->conn_handle = BLE_CONN_HANDLE_INVALID;
self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
self->pair_status = PAIR_NOT_PAIRED;
// Add all the services. // Add all the services.
for (size_t service_idx = 0; service_idx < service_list->len; ++service_idx) { for (size_t service_idx = 0; service_idx < services_list->len; ++service_idx) {
bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[service_idx]); bleio_service_obj_t *service = MP_OBJ_TO_PTR(services_list->items[service_idx]);
service->device = MP_OBJ_FROM_PTR(self); 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) { mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self) {
return self->service_list; return self->services_list;
} }
bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) { 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) { void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *self) {
sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); 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"));
}
}

View File

@ -35,22 +35,29 @@
#include "py/obj.h" #include "py/obj.h"
#include "py/objlist.h" #include "py/objlist.h"
#include "shared-module/bleio/__init__.h"
#include "shared-module/bleio/Address.h" #include "shared-module/bleio/Address.h"
typedef enum {
PAIR_NOT_PAIRED,
PAIR_WAITING,
PAIR_PAIRED,
} pair_status_t;
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
mp_obj_t name; mp_obj_t name;
gatt_role_t gatt_role;
volatile uint16_t conn_handle; 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 // 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, // 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). // there are tricks to get the SD to notice (see DevZone - TBS).
uint8_t* advertising_data; uint8_t* advertising_data;
uint8_t* scan_response_data; uint8_t* scan_response_data;
uint8_t adv_handle; uint8_t adv_handle;
pair_status_t pair_status;
} bleio_peripheral_obj_t; } bleio_peripheral_obj_t;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H

View File

@ -29,8 +29,8 @@
#include "ble.h" #include "ble.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "common-hal/bleio/__init__.h" #include "common-hal/bleio/__init__.h"
#include "common-hal/bleio/Characteristic.h"
#include "shared-bindings/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/Service.h"
#include "shared-bindings/bleio/Adapter.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->handle = 0xFFFF;
self->uuid = uuid; self->uuid = uuid;
self->characteristic_list = characteristic_list; self->characteristic_list = characteristic_list;
self->is_remote = false;
self->is_secondary = is_secondary; self->is_secondary = is_secondary;
for (size_t characteristic_idx = 0; characteristic_idx < characteristic_list->len; ++characteristic_idx) { 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]); MP_OBJ_TO_PTR(characteristic_list->items[characteristic_idx]);
characteristic->service = self; characteristic->service = self;
} }
} }
bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *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; 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) { bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) {
return self->is_secondary; 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 = bleio_characteristic_obj_t *characteristic =
MP_OBJ_TO_PTR(self->characteristic_list->items[characteristic_idx]); 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 = { ble_gatts_char_md_t char_md = {
.char_props.broadcast = characteristic->props.broadcast, .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0,
.char_props.read = characteristic->props.read, .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0,
.char_props.write_wo_resp = characteristic->props.write_no_response, .char_props.write_wo_resp = (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) ? 1 : 0,
.char_props.write = characteristic->props.write, .char_props.write = (characteristic->props & CHAR_PROP_WRITE) ? 1 : 0,
.char_props.notify = characteristic->props.notify, .char_props.notify = (characteristic->props & CHAR_PROP_NOTIFY) ? 1 : 0,
.char_props.indicate = characteristic->props.indicate, .char_props.indicate = (characteristic->props & CHAR_PROP_INDICATE) ? 1 : 0,
}; };
ble_gatts_attr_md_t cccd_md = { 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; char_md.p_cccd_md = &cccd_md;
} }
ble_uuid_t uuid; ble_uuid_t char_uuid;
bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &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, .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(&char_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.write_perm);
ble_gatts_attr_t attr_char_value = { mp_buffer_info_t char_value_bufinfo;
.p_uuid = &uuid, mp_get_buffer_raise(characteristic->value, &char_value_bufinfo, MP_BUFFER_READ);
.p_attr_md = &attr_md,
.init_len = sizeof(uint8_t), ble_gatts_attr_t char_attr = {
.max_len = GATT_MAX_DATA_LENGTH, .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; 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) { if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code); mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code);
} }
if (characteristic->handle != BLE_GATT_HANDLE_INVALID) { characteristic->user_desc_handle = char_handles.user_desc_handle;
mp_raise_ValueError(translate("Characteristic already in use by another Service.")); 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; // Add the descriptors for this characteristic.
characteristic->cccd_handle = handles.cccd_handle; for (size_t descriptor_idx = 0; descriptor_idx < characteristic->descriptor_list->len; ++descriptor_idx) {
characteristic->sccd_handle = handles.sccd_handle; bleio_descriptor_obj_t *descriptor =
characteristic->handle = handles.value_handle; 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
} }

View File

@ -35,6 +35,8 @@ typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
// Handle for this service. // Handle for this service.
uint16_t handle; uint16_t handle;
// True if created during discovery.
bool is_remote;
bool is_secondary; bool is_secondary;
bleio_uuid_obj_t *uuid; bleio_uuid_obj_t *uuid;
// May be a Peripheral, Central, etc. // May be a Peripheral, Central, etc.

View File

@ -26,12 +26,24 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "py/runtime.h"
#include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Adapter.h"
#include "shared-bindings/bleio/Central.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/Peripheral.h"
#include "shared-bindings/bleio/Service.h"
#include "shared-bindings/bleio/UUID.h"
#include "common-hal/bleio/__init__.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. // Turn off BLE on a reset or reload.
void bleio_reset() { void bleio_reset() {
if (common_hal_bleio_adapter_get_enabled()) { 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) { void common_hal_bleio_check_connected(uint16_t conn_handle) {
if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { if (conn_handle == BLE_CONN_HANDLE_INVALID) {
return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; mp_raise_OSError_msg(translate("Not connected"));
} 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;
} }
} }
@ -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)) { } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
} else { } 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);
}
}

View File

@ -27,16 +27,10 @@
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H #ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H #define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H
#include "shared-bindings/bleio/__init__.h" void bleio_reset(void);
#include "shared-bindings/bleio/Adapter.h"
#include "shared-module/bleio/__init__.h"
// We assume variable length data. // We assume variable length data.
// 20 bytes max (23 - 3). // 20 bytes max (23 - 3).
#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 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 #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H

View File

@ -116,7 +116,7 @@ void CRYPTOCELL_IRQHandler (void) __attribute__ ((weak, alias("Default_Han
void SPIM3_IRQHandler (void) __attribute__ ((weak, alias("Default_Handler"))); void SPIM3_IRQHandler (void) __attribute__ ((weak, alias("Default_Handler")));
void PWM3_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, (func)&_estack,
Reset_Handler, Reset_Handler,
NMI_Handler, NMI_Handler,

View File

@ -78,6 +78,7 @@
// TIMERS // TIMERS
#define NRFX_TIMER_ENABLED 1 #define NRFX_TIMER_ENABLED 1
// Don't enable TIMER0: it's used by the SoftDevice. // Don't enable TIMER0: it's used by the SoftDevice.
#define NRFX_TIMER0_ENABLED 0
#define NRFX_TIMER1_ENABLED 1 #define NRFX_TIMER1_ENABLED 1
#define NRFX_TIMER2_ENABLED 1 #define NRFX_TIMER2_ENABLED 1

View File

@ -36,7 +36,7 @@
STATIC nrfx_timer_t nrfx_timers[] = { STATIC nrfx_timer_t nrfx_timers[] = {
#if NRFX_CHECK(NRFX_TIMER0_ENABLED) #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), NRFX_TIMER_INSTANCE(0),
#endif #endif
#if NRFX_CHECK(NRFX_TIMER1_ENABLED) #if NRFX_CHECK(NRFX_TIMER1_ENABLED)

View File

@ -232,6 +232,7 @@ $(filter $(SRC_PATTERNS), \
audioio/AudioOut.c \ audioio/AudioOut.c \
bleio/__init__.c \ bleio/__init__.c \
bleio/Adapter.c \ bleio/Adapter.c \
bleio/Attribute.c \
bleio/Central.c \ bleio/Central.c \
bleio/Characteristic.c \ bleio/Characteristic.c \
bleio/CharacteristicBuffer.c \ bleio/CharacteristicBuffer.c \
@ -281,6 +282,9 @@ $(filter $(SRC_PATTERNS), \
# All possible sources are listed here, and are filtered by SRC_PATTERNS. # All possible sources are listed here, and are filtered by SRC_PATTERNS.
SRC_BINDINGS_ENUMS = \ SRC_BINDINGS_ENUMS = \
$(filter $(SRC_PATTERNS), \ $(filter $(SRC_PATTERNS), \
bleio/Address.c \
bleio/Attribute.c \
bleio/ScanEntry.c \
digitalio/Direction.c \ digitalio/Direction.c \
digitalio/DriveMode.c \ digitalio/DriveMode.c \
digitalio/Pull.c \ digitalio/Pull.c \
@ -294,12 +298,6 @@ SRC_BINDINGS_ENUMS += \
help.c \ help.c \
util.c util.c
SRC_BINDINGS_ENUMS += \
$(filter $(SRC_PATTERNS), \
bleio/Address.c \
bleio/ScanEntry.c \
)
# All possible sources are listed here, and are filtered by SRC_PATTERNS. # All possible sources are listed here, and are filtered by SRC_PATTERNS.
SRC_SHARED_MODULE = \ SRC_SHARED_MODULE = \
$(filter $(SRC_PATTERNS), \ $(filter $(SRC_PATTERNS), \
@ -320,6 +318,7 @@ $(filter $(SRC_PATTERNS), \
bitbangio/__init__.c \ bitbangio/__init__.c \
board/__init__.c \ board/__init__.c \
bleio/Address.c \ bleio/Address.c \
bleio/Attribute.c \
bleio/ScanEntry.c \ bleio/ScanEntry.c \
busio/OneWire.c \ busio/OneWire.c \
displayio/Bitmap.c \ displayio/Bitmap.c \

View File

@ -668,6 +668,7 @@ mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed, const mp_obj_t *closed); mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed, const mp_obj_t *closed);
mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items); mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items);
mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items); mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items);
mp_obj_t mp_obj_new_list_from_iter(mp_obj_t iterable);
mp_obj_t mp_obj_new_dict(size_t n_args); mp_obj_t mp_obj_new_dict(size_t n_args);
mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items); mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items);
mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step); mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step);

View File

@ -68,6 +68,11 @@ STATIC mp_obj_t list_extend_from_iter(mp_obj_t list, mp_obj_t iterable) {
return list; return list;
} }
mp_obj_t mp_obj_new_list_from_iter(mp_obj_t iterable) {
mp_obj_t list = mp_obj_new_list(0, NULL);
return list_extend_from_iter(list, iterable);
}
STATIC mp_obj_t list_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { STATIC mp_obj_t list_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
(void)type_in; (void)type_in;
mp_arg_check_num(n_args, kw_args, 0, 1, false); mp_arg_check_num(n_args, kw_args, 0, 1, false);

View File

@ -41,6 +41,12 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_
STATIC mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); STATIC mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf);
STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in); STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in);
const char nibble_to_hex_upper[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'};
const char nibble_to_hex_lower[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
/******************************************************************************/ /******************************************************************************/
/* str */ /* str */

View File

@ -77,6 +77,9 @@ const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, s
mp_obj_t index, bool is_slice); mp_obj_t index, bool is_slice);
const byte *find_subbytes(const byte *haystack, size_t hlen, const byte *needle, size_t nlen, int direction); const byte *find_subbytes(const byte *haystack, size_t hlen, const byte *needle, size_t nlen, int direction);
const char nibble_to_hex_upper[16];
const char nibble_to_hex_lower[16];
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_encode_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_encode_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rfind_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rfind_obj);

View File

@ -53,12 +53,6 @@
//| //|
//| State of the BLE adapter. //| State of the BLE adapter.
//| //|
//| .. attribute:: adapter.address
//|
//| MAC address of the BLE adapter. (read-only)
//|
STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) { STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) {
return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled()); return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled());
} }
@ -80,13 +74,13 @@ const mp_obj_property_t bleio_adapter_enabled_obj = {
(mp_obj_t)&mp_const_none_obj }, (mp_obj_t)&mp_const_none_obj },
}; };
//| .. attribute:: adapter.address
//|
//| MAC address of the BLE adapter. (read-only)
//|
STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) { STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) {
mp_obj_t obj = bleio_address_type.make_new(&bleio_address_type, 1, 0, mp_const_none); return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_address());
bleio_address_obj_t *address = MP_OBJ_TO_PTR(obj);
common_hal_bleio_adapter_get_address(address);
return obj;
} }
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address); MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address);
@ -97,9 +91,29 @@ const mp_obj_property_t bleio_adapter_address_obj = {
(mp_obj_t)&mp_const_none_obj }, (mp_obj_t)&mp_const_none_obj },
}; };
//| .. attribute:: adapter.default_name
//|
//| default_name of the BLE adapter. (read-only)
//| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``,
//| to make it easy to distinguish multiple CircuitPython boards.
//|
STATIC mp_obj_t bleio_adapter_get_default_name(mp_obj_t self) {
return common_hal_bleio_adapter_get_default_name();
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_default_name_obj, bleio_adapter_get_default_name);
const mp_obj_property_t bleio_adapter_default_name_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_adapter_get_default_name_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = { STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) }, { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) },
{ MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_adapter_address_obj) }, { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_adapter_address_obj) },
{ MP_ROM_QSTR(MP_QSTR_default_name), MP_ROM_PTR(&bleio_adapter_default_name_obj) },
}; };
STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table);

View File

@ -34,6 +34,7 @@ const mp_obj_type_t bleio_adapter_type;
extern bool common_hal_bleio_adapter_get_enabled(void); extern bool common_hal_bleio_adapter_get_enabled(void);
extern void common_hal_bleio_adapter_set_enabled(bool enabled); extern void common_hal_bleio_adapter_set_enabled(bool enabled);
extern void common_hal_bleio_adapter_get_address(bleio_address_obj_t *address); extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(void);
extern mp_obj_t common_hal_bleio_adapter_get_default_name(void);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H

View File

@ -83,7 +83,21 @@ STATIC mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args,
//| .. attribute:: address_bytes //| .. attribute:: address_bytes
//| //|
//| The bytes that make up the device address (read-only) //| The bytes that make up the device address (read-only).
//|
//| Note that the ``bytes`` object returned is in little-endian order:
//| The least significant byte is ``address_bytes[0]``. So the address will
//| appear to be reversed if you print the raw ``bytes`` object. If you print
//| or use `str()` on the :py:class:`~bleio.Attribute` object itself, the address will be printed
//| in the expected order. For example:
//|
//| .. code-block:: pycon
//|
//| >>> import bleio
//| >>> bleio.adapter.address
//| <Address c8:1d:f5:ed:a8:35>
//| >>> bleio.adapter.address.address_bytes
//| b'5\xa8\xed\xf5\x1d\xc8'
//| //|
STATIC mp_obj_t bleio_address_get_address_bytes(mp_obj_t self_in) { STATIC mp_obj_t bleio_address_get_address_bytes(mp_obj_t self_in) {
bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -147,18 +161,13 @@ STATIC mp_obj_t bleio_address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_o
STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (kind == PRINT_STR) { mp_buffer_info_t buf_info;
mp_buffer_info_t buf_info; mp_obj_t address_bytes = common_hal_bleio_address_get_address_bytes(self);
mp_obj_t address_bytes = common_hal_bleio_address_get_address_bytes(self); mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ);
mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ);
const uint8_t *buf = (uint8_t *) buf_info.buf; const uint8_t *buf = (uint8_t *) buf_info.buf;
mp_printf(print, mp_printf(print, "<Address %02x:%02x:%02x:%02x:%02x:%02x>",
"%02x:%02x:%02x:%02x:%02x:%02x", buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]);
buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]);
} else {
mp_printf(print, "<Address>");
}
} }
//| .. data:: PUBLIC //| .. data:: PUBLIC
@ -179,13 +188,13 @@ STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
//| A randomly generated address that changes on every connection. //| A randomly generated address that changes on every connection.
//| //|
STATIC const mp_rom_map_elem_t bleio_address_locals_dict_table[] = { STATIC const mp_rom_map_elem_t bleio_address_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_address_bytes), MP_ROM_PTR(&bleio_address_address_bytes_obj) }, { MP_ROM_QSTR(MP_QSTR_address_bytes), MP_ROM_PTR(&bleio_address_address_bytes_obj) },
{ MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&bleio_address_type_obj) }, { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&bleio_address_type_obj) },
// These match the BLE_GAP_ADDR_TYPES values used by the nRF library. // These match the BLE_GAP_ADDR_TYPES values used by the nRF library.
{ MP_ROM_QSTR(MP_QSTR_PUBLIC), MP_OBJ_NEW_SMALL_INT(0) }, { MP_ROM_QSTR(MP_QSTR_PUBLIC), MP_ROM_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_RANDOM_STATIC), MP_OBJ_NEW_SMALL_INT(1) }, { MP_ROM_QSTR(MP_QSTR_RANDOM_STATIC), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_RESOLVABLE), MP_OBJ_NEW_SMALL_INT(2) }, { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_RESOLVABLE), MP_ROM_INT(2) },
{ MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_NON_RESOLVABLE), MP_OBJ_NEW_SMALL_INT(3) }, { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_NON_RESOLVABLE), MP_ROM_INT(3) },
}; };

View File

@ -0,0 +1,94 @@
/*
* 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 "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/UUID.h"
//
//| .. currentmodule:: bleio
//|
//| :class:`Attribute` -- BLE Attribute
//| =========================================================
//|
//| Definitions associated with all BLE attributes: characteristics, descriptors, etc.
//| :py:class:`~bleio.Attribute` is, notionally, a superclass of
//| :py:class:`~Characteristic` and :py:class:`~Descriptor`,
//| but is not defined as a Python superclass of those classes.
//|
//| .. class:: Attribute()
//|
//| You cannot create an instance of :py:class:`~bleio.Attribute`.
//|
STATIC const mp_rom_map_elem_t bleio_attribute_locals_dict_table[] = {
//| .. data:: NO_ACCESS
//|
//| security mode: access not allowed
//|
//| .. data:: OPEN
//|
//| security_mode: no security (link is not encrypted)
//|
//| .. data:: ENCRYPT_NO_MITM
//|
//| security_mode: unauthenticated encryption, without man-in-the-middle protection
//|
//| .. data:: ENCRYPT_WITH_MITM
//|
//| security_mode: authenticated encryption, with man-in-the-middle protection
//|
//| .. data:: LESC_ENCRYPT_WITH_MITM
//|
//| security_mode: LESC encryption, with man-in-the-middle protection
//|
//| .. data:: SIGNED_NO_MITM
//|
//| security_mode: unauthenticated data signing, without man-in-the-middle protection
//|
//| .. data:: SIGNED_WITH_MITM
//|
//| security_mode: authenticated data signing, without man-in-the-middle protection
//|
{ MP_ROM_QSTR(MP_QSTR_NO_ACCESS), MP_ROM_INT(SECURITY_MODE_NO_ACCESS) },
{ MP_ROM_QSTR(MP_QSTR_OPEN), MP_ROM_INT(SECURITY_MODE_OPEN) },
{ MP_ROM_QSTR(MP_QSTR_ENCRYPT_NO_MITM), MP_ROM_INT(SECURITY_MODE_ENC_NO_MITM) },
{ MP_ROM_QSTR(MP_QSTR_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_ENC_WITH_MITM) },
{ MP_ROM_QSTR(MP_QSTR_LESC_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_LESC_ENC_WITH_MITM) },
{ MP_ROM_QSTR(MP_QSTR_SIGNED_NO_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_NO_MITM) },
{ MP_ROM_QSTR(MP_QSTR_SIGNED_WITH_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_WITH_MITM) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_attribute_locals_dict, bleio_attribute_locals_dict_table);
const mp_obj_type_t bleio_attribute_type = {
{ &mp_type_type },
.name = MP_QSTR_Attribute,
.locals_dict = (mp_obj_dict_t*)&bleio_attribute_locals_dict,
};

View File

@ -0,0 +1,38 @@
/*
* 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.
*/
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H
#include "py/obj.h"
#include "shared-module/bleio/Attribute.h"
extern const mp_obj_type_t bleio_attribute_type;
extern void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H

View File

@ -56,12 +56,16 @@
//| //|
//| my_entry = None //| my_entry = None
//| for entry in entries: //| for entry in entries:
//| if entry.name is not None and entry.name == 'MyPeripheral': //| if entry.name is not None and entry.name == 'InterestingPeripheral':
//| my_entry = entry //| my_entry = entry
//| break //| break
//| //|
//| central = bleio.Central(my_entry.address) //| if not my_entry:
//| central.connect(10.0) # timeout after 10 seconds //| raise Exception("'InterestingPeripheral' not found")
//|
//| central = bleio.Central()
//| central.connect(my_entry.address, 10) # timeout after 10 seconds
//| remote_services = central.discover_remote_services()
//| //|
//| .. class:: Central() //| .. class:: Central()
@ -79,24 +83,11 @@ STATIC mp_obj_t bleio_central_make_new(const mp_obj_type_t *type, size_t n_args,
return MP_OBJ_FROM_PTR(self); return MP_OBJ_FROM_PTR(self);
} }
//| .. method:: connect(address, timeout, *, service_uuids=None) //| .. method:: connect(address, timeout, *, service_uuids_whitelist=None)
//| Attempts a connection to the remote peripheral. If the connection is successful, //| Attempts a connection to the remote peripheral.
//| Do BLE discovery for the listed services, to find their handles and characteristics.
//| The attribute `remote_services` will contain a list of all discovered services.
//| //|
//| :param bleio.Address address: The address of the peripheral to connect to //| :param bleio.Address address: The address of the peripheral to connect to
//| :param float/int timeout: Try to connect for timeout seconds. //| :param float/int timeout: Try to connect for timeout seconds.
//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
//| provided by the peripheral that you want to use.
//| The peripheral may provide more services, but services not listed are ignored.
//| If a service in service_uuids is not found during discovery, it will not
//| appear in `remote_services`.
//|
//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
//|
//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
//| service or characteristic to be discovered. (This restriction may be lifted in the future.)
//| //|
STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
@ -105,7 +96,6 @@ STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
{ MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_service_uuids_whitelist, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
}; };
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -119,7 +109,7 @@ STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args
mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
// common_hal_bleio_central_connect() will validate that services is an iterable or None. // common_hal_bleio_central_connect() will validate that services is an iterable or None.
common_hal_bleio_central_connect(self, address, timeout, args[ARG_service_uuids_whitelist].u_obj); common_hal_bleio_central_connect(self, address, timeout);
return mp_const_none; return mp_const_none;
} }
@ -139,6 +129,46 @@ STATIC mp_obj_t bleio_central_disconnect(mp_obj_t self_in) {
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_disconnect_obj, bleio_central_disconnect); STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_disconnect_obj, bleio_central_disconnect);
//| .. method:: discover_remote_services(service_uuids_whitelist=None)
//| Do BLE discovery for all services or for the given service UUIDS,
//| to find their handles and characteristics, and return the discovered services.
//| `Peripheral.connected` must be True.
//|
//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
//| provided by the peripheral that you want to use.
//| The peripheral may provide more services, but services not listed are ignored
//| and will not be returned.
//|
//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
//|
//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered
//| for use. (This restriction may be lifted in the future.)
//|
//| :return: A tuple of services provided by the remote peripheral.
//|
STATIC mp_obj_t bleio_central_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_service_uuids_whitelist };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (!common_hal_bleio_central_get_connected(self)) {
mp_raise_ValueError(translate("Not connected"));
}
return MP_OBJ_FROM_PTR(common_hal_bleio_central_discover_remote_services(
MP_OBJ_FROM_PTR(self),
args[ARG_service_uuids_whitelist].u_obj));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_discover_remote_services_obj, 1, bleio_central_discover_remote_services);
//| .. attribute:: connected //| .. attribute:: connected
//| //|
//| True if connected to a remove peripheral. //| True if connected to a remove peripheral.
@ -157,36 +187,14 @@ const mp_obj_property_t bleio_central_connected_obj = {
(mp_obj_t)&mp_const_none_obj }, (mp_obj_t)&mp_const_none_obj },
}; };
//| .. attribute:: remote_services (read-only)
//|
//| A tuple of services provided by the remote peripheral.
//| If the Central is not connected, an empty tuple will be returned.
//|
STATIC mp_obj_t bleio_central_get_remote_services(mp_obj_t self_in) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Return list as a tuple so user won't be able to change it.
mp_obj_list_t *service_list = common_hal_bleio_central_get_remote_services(self);
return mp_obj_new_tuple(service_list->len, service_list->items);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_get_remote_services_obj, bleio_central_get_remote_services);
const mp_obj_property_t bleio_central_remote_services_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_central_get_remote_services_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
STATIC const mp_rom_map_elem_t bleio_central_locals_dict_table[] = { STATIC const mp_rom_map_elem_t bleio_central_locals_dict_table[] = {
// Methods // Methods
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) }, { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) },
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) }, { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_central_discover_remote_services_obj) },
// Properties // Properties
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_central_connected_obj) }, { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_central_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_remote_services), MP_ROM_PTR(&bleio_central_remote_services_obj) },
}; };
STATIC MP_DEFINE_CONST_DICT(bleio_central_locals_dict, bleio_central_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(bleio_central_locals_dict, bleio_central_locals_dict_table);

View File

@ -28,15 +28,16 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H
#include "py/objtuple.h"
#include "common-hal/bleio/Central.h" #include "common-hal/bleio/Central.h"
#include "common-hal/bleio/Service.h" #include "common-hal/bleio/Service.h"
extern const mp_obj_type_t bleio_central_type; extern const mp_obj_type_t bleio_central_type;
extern void common_hal_bleio_central_construct(bleio_central_obj_t *self); extern void common_hal_bleio_central_construct(bleio_central_obj_t *self);
extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout, mp_obj_t service_uuids); extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout);
extern void common_hal_bleio_central_disconnect(bleio_central_obj_t *self); extern void common_hal_bleio_central_disconnect(bleio_central_obj_t *self);
extern bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self); extern bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self); extern mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H

View File

@ -28,7 +28,9 @@
#include "py/objproperty.h" #include "py/objproperty.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared-bindings/bleio/Attribute.h"
#include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/UUID.h" #include "shared-bindings/bleio/UUID.h"
//| .. currentmodule:: bleio //| .. currentmodule:: bleio
@ -40,30 +42,36 @@
//| and writing of the characteristic's value. //| and writing of the characteristic's value.
//| //|
//| //|
//| .. class:: Characteristic(uuid, *, broadcast=False, indicate=False, notify=False, read=False, write=False, write_no_response=False) //| .. class:: Characteristic(uuid, *, properties=0, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, descriptors=None)
//| //|
//| Create a new Characteristic object identified by the specified UUID. //| Create a new Characteristic object identified by the specified UUID.
//| //|
//| :param bleio.UUID uuid: The uuid of the characteristic //| :param bleio.UUID uuid: The uuid of the characteristic
//| :param bool broadcast: Allowed in advertising packets //| :param int properties: bitmask of these values bitwise-or'd together: `BROADCAST`, `INDICATE`,
//| :param bool indicate: Server will indicate to the client when the value is set and wait for a response //| `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`
//| :param bool notify: Server will notify the client when the value is set //| :param int read_perm: Specifies whether the characteristic can be read by a client, and if so, which
//| :param bool read: Clients may read this characteristic //| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`,
//| :param bool write: Clients may write this characteristic; a response will be sent back //| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`,
//| :param bool write_no_response: Clients may write this characteristic; no response will be sent back //| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`.
//| :param int write_perm: Specifies whether the characteristic can be written by a client, and if so, which
//| security mode is required. Values allowed are the same as ``read_perm``.
//| :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed is
//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum
//| number of data bytes that fit in a single BLE 4.x ATT packet.
//| :param bool fixed_length: True if the characteristic value is of fixed length.
//| :param iterable descriptors: BLE descriptors for this characteristic.
//| //|
STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { enum { ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm,
ARG_uuid, ARG_broadcast, ARG_indicate, ARG_notify, ARG_read, ARG_write, ARG_write_no_response, ARG_max_length, ARG_fixed_length, ARG_descriptors };
};
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
{ MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_broadcast, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_properties, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_indicate, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} },
{ MP_QSTR_notify, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} },
{ MP_QSTR_read, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} },
{ MP_QSTR_write, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_write_no_response, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_descriptors, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_none} },
}; };
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -76,129 +84,69 @@ STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t
} }
bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj);
const bleio_characteristic_properties_t properties = args[ARG_properties].u_int;
if (properties & ~CHAR_PROP_ALL) {
mp_raise_ValueError(translate("Invalid properties"));
}
const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(read_perm);
const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(write_perm);
mp_obj_t descriptors = args[ARG_descriptors].u_obj;
if (descriptors == mp_const_none) {
descriptors = mp_const_empty_tuple;
}
bleio_characteristic_obj_t *self = m_new_obj(bleio_characteristic_obj_t); bleio_characteristic_obj_t *self = m_new_obj(bleio_characteristic_obj_t);
self->base.type = &bleio_characteristic_type; self->base.type = &bleio_characteristic_type;
bleio_characteristic_properties_t properties; // Copy the descriptors list and validate its items.
mp_obj_t desc_list_obj = mp_obj_new_list(0, NULL);
mp_obj_list_t *desc_list = MP_OBJ_TO_PTR(desc_list_obj);
properties.broadcast = args[ARG_broadcast].u_bool; // If descriptors is not an iterable, an exception will be thrown.
properties.indicate = args[ARG_indicate].u_bool; mp_obj_iter_buf_t iter_buf;
properties.notify = args[ARG_notify].u_bool; mp_obj_t descriptors_iter = mp_getiter(descriptors, &iter_buf);
properties.read = args[ARG_read].u_bool;
properties.write = args[ARG_write].u_bool;
properties.write_no_response = args[ARG_write_no_response].u_bool;
// Initialize, with an empty descriptor list. mp_obj_t descriptor_obj;
common_hal_bleio_characteristic_construct(self, uuid, properties, mp_obj_new_list(0, NULL)); while ((descriptor_obj = mp_iternext(descriptors_iter)) != MP_OBJ_STOP_ITERATION) {
if (!MP_OBJ_IS_TYPE(descriptor_obj, &bleio_descriptor_type)) {
mp_raise_ValueError(translate("descriptors includes an object that is not a Descriptors"));
}
bleio_descriptor_obj_t *descriptor = MP_OBJ_TO_PTR(descriptor_obj);
if (common_hal_bleio_descriptor_get_characteristic(descriptor) != MP_OBJ_NULL) {
mp_raise_ValueError(translate("Descriptor is already attached to a Characteristic"));
}
mp_obj_list_append(desc_list_obj, descriptor_obj);
}
// Range checking on max_length arg is done by the common_hal layer, because
// it may vary depending on underlying BLE implementation.
common_hal_bleio_characteristic_construct(self, uuid, properties,
read_perm, write_perm,
args[ARG_max_length].u_int, args[ARG_fixed_length].u_bool,
desc_list);
return MP_OBJ_FROM_PTR(self); return MP_OBJ_FROM_PTR(self);
} }
//| .. attribute:: broadcast //| .. attribute:: properties
//| //|
//| A `bool` specifying if the characteristic allows broadcasting its value. (read-only) //| An int bitmask representing which properties are set.
//| //|
STATIC mp_obj_t bleio_characteristic_get_broadcast(mp_obj_t self_in) { STATIC mp_obj_t bleio_characteristic_get_properties(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_characteristic_get_properties(self).broadcast); return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_get_properties(self));
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_broadcast_obj, bleio_characteristic_get_broadcast); STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_properties_obj, bleio_characteristic_get_properties);
const mp_obj_property_t bleio_characteristic_broadcast_obj = { const mp_obj_property_t bleio_characteristic_properties_obj = {
.base.type = &mp_type_property, .base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_broadcast_obj, .proxy = { (mp_obj_t)&bleio_characteristic_get_properties_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: indicate
//|
//| A `bool` specifying if the characteristic allows indicating its value. (read-only)
//|
STATIC mp_obj_t bleio_characteristic_get_indicate(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_characteristic_get_properties(self).indicate);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_indicate_obj, bleio_characteristic_get_indicate);
const mp_obj_property_t bleio_characteristic_indicate_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_indicate_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: notify
//|
//| A `bool` specifying if the characteristic allows notifying its value. (read-only)
//|
STATIC mp_obj_t bleio_characteristic_get_notify(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_characteristic_get_properties(self).notify);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_notify_obj, bleio_characteristic_get_notify);
const mp_obj_property_t bleio_characteristic_notify_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_notify_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: read
//|
//| A `bool` specifying if the characteristic allows reading its value. (read-only)
//|
STATIC mp_obj_t bleio_characteristic_get_read(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_characteristic_get_properties(self).read);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_read_obj, bleio_characteristic_get_read);
const mp_obj_property_t bleio_characteristic_read_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_read_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: write
//|
//| A `bool` specifying if the characteristic allows writing to its value. (read-only)
//|
STATIC mp_obj_t bleio_characteristic_get_write(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_characteristic_get_properties(self).write);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_write_obj, bleio_characteristic_get_write);
const mp_obj_property_t bleio_characteristic_write_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_write_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: write_no_response
//|
//| A `bool` specifying if the characteristic allows writing to its value without response. (read-only)
//|
STATIC mp_obj_t bleio_characteristic_get_write_no_response(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_characteristic_get_properties(self).write_no_response);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_write_no_response_obj, bleio_characteristic_get_write_no_response);
const mp_obj_property_t bleio_characteristic_write_no_response_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_write_no_response_obj,
(mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj }, (mp_obj_t)&mp_const_none_obj },
}; };
@ -225,9 +173,7 @@ const mp_obj_property_t bleio_characteristic_uuid_obj = {
//| .. attribute:: value //| .. attribute:: value
//| //|
//| The value of this characteristic. The value can be written to if the `write` property allows it. //| The value of this characteristic.
//| If the `read` property allows it, the value can be read. If the `notify` property is set, writing
//| to the value will generate a BLE notification.
//| //|
STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) { STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -274,6 +220,24 @@ const mp_obj_property_t bleio_characteristic_descriptors_obj = {
(mp_obj_t)&mp_const_none_obj }, (mp_obj_t)&mp_const_none_obj },
}; };
//| .. attribute:: service (read-only)
//|
//| The Service this Characteristic is a part of. None if not yet assigned to a Service.
//|
STATIC mp_obj_t bleio_characteristic_get_service(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_characteristic_get_service(self);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_service_obj, bleio_characteristic_get_service);
const mp_obj_property_t bleio_characteristic_service_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_service_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. method:: set_cccd(*, notify=False, indicate=False) //| .. method:: set_cccd(*, notify=False, indicate=False)
//| //|
//| Set the remote characteristic's CCCD to enable or disable notification and indication. //| Set the remote characteristic's CCCD to enable or disable notification and indication.
@ -301,16 +265,43 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_ch
STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_broadcast), MP_ROM_PTR(&bleio_characteristic_broadcast_obj) }, { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_get_properties) },
{ MP_ROM_QSTR(MP_QSTR_descriptors), MP_ROM_PTR(&bleio_characteristic_descriptors_obj) }, { MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) },
{ MP_ROM_QSTR(MP_QSTR_indicate), MP_ROM_PTR(&bleio_characteristic_indicate_obj) }, { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) },
{ MP_ROM_QSTR(MP_QSTR_notify), MP_ROM_PTR(&bleio_characteristic_notify_obj) }, { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&bleio_characteristic_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) }, // Bitmask constants to represent properties
{ MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, //| .. data:: BROADCAST
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) }, //|
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&bleio_characteristic_write_obj) }, //| property: allowed in advertising packets
{ MP_ROM_QSTR(MP_QSTR_write_no_response), MP_ROM_PTR(&bleio_characteristic_write_no_response_obj) }, //|
//| .. data:: INDICATE
//|
//| property: server will indicate to the client when the value is set and wait for a response
//|
//| .. data:: NOTIFY
//|
//| property: server will notify the client when the value is set
//|
//| .. data:: READ
//|
//| property: clients may read this characteristic
//|
//| .. data:: WRITE
//|
//| property: clients may write this characteristic; a response will be sent back
//|
//| .. data:: WRITE_NO_RESPONSE
//|
//| property: clients may write this characteristic; no response will be sent back
//|
{ MP_ROM_QSTR(MP_QSTR_BROADCAST), MP_ROM_INT(CHAR_PROP_BROADCAST) },
{ MP_ROM_QSTR(MP_QSTR_INDICATE), MP_ROM_INT(CHAR_PROP_INDICATE) },
{ MP_ROM_QSTR(MP_QSTR_NOTIFY), MP_ROM_INT(CHAR_PROP_NOTIFY) },
{ MP_ROM_QSTR(MP_QSTR_READ), MP_ROM_INT(CHAR_PROP_READ) },
{ MP_ROM_QSTR(MP_QSTR_WRITE), MP_ROM_INT(CHAR_PROP_WRITE) },
{ MP_ROM_QSTR(MP_QSTR_WRITE_NO_RESPONSE), MP_ROM_INT(CHAR_PROP_WRITE_NO_RESPONSE) },
}; };
STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_locals_dict, bleio_characteristic_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_locals_dict, bleio_characteristic_locals_dict_table);
@ -330,5 +321,5 @@ const mp_obj_type_t bleio_characteristic_type = {
.name = MP_QSTR_Characteristic, .name = MP_QSTR_Characteristic,
.make_new = bleio_characteristic_make_new, .make_new = bleio_characteristic_make_new,
.print = bleio_characteristic_print, .print = bleio_characteristic_print,
.locals_dict = (mp_obj_dict_t*)&bleio_characteristic_locals_dict .locals_dict = (mp_obj_dict_t*)&bleio_characteristic_locals_dict,
}; };

View File

@ -28,17 +28,19 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H
#include "shared-bindings/bleio/Attribute.h"
#include "shared-module/bleio/Characteristic.h" #include "shared-module/bleio/Characteristic.h"
#include "common-hal/bleio/Characteristic.h" #include "common-hal/bleio/Characteristic.h"
extern const mp_obj_type_t bleio_characteristic_type; extern const mp_obj_type_t bleio_characteristic_type;
extern 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); extern 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);
extern mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self); extern mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self);
extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo);
extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self); extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self);
extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self); extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self); extern mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self);
extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self);
extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H

View File

@ -28,6 +28,7 @@
#include "py/objproperty.h" #include "py/objproperty.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared-bindings/bleio/Attribute.h"
#include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/UUID.h" #include "shared-bindings/bleio/UUID.h"
@ -41,23 +42,30 @@
//| information about the characteristic. //| information about the characteristic.
//| //|
//| .. class:: Descriptor(uuid) //| .. class:: Descriptor(uuid, *, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`)
//| //|
//| Create a new descriptor object with the UUID uuid. //| Create a new descriptor object with the UUID uuid
//| .. attribute:: handle
//| //|
//| The descriptor handle. (read-only) //| :param bleio.UUID uuid: The uuid of the descriptor
//| //| :param int read_perm: Specifies whether the descriptor can be read by a client, and if so, which
//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`,
//| .. attribute:: uuid //| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`,
//| //| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`.
//| The descriptor uuid. (read-only) //| :param int write_perm: Specifies whether the descriptor can be written by a client, and if so, which
//| security mode is required. Values allowed are the same as ``read_perm``.
//| :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed is
//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum
//| number of data bytes that fit in a single BLE 4.x ATT packet.
//| :param bool fixed_length: True if the characteristic value is of fixed length.
//| //|
STATIC mp_obj_t bleio_descriptor_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { STATIC mp_obj_t bleio_descriptor_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_uuid }; enum { ARG_uuid, ARG_read_perm, ARG_write_perm, ARG_max_length, ARG_fixed_length };
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
{ MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN } },
{ MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN } },
{ MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} },
{ MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} },
}; };
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -69,28 +77,28 @@ STATIC mp_obj_t bleio_descriptor_make_new(const mp_obj_type_t *type, size_t n_ar
mp_raise_ValueError(translate("Expected a UUID")); mp_raise_ValueError(translate("Expected a UUID"));
} }
const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(read_perm);
const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(write_perm);
bleio_descriptor_obj_t *self = m_new_obj(bleio_descriptor_obj_t); bleio_descriptor_obj_t *self = m_new_obj(bleio_descriptor_obj_t);
self->base.type = type; self->base.type = type;
bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_arg); bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_arg);
common_hal_bleio_descriptor_construct(self, uuid);
// Range checking on max_length arg is done by the common_hal layer, because
// it may vary depending on underlying BLE implementation.
common_hal_bleio_descriptor_construct(self, uuid, read_perm, write_perm,
args[ARG_max_length].u_int, args[ARG_fixed_length].u_bool);
return MP_OBJ_FROM_PTR(self); return MP_OBJ_FROM_PTR(self);
} }
STATIC mp_obj_t bleio_descriptor_get_handle(mp_obj_t self_in) { //| .. attribute:: uuid
bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); //|
//| The descriptor uuid. (read-only)
return mp_obj_new_int(common_hal_bleio_descriptor_get_handle(self)); //|
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_handle_obj, bleio_descriptor_get_handle);
const mp_obj_property_t bleio_descriptor_handle_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&bleio_descriptor_get_handle_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj},
};
STATIC mp_obj_t bleio_descriptor_get_uuid(mp_obj_t self_in) { STATIC mp_obj_t bleio_descriptor_get_uuid(mp_obj_t self_in) {
bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -106,56 +114,59 @@ const mp_obj_property_t bleio_descriptor_uuid_obj = {
(mp_obj_t)&mp_const_none_obj}, (mp_obj_t)&mp_const_none_obj},
}; };
//| .. attribute:: characteristic (read-only)
//|
//| The Characteristic this Descriptor is a part of. None if not yet assigned to a Characteristic.
//|
STATIC mp_obj_t bleio_descriptor_get_characteristic(mp_obj_t self_in) {
bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_descriptor_get_characteristic(self);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_characteristic_obj, bleio_descriptor_get_characteristic);
const mp_obj_property_t bleio_descriptor_characteristic_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_descriptor_get_characteristic_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: value
//|
//| The value of this descriptor.
//|
STATIC mp_obj_t bleio_descriptor_get_value(mp_obj_t self_in) {
bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_descriptor_get_value(self);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_value_obj, bleio_descriptor_get_value);
STATIC mp_obj_t bleio_descriptor_set_value(mp_obj_t self_in, mp_obj_t value_in) {
bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ);
common_hal_bleio_descriptor_set_value(self, &bufinfo);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_descriptor_set_value_obj, bleio_descriptor_set_value);
const mp_obj_property_t bleio_descriptor_value_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_descriptor_get_value_obj,
(mp_obj_t)&bleio_descriptor_set_value_obj,
(mp_obj_t)&mp_const_none_obj },
};
STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = { STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = {
// Properties // Properties
{ MP_ROM_QSTR(MP_QSTR_handle), MP_ROM_PTR(&bleio_descriptor_handle_obj) },
{ MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_descriptor_uuid_obj) }, { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_descriptor_uuid_obj) },
{ MP_ROM_QSTR(MP_QSTR_characteristic), MP_ROM_PTR(&bleio_descriptor_characteristic_obj) },
// Static variables { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_descriptor_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_CHARACTERISTIC_EXTENDED_PROPERTIES),
MP_ROM_INT(DESCRIPTOR_UUID_CHARACTERISTIC_EXTENDED_PROPERTIES) },
{ MP_ROM_QSTR(MP_QSTR_CHARACTERISTIC_USER_DESCRIPTION),
MP_ROM_INT(DESCRIPTOR_UUID_CHARACTERISTIC_USER_DESCRIPTION) },
{ MP_ROM_QSTR(MP_QSTR_CLIENT_CHARACTERISTIC_CONFIGURATION),
MP_ROM_INT(DESCRIPTOR_UUID_CLIENT_CHARACTERISTIC_CONFIGURATION) },
{ MP_ROM_QSTR(MP_QSTR_SERVER_CHARACTERISTIC_CONFIGURATION),
MP_ROM_INT(DESCRIPTOR_UUID_SERVER_CHARACTERISTIC_CONFIGURATION) },
{ MP_ROM_QSTR(MP_QSTR_CHARACTERISTIC_PRESENTATION_FORMAT),
MP_ROM_INT(DESCRIPTOR_UUID_CHARACTERISTIC_PRESENTATION_FORMAT) },
{ MP_ROM_QSTR(MP_QSTR_CHARACTERISTIC_AGGREGATE_FORMAT),
MP_ROM_INT(DESCRIPTOR_UUID_CHARACTERISTIC_AGGREGATE_FORMAT) },
{ MP_ROM_QSTR(MP_QSTR_VALID_RANGE),
MP_ROM_INT(DESCRIPTOR_UUID_VALID_RANGE) },
{ MP_ROM_QSTR(MP_QSTR_EXTERNAL_REPORT_REFERENCE),
MP_ROM_INT(DESCRIPTOR_UUID_EXTERNAL_REPORT_REFERENCE) },
{ MP_ROM_QSTR(MP_QSTR_REPORT_REFERENCE),
MP_ROM_INT(DESCRIPTOR_UUID_REPORT_REFERENCE) },
{ MP_ROM_QSTR(MP_QSTR_NUMBER_OF_DIGITALS),
MP_ROM_INT(DESCRIPTOR_UUID_NUMBER_OF_DIGITALS) },
{ MP_ROM_QSTR(MP_QSTR_VALUE_TRIGGER_SETTING),
MP_ROM_INT(DESCRIPTOR_UUID_VALUE_TRIGGER_SETTING) },
{ MP_ROM_QSTR(MP_QSTR_ENVIRONMENTAL_SENSING_CONFIGURATION),
MP_ROM_INT(DESCRIPTOR_UUID_ENVIRONMENTAL_SENSING_CONFIGURATION) },
{ MP_ROM_QSTR(MP_QSTR_ENVIRONMENTAL_SENSING_MEASUREMENT ),
MP_ROM_INT(DESCRIPTOR_UUID_ENVIRONMENTAL_SENSING_MEASUREMENT) },
{ MP_ROM_QSTR(MP_QSTR_ENVIRONMENTAL_SENSING_TRIGGER_SETTING),
MP_ROM_INT(DESCRIPTOR_UUID_ENVIRONMENTAL_SENSING_TRIGGER_SETTING) },
{ MP_ROM_QSTR(MP_QSTR_TIME_TRIGGER_SETTING),
MP_ROM_INT(DESCRIPTOR_UUID_TIME_TRIGGER_SETTING) },
}; };
STATIC MP_DEFINE_CONST_DICT(bleio_descriptor_locals_dict, bleio_descriptor_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(bleio_descriptor_locals_dict, bleio_descriptor_locals_dict_table);

View File

@ -28,31 +28,16 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H
#include "shared-module/bleio/Attribute.h"
#include "common-hal/bleio/Descriptor.h" #include "common-hal/bleio/Descriptor.h"
#include "common-hal/bleio/UUID.h" #include "common-hal/bleio/UUID.h"
enum {
DESCRIPTOR_UUID_CHARACTERISTIC_EXTENDED_PROPERTIES = 0x2900,
DESCRIPTOR_UUID_CHARACTERISTIC_USER_DESCRIPTION = 0x2901,
DESCRIPTOR_UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = 0x2902,
DESCRIPTOR_UUID_SERVER_CHARACTERISTIC_CONFIGURATION = 0x2903,
DESCRIPTOR_UUID_CHARACTERISTIC_PRESENTATION_FORMAT = 0x2904,
DESCRIPTOR_UUID_CHARACTERISTIC_AGGREGATE_FORMAT = 0x2905,
DESCRIPTOR_UUID_VALID_RANGE = 0x2906,
DESCRIPTOR_UUID_EXTERNAL_REPORT_REFERENCE = 0x2907,
DESCRIPTOR_UUID_REPORT_REFERENCE = 0x2908,
DESCRIPTOR_UUID_NUMBER_OF_DIGITALS = 0x2909,
DESCRIPTOR_UUID_VALUE_TRIGGER_SETTING = 0x290A,
DESCRIPTOR_UUID_ENVIRONMENTAL_SENSING_CONFIGURATION = 0x290B,
DESCRIPTOR_UUID_ENVIRONMENTAL_SENSING_MEASUREMENT = 0x290C,
DESCRIPTOR_UUID_ENVIRONMENTAL_SENSING_TRIGGER_SETTING = 0x290D,
DESCRIPTOR_UUID_TIME_TRIGGER_SETTING = 0x290E,
};
extern const mp_obj_type_t bleio_descriptor_type; extern const mp_obj_type_t bleio_descriptor_type;
extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid); extern 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);
extern mp_int_t common_hal_bleio_descriptor_get_handle(bleio_descriptor_obj_t *self); extern bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self);
extern mp_obj_t common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self); extern bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self);
extern mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self);
extern void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H

View File

@ -44,8 +44,6 @@
#include "common-hal/bleio/Peripheral.h" #include "common-hal/bleio/Peripheral.h"
static const char default_name[] = "CIRCUITPY";
#define ADV_INTERVAL_DEFAULT (1.0f) #define ADV_INTERVAL_DEFAULT (1.0f)
#define ADV_INTERVAL_MIN (0.0020f) #define ADV_INTERVAL_MIN (0.0020f)
#define ADV_INTERVAL_MIN_STRING "0.0020" #define ADV_INTERVAL_MIN_STRING "0.0020"
@ -80,14 +78,14 @@ static const char default_name[] = "CIRCUITPY";
//| # Wait for connection. //| # Wait for connection.
//| pass //| pass
//| //|
//| .. class:: Peripheral(services=(), \*, name='CIRCUITPY') //| .. class:: Peripheral(services=(), \*, name=None)
//| //|
//| Create a new Peripheral object. //| Create a new Peripheral object.
//| //|
//| :param iterable services: the Service objects representing services available from this peripheral, if any. //| :param iterable services: the Service objects representing services available from this peripheral, if any.
//| A non-connectable peripheral will have no services. //| A non-connectable peripheral will have no services.
//| :param str name: The name used when advertising this peripheral. Use ``None`` when a name is not needed, //| :param str name: The name used when advertising this peripheral. If name is None,
//| such as when the peripheral is a beacon //| bleio.adapter.default_name will be used.
//| //|
STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_services, ARG_name }; enum { ARG_services, ARG_name };
@ -107,33 +105,30 @@ STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_ar
self->base.type = &bleio_peripheral_type; self->base.type = &bleio_peripheral_type;
// Copy the services list and validate its items. // Copy the services list and validate its items.
mp_obj_t service_list_obj = mp_obj_new_list(0, NULL); mp_obj_t services_list_obj = mp_obj_new_list(0, NULL);
mp_obj_list_t *service_list = MP_OBJ_FROM_PTR(service_list_obj); mp_obj_list_t *services_list = MP_OBJ_FROM_PTR(services_list_obj);
mp_obj_t service; mp_obj_t service;
while ((service = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { while ((service = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if (!MP_OBJ_IS_TYPE(service, &bleio_service_type)) { if (!MP_OBJ_IS_TYPE(service, &bleio_service_type)) {
mp_raise_ValueError(translate("non-Service found in services")); mp_raise_ValueError(translate("non-Service found in services"));
} }
mp_obj_list_append(service_list, service); mp_obj_list_append(services_list, service);
} }
const mp_obj_t name = args[ARG_name].u_obj; mp_obj_t name = args[ARG_name].u_obj;
mp_obj_t name_str;
if (name == MP_OBJ_NULL || name == mp_const_none) { if (name == MP_OBJ_NULL || name == mp_const_none) {
name_str = mp_obj_new_str(default_name, strlen(default_name)); name = common_hal_bleio_adapter_get_default_name();
} else if (MP_OBJ_IS_STR(name)) { } else if (!MP_OBJ_IS_STR(name)) {
name_str = name;
} else {
mp_raise_ValueError(translate("name must be a string")); mp_raise_ValueError(translate("name must be a string"));
} }
common_hal_bleio_peripheral_construct(self, service_list, name_str); common_hal_bleio_peripheral_construct(self, services_list, name);
return MP_OBJ_FROM_PTR(self); return MP_OBJ_FROM_PTR(self);
} }
//| .. attribute:: connected //| .. attribute:: connected (read-only)
//| //|
//| True if connected to a BLE Central device. //| True if connected to a BLE Central device.
//| //|
@ -158,8 +153,8 @@ const mp_obj_property_t bleio_peripheral_connected_obj = {
STATIC mp_obj_t bleio_peripheral_get_services(mp_obj_t self_in) { STATIC mp_obj_t bleio_peripheral_get_services(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Return list as a tuple so user won't be able to change it. // Return list as a tuple so user won't be able to change it.
mp_obj_list_t *service_list = common_hal_bleio_peripheral_get_service_list(self); mp_obj_list_t *services_list = common_hal_bleio_peripheral_get_services(self);
return mp_obj_new_tuple(service_list->len, service_list->items); return mp_obj_new_tuple(services_list->len, services_list->items);
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services); STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services);
@ -265,16 +260,75 @@ STATIC mp_obj_t bleio_peripheral_disconnect(mp_obj_t self_in) {
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_disconnect_obj, bleio_peripheral_disconnect); STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_disconnect_obj, bleio_peripheral_disconnect);
//| .. method:: discover_remote_services(service_uuids_whitelist=None)
//| Do BLE discovery for all services or for the given service UUIDS,
//| to find their handles and characteristics, and return the discovered services.
//| `Peripheral.connected` must be True.
//|
//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
//| provided by the peripheral that you want to use.
//| The peripheral may provide more services, but services not listed are ignored
//| and will not be returned.
//|
//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
//|
//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered
//| for use. (This restriction may be lifted in the future.)
//|
//| Thought it is unusual for a peripheral to act as a BLE client, it can do so, and
//| needs to be able to do discovery on its peer (a central).
//| Examples include a peripheral accessing a central that provides Current Time Service,
//| Apple Notification Center Service, or Battery Service.
//|
//| :return: A tuple of services provided by the remote central.
//|
STATIC mp_obj_t bleio_peripheral_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_service_uuids_whitelist };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (!common_hal_bleio_peripheral_get_connected(self)) {
mp_raise_ValueError(translate("Not connected"));
}
return MP_OBJ_FROM_PTR(common_hal_bleio_peripheral_discover_remote_services(
MP_OBJ_FROM_PTR(self),
args[ARG_service_uuids_whitelist].u_obj));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_discover_remote_services_obj, 1, bleio_peripheral_discover_remote_services);
//| .. method:: pair()
//|
//| Request pairing with connected central.
STATIC mp_obj_t bleio_peripheral_pair(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_peripheral_pair(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_pair_obj, bleio_peripheral_pair);
STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = { STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = {
// Methods // Methods
{ MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) }, { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) }, { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) },
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) }, { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_peripheral_discover_remote_services_obj) },
{ MP_ROM_QSTR(MP_QSTR_pair) , MP_ROM_PTR(&bleio_peripheral_pair_obj) },
// Properties // Properties
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) }, { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) }, { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) },
{ MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) }, { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) },
}; };
STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table);

View File

@ -28,16 +28,19 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H
#include "py/objtuple.h"
#include "common-hal/bleio/Peripheral.h" #include "common-hal/bleio/Peripheral.h"
extern const mp_obj_type_t bleio_peripheral_type; extern const mp_obj_type_t bleio_peripheral_type;
extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *service_list, mp_obj_t name); extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *service_list, mp_obj_t name);
extern mp_obj_list_t *common_hal_bleio_peripheral_get_service_list(bleio_peripheral_obj_t *self); extern mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self);
extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self); extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self);
extern mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self); extern mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self);
extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, float interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo); extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, float interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
extern void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *device); extern void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *device);
extern void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *device); extern void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *device);
extern mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist);
extern void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *device);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H

View File

@ -43,12 +43,16 @@
//| .. class:: Service(uuid, characteristics, *, secondary=False) //| .. class:: Service(uuid, characteristics, *, secondary=False)
//| //|
//| Create a new Service object identified by the specified UUID. //| Create a new Service object identified by the specified UUID.
//|
//| To mark the service as secondary, pass `True` as :py:data:`secondary`. //| To mark the service as secondary, pass `True` as :py:data:`secondary`.
//| //|
//| :param bleio.UUID uuid: The uuid of the service //| :param bleio.UUID uuid: The uuid of the service
//| :param iterable characteristics: the Characteristic objects for this service //| :param iterable characteristics: the Characteristic objects for this service
//| :param bool secondary: If the service is a secondary one //| :param bool secondary: If the service is a secondary one
//| //|
//| A Service may be remote (:py:data:`remote` is ``True``), but a remote Service
//| cannot be constructed directly. It is created by `Central.discover_remote_services()`
//| or `Peripheral.discover_remote_services()`.
STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_uuid, ARG_characteristics, ARG_secondary }; enum { ARG_uuid, ARG_characteristics, ARG_secondary };
@ -92,6 +96,9 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args,
// The descriptor base UUID doesn't match the characteristic base UUID. // The descriptor base UUID doesn't match the characteristic base UUID.
mp_raise_ValueError(translate("Characteristic UUID doesn't match Service UUID")); mp_raise_ValueError(translate("Characteristic UUID doesn't match Service UUID"));
} }
if (common_hal_bleio_characteristic_get_service(characteristic) != MP_OBJ_NULL) {
mp_raise_ValueError(translate("Characteristic is already attached to a Service"));
}
mp_obj_list_append(char_list_obj, characteristic_obj); mp_obj_list_append(char_list_obj, characteristic_obj);
} }
@ -119,6 +126,24 @@ const mp_obj_property_t bleio_service_characteristics_obj = {
(mp_obj_t)&mp_const_none_obj }, (mp_obj_t)&mp_const_none_obj },
}; };
//| .. attribute:: remote
//|
//| True if this is a service provided by a remote device. (read-only)
//|
STATIC mp_obj_t bleio_service_get_remote(mp_obj_t self_in) {
bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_service_get_is_remote(self));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_remote_obj, bleio_service_get_remote);
const mp_obj_property_t bleio_service_remote_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_service_get_remote_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: secondary //| .. attribute:: secondary
//| //|
//| True if this is a secondary service. (read-only) //| True if this is a secondary service. (read-only)

View File

@ -35,6 +35,7 @@ const mp_obj_type_t bleio_service_type;
extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, mp_obj_list_t *characteristic_list, bool is_secondary); extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, mp_obj_list_t *characteristic_list, bool is_secondary);
extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self); extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self); extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self);
extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self);
extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self); extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self);
extern void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self); extern void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self);

View File

@ -28,24 +28,30 @@
#include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/Address.h"
#include "shared-bindings/bleio/Attribute.h"
#include "shared-bindings/bleio/Central.h" #include "shared-bindings/bleio/Central.h"
#include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/CharacteristicBuffer.h" #include "shared-bindings/bleio/CharacteristicBuffer.h"
// #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/Peripheral.h" #include "shared-bindings/bleio/Peripheral.h"
#include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/ScanEntry.h"
#include "shared-bindings/bleio/Scanner.h" #include "shared-bindings/bleio/Scanner.h"
#include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/Service.h"
#include "shared-bindings/bleio/UUID.h" #include "shared-bindings/bleio/UUID.h"
//| :mod:`bleio` --- Bluetooth Low Energy functionality //| :mod:`bleio` --- Bluetooth Low Energy (BLE) communication
//| ================================================================ //| ================================================================
//| //|
//| .. module:: bleio //| .. module:: bleio
//| :synopsis: Bluetooth Low Energy functionality //| :synopsis: Bluetooth Low Energy functionality
//| :platform: nRF //| :platform: nRF
//| //|
//| The `bleio` module contains methods for managing the BLE adapter. //| The `bleio` module provides necessary low-level functionality for communicating
//| using Bluetooth Low Energy (BLE). We recommend you use `bleio` in conjunction
//| with the `adafruit_ble <https://circuitpython.readthedocs.io/projects/ble/en/latest/>`_
//| CircuitPython library, which builds on `bleio`, and
//| provides higher-level convenience functionality, including predefined beacons, clients,
//| servers.
//| //|
//| Libraries //| Libraries
//| //|
@ -54,6 +60,7 @@
//| //|
//| Address //| Address
//| Adapter //| Adapter
//| Attribute
//| Central //| Central
//| Characteristic //| Characteristic
//| CharacteristicBuffer //| CharacteristicBuffer
@ -72,20 +79,21 @@
//| //|
STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) }, { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) },
{ MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) },
{ MP_ROM_QSTR(MP_QSTR_Central), MP_ROM_PTR(&bleio_central_type) }, { MP_ROM_QSTR(MP_QSTR_Attribute), MP_ROM_PTR(&bleio_attribute_type) },
{ MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, { MP_ROM_QSTR(MP_QSTR_Central), MP_ROM_PTR(&bleio_central_type) },
{ MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), MP_ROM_PTR(&bleio_characteristic_buffer_type) }, { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) },
// { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, { MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), MP_ROM_PTR(&bleio_characteristic_buffer_type) },
{ MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&bleio_peripheral_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) },
{ MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&bleio_peripheral_type) },
{ MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) },
{ MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) },
{ MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) },
{ MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) },
// Properties // Properties
{ MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) }, { MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) },
}; };
STATIC MP_DEFINE_CONST_DICT(bleio_module_globals, bleio_module_globals_table); STATIC MP_DEFINE_CONST_DICT(bleio_module_globals, bleio_module_globals_table);

View File

@ -29,8 +29,24 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H
#include "py/objlist.h"
#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Adapter.h"
#include "common-hal/bleio/Adapter.h" #include "common-hal/bleio/Adapter.h"
extern const super_adapter_obj_t common_hal_bleio_adapter_obj; extern const super_adapter_obj_t common_hal_bleio_adapter_obj;
extern void common_hal_bleio_check_connected(uint16_t conn_handle);
extern uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device);
extern mp_obj_list_t *common_hal_bleio_device_get_remote_services_list(mp_obj_t device);
extern void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist);
extern mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle);
extern void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo);
extern void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H

View File

@ -99,16 +99,17 @@ mp_obj_t struct_time_make_new(const mp_obj_type_t *type, size_t n_args, const mp
//| //|
//| Structure used to capture a date and time. Note that it takes a tuple! //| Structure used to capture a date and time. Note that it takes a tuple!
//| //|
//| :param Tuple[tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst] time_tuple: Tuple of time info. //| :param tuple time_tuple: Tuple of time info: ``(tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)``
//| * the year, 2017 for example //|
//| * the month, range [1, 12] //| * ``tm_year``: the year, 2017 for example
//| * the day of the month, range [1, 31] //| * ``tm_month``: the month, range [1, 12]
//| * the hour, range [0, 23] //| * ``tm_mday``: the day of the month, range [1, 31]
//| * the minute, range [0, 59] //| * ``tm_hour``: the hour, range [0, 23]
//| * the second, range [0, 61] //| * ``tm_minute``: the minute, range [0, 59]
//| * the day of the week, range [0, 6], Monday is 0 //| * ``tm_sec``: the second, range [0, 61]
//| * the day of the year, range [1, 366], -1 indicates not known //| * ``tm_wday``: the day of the week, range [0, 6], Monday is 0
//| * 1 when in daylight savings, 0 when not, -1 if unknown. //| * ``tm_yday``: the day of the year, range [1, 366], -1 indicates not known
//| * ``tm_isdst``: 1 when in daylight savings, 0 when not, -1 if unknown.
//| //|
const mp_obj_namedtuple_type_t struct_time_type_obj = { const mp_obj_namedtuple_type_t struct_time_type_obj = {
.base = { .base = {
@ -235,9 +236,16 @@ STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) {
return rtc_get_time_source_time(); return rtc_get_time_source_time();
} }
mp_int_t secs = mp_obj_int_get_checked(args[0]); mp_obj_t arg = args[0];
if (secs < EPOCH1970_EPOCH2000_DIFF_SECS) if (mp_obj_is_float(arg)) {
arg = mp_obj_new_int_from_float(mp_obj_get_float(arg));
}
mp_int_t secs = mp_obj_get_int(arg);
if (secs < EPOCH1970_EPOCH2000_DIFF_SECS) {
mp_raise_msg(&mp_type_OverflowError, translate("timestamp out of range for platform time_t")); mp_raise_msg(&mp_type_OverflowError, translate("timestamp out of range for platform time_t"));
}
timeutils_struct_time_t tm; timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(secs, &tm); timeutils_seconds_since_epoch_to_struct_time(secs, &tm);
@ -269,8 +277,9 @@ STATIC mp_obj_t time_mktime(mp_obj_t t) {
mp_raise_TypeError(translate("function takes exactly 9 arguments")); mp_raise_TypeError(translate("function takes exactly 9 arguments"));
} }
if (mp_obj_get_int(elem[0]) < 2000) if (mp_obj_get_int(elem[0]) < 2000) {
mp_raise_msg(&mp_type_OverflowError, translate("timestamp out of range for platform time_t")); mp_raise_msg(&mp_type_OverflowError, translate("timestamp out of range for platform time_t"));
}
mp_uint_t secs = timeutils_mktime(mp_obj_get_int(elem[0]), mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_uint_t secs = timeutils_mktime(mp_obj_get_int(elem[0]), mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]),
mp_obj_get_int(elem[3]), mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5])); mp_obj_get_int(elem[3]), mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5]));

View File

@ -0,0 +1,46 @@
/*
* 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 <string.h>
#include "py/runtime.h"
#include "shared-bindings/bleio/Attribute.h"
void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode) {
switch (security_mode) {
case SECURITY_MODE_NO_ACCESS:
case SECURITY_MODE_OPEN:
case SECURITY_MODE_ENC_NO_MITM:
case SECURITY_MODE_ENC_WITH_MITM:
case SECURITY_MODE_LESC_ENC_WITH_MITM:
case SECURITY_MODE_SIGNED_NO_MITM:
case SECURITY_MODE_SIGNED_WITH_MITM:
break;
default:
mp_raise_ValueError(translate("Invalid security_mode"));
break;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H
#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H
// BLE security modes: 0x<level><mode>
typedef enum {
SECURITY_MODE_NO_ACCESS = 0x00,
SECURITY_MODE_OPEN = 0x11,
SECURITY_MODE_ENC_NO_MITM = 0x21,
SECURITY_MODE_ENC_WITH_MITM = 0x31,
SECURITY_MODE_LESC_ENC_WITH_MITM = 0x41,
SECURITY_MODE_SIGNED_NO_MITM = 0x12,
SECURITY_MODE_SIGNED_WITH_MITM = 0x22,
} bleio_attribute_security_mode_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H

View File

@ -27,14 +27,17 @@
#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H
#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H
// Flags for each characteristic property. Common across ports. typedef enum {
typedef struct { CHAR_PROP_NONE = 0,
bool broadcast : 1; CHAR_PROP_BROADCAST = 1u << 0,
bool read : 1; CHAR_PROP_INDICATE = 1u << 1,
bool write_no_response : 1; CHAR_PROP_NOTIFY = 1u << 2,
bool write : 1; CHAR_PROP_READ = 1u << 3,
bool notify : 1; CHAR_PROP_WRITE = 1u << 4,
bool indicate : 1; CHAR_PROP_WRITE_NO_RESPONSE = 1u << 5,
} bleio_characteristic_properties_t; CHAR_PROP_ALL = (CHAR_PROP_BROADCAST | CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY |
CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE)
} bleio_characteristic_properties_enum_t;
typedef uint8_t bleio_characteristic_properties_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H

View File

@ -25,6 +25,7 @@
*/ */
#include "tick.h" #include "tick.h"
#include "py/objstr.h"
#include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/Processor.h"
#include "shared-module/usb_midi/__init__.h" #include "shared-module/usb_midi/__init__.h"
#include "supervisor/port.h" #include "supervisor/port.h"
@ -43,13 +44,11 @@ void load_serial_number(void) {
uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH];
common_hal_mcu_processor_get_uid(raw_id); common_hal_mcu_processor_get_uid(raw_id);
static const char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'};
for (int i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) { for (int i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) {
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf; uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf;
// Strings are UTF-16-LE encoded. // Strings are UTF-16-LE encoded.
usb_serial_number[1 + i * 2 + j] = nibble_to_hex[nibble]; usb_serial_number[1 + i * 2 + j] = nibble_to_hex_upper[nibble];
} }
} }
} }