diff options
Diffstat (limited to 'circuitpython/shared-bindings/_bleio')
26 files changed, 3612 insertions, 0 deletions
diff --git a/circuitpython/shared-bindings/_bleio/Adapter.c b/circuitpython/shared-bindings/_bleio/Adapter.c new file mode 100644 index 0000000..ce10809 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Adapter.c @@ -0,0 +1,468 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Adapter.h" + +#define ADV_INTERVAL_MIN (0.02f) +#define ADV_INTERVAL_MIN_STRING "0.02" +#define ADV_INTERVAL_MAX (10.24f) +#define ADV_INTERVAL_MAX_STRING "10.24" +// 20ms is recommended by Apple +#define ADV_INTERVAL_DEFAULT (0.1f) + +#define INTERVAL_DEFAULT (0.1f) +#define INTERVAL_MIN (0.0025f) +#define INTERVAL_MIN_STRING "0.0025" +#define INTERVAL_MAX (40.959375f) +#define INTERVAL_MAX_STRING "40.959375" +#define WINDOW_DEFAULT (0.1f) + +//| class Adapter: +//| """ +//| The BLE Adapter object manages the discovery and connection to other nearby Bluetooth Low Energy devices. +//| This part of the Bluetooth Low Energy Specification is known as Generic Access Profile (GAP). +//| +//| Discovery of other devices happens during a scanning process that listens for small packets of +//| information, known as advertisements, that are broadcast unencrypted. The advertising packets +//| have two different uses. The first is to broadcast a small piece of data to anyone who cares and +//| and nothing more. These are known as beacons. The second class of advertisement is to promote +//| additional functionality available after the devices establish a connection. For example, a +//| BLE heart rate monitor would advertise that it provides the standard BLE Heart Rate Service. +//| +//| The Adapter can do both parts of this process: it can scan for other device +//| advertisements and it can advertise its own data. Furthermore, Adapters can accept incoming +//| connections and also initiate connections.""" +//| + +//| def __init__(self, *, uart: busio.UART, rts: digitalio.DigitalInOut, cts: digitalio.DigitalInOut) -> None: +//| """On boards that do not have native BLE, you can use an HCI co-processor. +//| Pass the uart and pins used to communicate with the co-processor, such as an Adafruit AirLift. +//| The co-processor must have been reset and put into BLE mode beforehand +//| by the appropriate pin manipulation. +//| The ``uart``, ``rts``, and ``cts`` objects are used to +//| communicate with the HCI co-processor in HCI mode. +//| The `Adapter` object is enabled during this call. +//| +//| After instantiating an Adapter, call `_bleio.set_adapter()` to set `_bleio.adapter` +//| +//| On boards with native BLE, you cannot create an instance of `_bleio.Adapter`; +//| this constructor will raise `NotImplementedError`. +//| Use `_bleio.adapter` to access the sole instance already available. +//| """ +//| ... +//| +STATIC mp_obj_t bleio_adapter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + #if CIRCUITPY_BLEIO_HCI + bleio_adapter_obj_t *self = common_hal_bleio_allocate_adapter_or_raise(); + + enum { ARG_uart, ARG_rts, ARG_cts }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_uart, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + busio_uart_obj_t *uart = mp_arg_validate_type(args[ARG_uart].u_obj, &busio_uart_type, MP_QSTR_uart); + + digitalio_digitalinout_obj_t *rts = mp_arg_validate_type(args[ARG_rts].u_obj, &digitalio_digitalinout_type, MP_QSTR_rts); + digitalio_digitalinout_obj_t *cts = mp_arg_validate_type(args[ARG_cts].u_obj, &digitalio_digitalinout_type, MP_QSTR_cts); + + // Will enable the adapter. + common_hal_bleio_adapter_construct_hci_uart(self, uart, rts, cts); + + return MP_OBJ_FROM_PTR(self); + #else + mp_raise_NotImplementedError(translate("Cannot create a new Adapter; use _bleio.adapter;")); + return mp_const_none; + #endif // CIRCUITPY_BLEIO_HCI +} + +//| +//| enabled: bool +//| """State of the BLE adapter.""" +//| +STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) { + return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_enabled_obj, bleio_adapter_get_enabled); + +static mp_obj_t bleio_adapter_set_enabled(mp_obj_t self, mp_obj_t value) { + const bool enabled = mp_obj_is_true(value); + + common_hal_bleio_adapter_set_enabled(self, enabled); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_enabled_obj, bleio_adapter_set_enabled); + +MP_PROPERTY_GETSET(bleio_adapter_enabled_obj, + (mp_obj_t)&bleio_adapter_get_enabled_obj, + (mp_obj_t)&bleio_adapter_set_enabled_obj); + +//| address: Address +//| """MAC address of the BLE adapter.""" +//| +STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) { + return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_address(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address); + +STATIC mp_obj_t bleio_adapter_set_address(mp_obj_t self, mp_obj_t new_address) { + if (!common_hal_bleio_adapter_set_address(self, new_address)) { + mp_raise_bleio_BluetoothError(translate("Could not set address")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_address_obj, bleio_adapter_set_address); + +MP_PROPERTY_GETSET(bleio_adapter_address_obj, + (mp_obj_t)&bleio_adapter_get_address_obj, + (mp_obj_t)&bleio_adapter_set_address_obj); + +//| name: str +//| """name of the BLE adapter used once connected. +//| 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_name(mp_obj_t self) { + return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_name(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_name_obj, bleio_adapter_get_name); + + +STATIC mp_obj_t bleio_adapter_set_name(mp_obj_t self, mp_obj_t new_name) { + common_hal_bleio_adapter_set_name(self, mp_obj_str_get_str(new_name)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_name_obj, bleio_adapter_set_name); + +MP_PROPERTY_GETSET(bleio_adapter_name_obj, + (mp_obj_t)&bleio_adapter_get_name_obj, + (mp_obj_t)&bleio_adapter_set_name_obj); + +//| def start_advertising(self, data: ReadableBuffer, *, +//| scan_response: Optional[ReadableBuffer] = None, connectable: bool = True, +//| anonymous: bool = False, timeout: int = 0, interval: float = 0.1, +//| tx_power: int = 0, directed_to: Optional[Address] = None) -> None: +//| """Starts advertising until `stop_advertising` is called or if connectable, another device +//| connects to us. +//| +//| .. warning:: If data is longer than 31 bytes, then this will automatically advertise as an +//| extended advertisement that older BLE 4.x clients won't be able to scan for. +//| +//| .. note:: If you set ``anonymous=True``, then a timeout must be specified. If no timeout is +//| specified, then the maximum allowed timeout will be selected automatically. +//| +//| :param ~circuitpython_typing.ReadableBuffer data: advertising data packet bytes +//| :param ~circuitpython_typing.ReadableBuffer scan_response: scan response data packet bytes. ``None`` if no scan response is needed. +//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral. +//| :param bool anonymous: If `True` then this device's MAC address is randomized before advertising. +//| :param int timeout: If set, we will only advertise for this many seconds. Zero means no timeout. +//| :param float interval: advertising interval, in seconds +//| :param tx_power int: transmitter power while advertising in dBm +//| :param directed_to Address: peer to advertise directly to""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_anonymous, ARG_timeout, ARG_interval, ARG_tx_power, ARG_directed_to }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_scan_response, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_anonymous, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_tx_power, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_directed_to, MP_ARG_KW_ONLY | 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); + + mp_buffer_info_t data_bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); + + // Pass an empty buffer if scan_response not provided. + mp_buffer_info_t scan_response_bufinfo = { 0 }; + if (args[ARG_scan_response].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_scan_response].u_obj, &scan_response_bufinfo, MP_BUFFER_READ); + } + + if (args[ARG_interval].u_obj == MP_OBJ_NULL) { + args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT); + } + + const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) { + mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), + ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING); + } + + bool connectable = args[ARG_connectable].u_bool; + bool anonymous = args[ARG_anonymous].u_bool; + uint32_t timeout = args[ARG_timeout].u_int; + if (data_bufinfo.len > 31 && connectable && scan_response_bufinfo.len > 0) { + mp_raise_bleio_BluetoothError(translate("Cannot have scan responses for extended, connectable advertisements.")); + } + + const bleio_address_obj_t *address = NULL; + if (args[ARG_directed_to].u_obj != mp_const_none) { + if (!connectable) { + mp_raise_bleio_BluetoothError(translate("Only connectable advertisements can be directed")); + } + address = mp_arg_validate_type(args[ARG_directed_to].u_obj, &bleio_address_type, MP_QSTR_directed_to); + } + + common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, timeout, interval, + &data_bufinfo, &scan_response_bufinfo, args[ARG_tx_power].u_int, address); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_advertising_obj, 1, bleio_adapter_start_advertising); + +//| def stop_advertising(self) -> None: +//| """Stop sending advertising packets.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_stop_advertising(mp_obj_t self_in) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_adapter_stop_advertising(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_stop_advertising_obj, bleio_adapter_stop_advertising); + +//| def start_scan(self, prefixes: ReadableBuffer = b"", *, buffer_size: int = 512, extended: bool = False, timeout: Optional[float] = None, interval: float = 0.1, window: float = 0.1, minimum_rssi: int = -80, active: bool = True) -> Iterable[ScanEntry]: +//| """Starts a BLE scan and returns an iterator of results. Advertisements and scan responses are +//| filtered and returned separately. +//| +//| :param ~circuitpython_typing.ReadableBuffer prefixes: Sequence of byte string prefixes to filter advertising packets +//| with. A packet without an advertising structure that matches one of the prefixes is +//| ignored. Format is one byte for length (n) and n bytes of prefix and can be repeated. +//| :param int buffer_size: the maximum number of advertising bytes to buffer. +//| :param bool extended: When True, support extended advertising packets. Increasing buffer_size is recommended when this is set. +//| :param float timeout: the scan timeout in seconds. If None or zero, will scan until `stop_scan` is called. +//| :param float interval: the interval (in seconds) between the start of two consecutive scan windows +//| Must be in the range 0.0025 - 40.959375 seconds. +//| :param float window: the duration (in seconds) to scan a single BLE channel. +//| window must be <= interval. +//| :param int minimum_rssi: the minimum rssi of entries to return. +//| :param bool active: retrieve scan responses for scannable advertisements. +//| :returns: an iterable of `_bleio.ScanEntry` objects +//| :rtype: iterable""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_prefixes, ARG_buffer_size, ARG_extended, ARG_timeout, ARG_interval, ARG_window, ARG_minimum_rssi, ARG_active }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_prefixes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 512} }, + { MP_QSTR_extended, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_window, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_minimum_rssi, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -80} }, + { MP_QSTR_active, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + }; + + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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); + + mp_float_t timeout = 0.0f; + if (args[ARG_timeout].u_obj != mp_const_none) { + timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + } + + if (args[ARG_interval].u_obj == MP_OBJ_NULL) { + args[ARG_interval].u_obj = mp_obj_new_float(INTERVAL_DEFAULT); + } + + if (args[ARG_window].u_obj == MP_OBJ_NULL) { + args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT); + } + + const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { + mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING); + } + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" + if (timeout != 0.0f && timeout < interval) { + mp_raise_ValueError(translate("non-zero timeout must be >= interval")); + } + #pragma GCC diagnostic pop + + const mp_float_t window = mp_obj_get_float(args[ARG_window].u_obj); + if (window > interval) { + mp_raise_ValueError(translate("window must be <= interval")); + } + + mp_buffer_info_t prefix_bufinfo; + prefix_bufinfo.len = 0; + if (args[ARG_prefixes].u_obj != MP_OBJ_NULL) { + mp_get_buffer_raise(args[ARG_prefixes].u_obj, &prefix_bufinfo, MP_BUFFER_READ); + if (gc_nbytes(prefix_bufinfo.buf) == 0) { + mp_raise_ValueError(translate("Prefix buffer must be on the heap")); + } + } + + return common_hal_bleio_adapter_start_scan(self, prefix_bufinfo.buf, prefix_bufinfo.len, args[ARG_extended].u_bool, args[ARG_buffer_size].u_int, timeout, interval, window, args[ARG_minimum_rssi].u_int, args[ARG_active].u_bool); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_scan_obj, 1, bleio_adapter_start_scan); + +//| def stop_scan(self) -> None: +//| """Stop the current scan.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_stop_scan(mp_obj_t self_in) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_adapter_stop_scan(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_stop_scan_obj, bleio_adapter_stop_scan); + +//| advertising: bool +//| """True when the adapter is currently advertising. (read-only)""" +//| +STATIC mp_obj_t bleio_adapter_get_advertising(mp_obj_t self) { + return mp_obj_new_bool(common_hal_bleio_adapter_get_advertising(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_advertising_obj, bleio_adapter_get_advertising); + +MP_PROPERTY_GETTER(bleio_adapter_advertising_obj, + (mp_obj_t)&bleio_adapter_get_advertising_obj); + +//| connected: bool +//| """True when the adapter is connected to another device regardless of who initiated the +//| connection. (read-only)""" +//| +STATIC mp_obj_t bleio_adapter_get_connected(mp_obj_t self) { + return mp_obj_new_bool(common_hal_bleio_adapter_get_connected(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_connected_obj, bleio_adapter_get_connected); + +MP_PROPERTY_GETTER(bleio_adapter_connected_obj, + (mp_obj_t)&bleio_adapter_get_connected_obj); + +//| connections: Tuple[Connection] +//| """Tuple of active connections including those initiated through +//| :py:meth:`_bleio.Adapter.connect`. (read-only)""" +//| +STATIC mp_obj_t bleio_adapter_get_connections(mp_obj_t self) { + return common_hal_bleio_adapter_get_connections(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_connections_obj, bleio_adapter_get_connections); + +MP_PROPERTY_GETTER(bleio_adapter_connections_obj, + (mp_obj_t)&bleio_adapter_get_connections_obj); + +//| def connect(self, address: Address, *, timeout: float) -> Connection: +//| """Attempts a connection to the device with the given address. +//| +//| :param Address address: The address of the peripheral to connect to +//| :param float/int timeout: Try to connect for timeout seconds.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_address, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + bleio_address_obj_t *address = mp_arg_validate_type(args[ARG_address].u_obj, &bleio_address_type, MP_QSTR_address); + mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + + return common_hal_bleio_adapter_connect(self, address, timeout); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_connect_obj, 1, bleio_adapter_connect); + +//| def erase_bonding(self) -> None: +//| """Erase all bonding information stored in flash memory.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_erase_bonding(mp_obj_t self_in) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_adapter_erase_bonding(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_erase_bonding_obj, bleio_adapter_erase_bonding); + +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_address), MP_ROM_PTR(&bleio_adapter_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_adapter_name_obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_adapter_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_adapter_stop_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_advertising), MP_ROM_PTR(&bleio_adapter_advertising_obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_scan), MP_ROM_PTR(&bleio_adapter_start_scan_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_scan), MP_ROM_PTR(&bleio_adapter_stop_scan_obj) }, + + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_adapter_connect_obj) }, + + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_adapter_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_connections), MP_ROM_PTR(&bleio_adapter_connections_obj) }, + + { MP_ROM_QSTR(MP_QSTR_erase_bonding), MP_ROM_PTR(&bleio_adapter_erase_bonding_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table); + +const mp_obj_type_t bleio_adapter_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Adapter, + .make_new = bleio_adapter_make_new, + .locals_dict = (mp_obj_t)&bleio_adapter_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_bleio/Adapter.h b/circuitpython/shared-bindings/_bleio/Adapter.h new file mode 100644 index 0000000..1dce615 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Adapter.h @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_ADAPTER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H + +#include <stdint.h> + +#include "common-hal/_bleio/Adapter.h" + +#include "py/objstr.h" +#include "shared-module/_bleio/Address.h" + +extern const mp_obj_type_t bleio_adapter_type; + +#if CIRCUITPY_BLEIO_HCI +void common_hal_bleio_adapter_construct_hci_uart(bleio_adapter_obj_t *self, busio_uart_obj_t *uart, digitalio_digitalinout_obj_t *rts, digitalio_digitalinout_obj_t *cts); +#endif // CIRCUITPY_BLEIO_HCI + +extern bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self); +extern bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self); +extern void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled); +extern mp_int_t common_hal_bleio_adapter_get_tx_power(bleio_adapter_obj_t *self); +extern void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t tx_power); +extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self); +extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self); +extern bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address); + +extern mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self); +extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name); + +extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, float interval, + const uint8_t *advertising_data, uint16_t advertising_data_len, + const uint8_t *scan_response_data, uint16_t scan_response_data_len, + mp_int_t tx_power, const bleio_address_obj_t *directed_to); + +extern void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, + mp_buffer_info_t *advertising_data_bufinfo, + mp_buffer_info_t *scan_response_data_bufinfo, + mp_int_t tx_power, const bleio_address_obj_t *directed_to); +extern void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self); + +extern mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t *prefixes, size_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active); +extern void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self); + +extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self); +extern mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self); +extern mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout); + +extern void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self); +extern bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H diff --git a/circuitpython/shared-bindings/_bleio/Address.c b/circuitpython/shared-bindings/_bleio/Address.c new file mode 100644 index 0000000..9673642 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Address.c @@ -0,0 +1,219 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 <stdio.h> + +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-module/_bleio/Address.h" + +//| class Address: +//| """Encapsulates the address of a BLE device.""" +//| + +//| def __init__(self, address: ReadableBuffer, address_type: int) -> None: +//| """Create a new Address object encapsulating the address value. +//| The value itself can be one of: +//| +//| :param ~circuitpython_typing.ReadableBuffer address: The address value to encapsulate. A buffer object (bytearray, bytes) of 6 bytes. +//| :param int address_type: one of the integer values: `PUBLIC`, `RANDOM_STATIC`, +//| `RANDOM_PRIVATE_RESOLVABLE`, or `RANDOM_PRIVATE_NON_RESOLVABLE`.""" +//| ... +//| +STATIC mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_address, ARG_address_type }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_address_type, MP_ARG_INT, {.u_int = BLEIO_ADDRESS_TYPE_PUBLIC } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + bleio_address_obj_t *self = m_new_obj(bleio_address_obj_t); + self->base.type = &bleio_address_type; + + const mp_obj_t address = args[ARG_address].u_obj; + mp_buffer_info_t buf_info; + mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); + if (buf_info.len != NUM_BLEIO_ADDRESS_BYTES) { + mp_raise_ValueError_varg(translate("Address must be %d bytes long"), NUM_BLEIO_ADDRESS_BYTES); + } + + const mp_int_t address_type = args[ARG_address_type].u_int; + if (address_type < BLEIO_ADDRESS_TYPE_MIN || address_type > BLEIO_ADDRESS_TYPE_MAX) { + mp_raise_ValueError(translate("Address type out of range")); + } + + common_hal_bleio_address_construct(self, buf_info.buf, address_type); + + return MP_OBJ_FROM_PTR(self); +} + +//| address_bytes: bytes +//| """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:: python +//| +//| >>> 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) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_address_get_address_bytes(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_address_bytes_obj, bleio_address_get_address_bytes); + +MP_PROPERTY_GETTER(bleio_address_address_bytes_obj, + (mp_obj_t)&bleio_address_get_address_bytes_obj); + +//| type: int +//| """The address type (read-only). +//| +//| One of the integer values: `PUBLIC`, `RANDOM_STATIC`, `RANDOM_PRIVATE_RESOLVABLE`, +//| or `RANDOM_PRIVATE_NON_RESOLVABLE`.""" +//| +STATIC mp_obj_t bleio_address_get_type(mp_obj_t self_in) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_address_get_type(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_type_obj, bleio_address_get_type); + +MP_PROPERTY_GETTER(bleio_address_type_obj, + (mp_obj_t)&bleio_address_get_type_obj); + +//| def __eq__(self, other: object) -> bool: +//| """Two Address objects are equal if their addresses and address types are equal.""" +//| ... +//| +STATIC mp_obj_t bleio_address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + // Two Addresses are equal if their address bytes and address_type are equal + case MP_BINARY_OP_EQUAL: + if (mp_obj_is_type(rhs_in, &bleio_address_type)) { + bleio_address_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); + bleio_address_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool( + mp_obj_equal(common_hal_bleio_address_get_address_bytes(lhs), + common_hal_bleio_address_get_address_bytes(rhs)) && + common_hal_bleio_address_get_type(lhs) == + common_hal_bleio_address_get_type(rhs)); + + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __hash__(self) -> int: +//| """Returns a hash for the Address data.""" +//| ... +//| +STATIC mp_obj_t bleio_address_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + switch (op) { + // Two Addresses are equal if their address bytes and address_type are equal + case MP_UNARY_OP_HASH: { + mp_obj_t bytes = common_hal_bleio_address_get_address_bytes(MP_OBJ_TO_PTR(self_in)); + GET_STR_HASH(bytes, h); + if (h == 0) { + GET_STR_DATA_LEN(bytes, data, len); + h = qstr_compute_hash(data, len); + } + h ^= common_hal_bleio_address_get_type(MP_OBJ_TO_PTR(self_in)); + return MP_OBJ_NEW_SMALL_INT(h); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +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); + mp_buffer_info_t buf_info; + mp_obj_t address_bytes = common_hal_bleio_address_get_address_bytes(self); + mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ); + + const uint8_t *buf = (uint8_t *)buf_info.buf; + mp_printf(print, "<Address %02x:%02x:%02x:%02x:%02x:%02x>", + buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]); +} + +//| PUBLIC: int +//| """A publicly known address, with a company ID (high 24 bits)and company-assigned part (low 24 bits).""" +//| +//| RANDOM_STATIC: int +//| """A randomly generated address that does not change often. It may never change or may change after +//| a power cycle.""" +//| +//| RANDOM_PRIVATE_RESOLVABLE: int +//| """An address that is usable when the peer knows the other device's secret Identity Resolving Key (IRK).""" +//| +//| RANDOM_PRIVATE_NON_RESOLVABLE: int +//| """A randomly generated address that changes on every connection.""" +//| +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_type), MP_ROM_PTR(&bleio_address_type_obj) }, + // These match the BLE_GAP_ADDR_TYPES values used by the nRF library. + { MP_ROM_QSTR(MP_QSTR_PUBLIC), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_STATIC), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_RESOLVABLE), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_NON_RESOLVABLE), MP_ROM_INT(3) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_address_locals_dict, bleio_address_locals_dict_table); + +const mp_obj_type_t bleio_address_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Address, + .make_new = bleio_address_make_new, + .print = bleio_address_print, + .locals_dict = (mp_obj_dict_t *)&bleio_address_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = bleio_address_unary_op, + .binary_op = bleio_address_binary_op, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/Address.h b/circuitpython/shared-bindings/_bleio/Address.h new file mode 100644 index 0000000..98b6f80 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Address.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_ADDRESS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H + +#include "py/objtype.h" +#include "shared-module/_bleio/Address.h" + +#define BLEIO_ADDRESS_TYPE_PUBLIC (0) +#define BLEIO_ADDRESS_TYPE_RANDOM_STATIC (1) +#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE (2) +#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE (3) + +#define BLEIO_ADDRESS_TYPE_MIN BLEIO_ADDRESS_TYPE_PUBLIC +#define BLEIO_ADDRESS_TYPE_MAX BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE + +extern const mp_obj_type_t bleio_address_type; + +extern void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type); +extern mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self); +extern uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H diff --git a/circuitpython/shared-bindings/_bleio/Attribute.c b/circuitpython/shared-bindings/_bleio/Attribute.c new file mode 100644 index 0000000..d639f2a --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Attribute.c @@ -0,0 +1,82 @@ +/* + * 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" + +//| class 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.""" +//| +//| def __init__(self) -> None: +//| """You cannot create an instance of :py:class:`~_bleio.Attribute`.""" +//| ... +//| + +STATIC const mp_rom_map_elem_t bleio_attribute_locals_dict_table[] = { + +//| NO_ACCESS: int +//| """security mode: access not allowed""" +//| +//| OPEN: int +//| """security_mode: no security (link is not encrypted)""" +//| +//| ENCRYPT_NO_MITM: int +//| """security_mode: unauthenticated encryption, without man-in-the-middle protection""" +//| +//| ENCRYPT_WITH_MITM: int +//| """security_mode: authenticated encryption, with man-in-the-middle protection""" +//| +//| LESC_ENCRYPT_WITH_MITM: int +//| """security_mode: LESC encryption, with man-in-the-middle protection""" +//| +//| SIGNED_NO_MITM: int +//| """security_mode: unauthenticated data signing, without man-in-the-middle protection""" +//| +//| SIGNED_WITH_MITM: int +//| """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, +}; diff --git a/circuitpython/shared-bindings/_bleio/Attribute.h b/circuitpython/shared-bindings/_bleio/Attribute.h new file mode 100644 index 0000000..a0ce045 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Attribute.h @@ -0,0 +1,39 @@ +/* + * 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 "common-hal/_bleio/Attribute.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 diff --git a/circuitpython/shared-bindings/_bleio/Characteristic.c b/circuitpython/shared-bindings/_bleio/Characteristic.c new file mode 100644 index 0000000..4ff37f0 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Characteristic.c @@ -0,0 +1,334 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class Characteristic: +//| """Stores information about a BLE service characteristic and allows reading +//| and writing of the characteristic's value.""" +//| +//| def __init__(self) -> None: +//| """There is no regular constructor for a Characteristic. A new local Characteristic can be created +//| and attached to a Service by calling `add_to_service()`. +//| Remote Characteristic objects are created by `Connection.discover_remote_services()` +//| as part of remote Services.""" +//| ... +//| + +//| def add_to_service(self, service: Service, uuid: UUID, *, properties: int = 0, +//| read_perm: int = Attribute.OPEN, write_perm: int = Attribute.OPEN, +//| max_length: int = 20, fixed_length: bool = False, +//| initial_value: Optional[ReadableBuffer] = None, +//| user_description: Optional[str] = None) -> Characteristic: +//| """Create a new Characteristic object, and add it to this Service. +//| +//| :param Service service: The service that will provide this characteristic +//| :param UUID uuid: The uuid of the characteristic +//| :param int properties: The properties of the characteristic, +//| specified as a bitmask of these values bitwise-or'd together: +//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. +//| :param int read_perm: Specifies whether the characteristic 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.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `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 ~circuitpython_typing.ReadableBuffer initial_value: The initial value for this characteristic. If not given, will be +//| filled with zeros. +//| :param str user_description: User friendly description of the characteristic +//| +//| :return: the new Characteristic.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_service, ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value, ARG_user_description }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_properties, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { 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_QSTR_initial_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_user_description, MP_ARG_KW_ONLY | 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); + + bleio_service_obj_t *service = mp_arg_validate_type(args[ARG_service].u_obj, &bleio_service_type, MP_QSTR_service); + + bleio_uuid_obj_t *uuid = mp_arg_validate_type(args[ARG_uuid].u_obj, &bleio_uuid_type, MP_QSTR_uuid); + + 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); + + const mp_int_t max_length_int = args[ARG_max_length].u_int; + if (max_length_int < 0) { + mp_raise_ValueError(translate("max_length must be >= 0")); + } + const size_t max_length = (size_t)max_length_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + if (initial_value_bufinfo.len > max_length || + (fixed_length && initial_value_bufinfo.len != max_length)) { + mp_raise_ValueError(translate("initial_value length is wrong")); + } + + const char *user_description = NULL; + if (args[ARG_user_description].u_obj != mp_const_none) { + user_description = mp_obj_str_get_str(args[ARG_user_description].u_obj); + } + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + characteristic->base.type = &bleio_characteristic_type; + + // 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( + characteristic, service, 0, uuid, + properties, read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo, + user_description); + + return MP_OBJ_FROM_PTR(characteristic); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_add_to_service_fun_obj, 1, bleio_characteristic_add_to_service); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_characteristic_add_to_service_obj, MP_ROM_PTR(&bleio_characteristic_add_to_service_fun_obj)); + + + +//| properties: int +//| """An int bitmask representing which properties are set, specified as bitwise or'ing of +//| of these possible values. +//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`.""" +//| +STATIC mp_obj_t bleio_characteristic_get_properties(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_get_properties(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_properties_obj, bleio_characteristic_get_properties); + +MP_PROPERTY_GETTER(bleio_characteristic_properties_obj, + (mp_obj_t)&bleio_characteristic_get_properties_obj); + +//| uuid: Optional[UUID] +//| """The UUID of this characteristic. (read-only) +//| +//| Will be ``None`` if the 128-bit UUID for this characteristic is not known.""" +//| +STATIC mp_obj_t bleio_characteristic_get_uuid(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_characteristic_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_uuid_obj, bleio_characteristic_get_uuid); + +MP_PROPERTY_GETTER(bleio_characteristic_uuid_obj, + (mp_obj_t)&bleio_characteristic_get_uuid_obj); + +//| value: bytearray +//| """The value of this characteristic.""" +//| +STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uint8_t temp[512]; + size_t actual_len = common_hal_bleio_characteristic_get_value(self, temp, sizeof(temp)); + return mp_obj_new_bytearray(actual_len, temp); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_value_obj, bleio_characteristic_get_value); + +STATIC mp_obj_t bleio_characteristic_set_value(mp_obj_t self_in, mp_obj_t value_in) { + bleio_characteristic_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_characteristic_set_value(self, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_value_obj, bleio_characteristic_set_value); + +MP_PROPERTY_GETSET(bleio_characteristic_value_obj, + (mp_obj_t)&bleio_characteristic_get_value_obj, + (mp_obj_t)&bleio_characteristic_set_value_obj); + +//| max_length: int +//| """The max length of this characteristic.""" +//| +STATIC mp_obj_t bleio_characteristic_get_max_length(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_get_max_length(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_max_length_obj, bleio_characteristic_get_max_length); + +MP_PROPERTY_GETTER(bleio_characteristic_max_length_obj, + (mp_obj_t)&bleio_characteristic_get_max_length_obj); + +//| descriptors: Descriptor +//| """A tuple of :py:class:`Descriptor` objects related to this characteristic. (read-only)""" +//| +STATIC mp_obj_t bleio_characteristic_get_descriptors(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + // Return list as a tuple so user won't be able to change it. + return MP_OBJ_FROM_PTR(common_hal_bleio_characteristic_get_descriptors(self)); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_descriptors_obj, bleio_characteristic_get_descriptors); + +MP_PROPERTY_GETTER(bleio_characteristic_descriptors_obj, + (mp_obj_t)&bleio_characteristic_get_descriptors_obj); + +//| service: Service +//| """The Service this Characteristic is a part of.""" +//| +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); + +MP_PROPERTY_GETTER(bleio_characteristic_service_obj, + (mp_obj_t)&bleio_characteristic_get_service_obj); + +//| def set_cccd(self, *, notify: bool = False, indicate: bool = False) -> None: +//| """Set the remote characteristic's CCCD to enable or disable notification and indication. +//| +//| :param bool notify: True if Characteristic should receive notifications of remote writes +//| :param float indicate: True if Characteristic should receive indications of remote writes""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_set_cccd(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_notify, ARG_indicate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_notify, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_indicate, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + 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); + + common_hal_bleio_characteristic_set_cccd(self, args[ARG_notify].u_bool, args[ARG_indicate].u_bool); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_characteristic_set_cccd); + +STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) }, + { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_properties_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) }, + + // Bitmask constants to represent properties +//| BROADCAST: int +//| """property: allowed in advertising packets""" +//| +//| INDICATE: int +//| """property: server will indicate to the client when the value is set and wait for a response""" +//| +//| NOTIFY: int +//| """property: server will notify the client when the value is set""" +//| +//| READ: int +//| """property: clients may read this characteristic""" +//| +//| WRITE: int +//| """property: clients may write this characteristic; a response will be sent back""" +//| +//| WRITE_NO_RESPONSE: int +//| """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 void bleio_characteristic_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Characteristic("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, "<Characteristic with Unregistered UUID>"); + } +} + +const mp_obj_type_t bleio_characteristic_type = { + { &mp_type_type }, + .name = MP_QSTR_Characteristic, + .print = bleio_characteristic_print, + .locals_dict = (mp_obj_dict_t *)&bleio_characteristic_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_bleio/Characteristic.h b/circuitpython/shared-bindings/_bleio/Characteristic.h new file mode 100644 index 0000000..349dd5f --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Characteristic.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_CHARACTERISTIC_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H + +#include "py/objtuple.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-module/_bleio/Characteristic.h" +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Service.h" + +extern const mp_obj_type_t bleio_characteristic_type; + +extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self); +extern mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self); +extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self); +extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self); +extern size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t *self); +extern size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len); +extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor); +extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, 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_buffer_info_t *initial_value_bufinfo, const char *user_description); +extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); +extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H diff --git a/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.c b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.c new file mode 100644 index 0000000..78ff07f --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.c @@ -0,0 +1,242 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mperrno.h" +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-bindings/util.h" + +STATIC void raise_error_if_not_connected(bleio_characteristic_buffer_obj_t *self) { + if (!common_hal_bleio_characteristic_buffer_connected(self)) { + mp_raise_ConnectionError(translate("Not connected")); + } +} + +//| class CharacteristicBuffer: +//| """Accumulates a Characteristic's incoming values in a FIFO buffer.""" +//| +//| def __init__(self, characteristic: Characteristic, *, timeout: int = 1, buffer_size: int = 64) -> None: +//| +//| """Monitor the given Characteristic. Each time a new value is written to the Characteristic +//| add the newly-written bytes to a FIFO buffer. +//| +//| :param Characteristic characteristic: The Characteristic to monitor. +//| It may be a local Characteristic provided by a Peripheral Service, or a remote Characteristic +//| in a remote Service that a Central has connected to. +//| :param int timeout: the timeout in seconds to wait for the first character and between subsequent characters. +//| :param int buffer_size: Size of ring buffer that stores incoming data coming from client. +//| Must be >= 1.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_buffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_characteristic, ARG_timeout, ARG_buffer_size, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + { MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + bleio_characteristic_obj_t *characteristic = mp_arg_validate_type(args[ARG_characteristic].u_obj, &bleio_characteristic_type, MP_QSTR_characteristic); + + mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + if (timeout < 0.0f) { + mp_raise_ValueError(translate("timeout must be >= 0.0")); + } + + const int buffer_size = args[ARG_buffer_size].u_int; + if (buffer_size < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_buffer_size); + } + + bleio_characteristic_buffer_obj_t *self = m_new_obj(bleio_characteristic_buffer_obj_t); + self->base.type = &bleio_characteristic_buffer_type; + + common_hal_bleio_characteristic_buffer_construct(self, characteristic, timeout, buffer_size); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void check_for_deinit(bleio_characteristic_buffer_obj_t *self) { + if (common_hal_bleio_characteristic_buffer_deinited(self)) { + raise_deinited_error(); + } +} + +// These are standard stream methods. Code is in py/stream.c. +// +//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: +//| """Read characters. If ``nbytes`` is specified then read at most that many +//| bytes. Otherwise, read everything that arrives until the connection +//| times out. Providing the number of bytes expected is highly recommended +//| because it will be faster. +//| +//| :return: Data read +//| :rtype: bytes or None""" +//| ... +//| +//| def readinto(self, buf: WriteableBuffer) -> Optional[int]: +//| """Read bytes into the ``buf``. Read at most ``len(buf)`` bytes. +//| +//| :return: number of bytes read and stored into ``buf`` +//| :rtype: int or None (on a non-blocking error)""" +//| ... +//| +//| def readline(self) -> bytes: +//| """Read a line, ending in a newline character. +//| +//| :return: the line read +//| :rtype: int or None""" +//| ... +//| + +// These three methods are used by the shared stream methods. +STATIC mp_uint_t bleio_characteristic_buffer_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + raise_error_if_not_connected(self); + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + return common_hal_bleio_characteristic_buffer_read(self, buf, size, errcode); +} + +STATIC mp_uint_t bleio_characteristic_buffer_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + mp_raise_NotImplementedError(translate("CharacteristicBuffer writing not provided")); + return 0; +} + +STATIC mp_uint_t bleio_characteristic_buffer_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + raise_error_if_not_connected(self); + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && common_hal_bleio_characteristic_buffer_rx_characters_available(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } +// No writing provided. +// if ((flags & MP_IOCTL_POLL_WR) && common_hal_busio_uart_ready_to_tx(self)) { +// ret |= MP_IOCTL_POLL_WR; +// } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +//| in_waiting: int +//| """The number of bytes in the input buffer, available to be read""" +//| +STATIC mp_obj_t bleio_characteristic_buffer_obj_get_in_waiting(mp_obj_t self_in) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_buffer_rx_characters_available(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_get_in_waiting_obj, bleio_characteristic_buffer_obj_get_in_waiting); + +MP_PROPERTY_GETTER(bleio_characteristic_buffer_in_waiting_obj, + (mp_obj_t)&bleio_characteristic_buffer_get_in_waiting_obj); + +//| def reset_input_buffer(self) -> None: +//| """Discard any unread characters in the input buffer.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_buffer_obj_reset_input_buffer(mp_obj_t self_in) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_bleio_characteristic_buffer_clear_rx_buffer(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_reset_input_buffer_obj, bleio_characteristic_buffer_obj_reset_input_buffer); + +//| def deinit(self) -> None: +//| """Disable permanently.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_buffer_deinit(mp_obj_t self_in) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_bleio_characteristic_buffer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_deinit_obj, bleio_characteristic_buffer_deinit); + +STATIC const mp_rom_map_elem_t bleio_characteristic_buffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bleio_characteristic_buffer_deinit_obj) }, + + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + // CharacteristicBuffer is currently read-only. + // { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&bleio_characteristic_buffer_reset_input_buffer_obj) }, + // Properties + { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&bleio_characteristic_buffer_in_waiting_obj) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_buffer_locals_dict, bleio_characteristic_buffer_locals_dict_table); + +STATIC const mp_stream_p_t characteristic_buffer_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = bleio_characteristic_buffer_read, + .write = bleio_characteristic_buffer_write, + .ioctl = bleio_characteristic_buffer_ioctl, + .is_text = false, + // Disallow readinto() size parameter. + .pyserial_readinto_compatibility = true, +}; + + +const mp_obj_type_t bleio_characteristic_buffer_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_CharacteristicBuffer, + .make_new = bleio_characteristic_buffer_make_new, + .locals_dict = (mp_obj_dict_t *)&bleio_characteristic_buffer_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &characteristic_buffer_stream_p, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.h b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.h new file mode 100644 index 0000000..de85f01 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.h @@ -0,0 +1,50 @@ +/* + * 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_CHARACTERISTICBUFFER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H + +#include "common-hal/_bleio/CharacteristicBuffer.h" + +extern const mp_obj_type_t bleio_characteristic_buffer_type; + +void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + uint8_t *buffer, size_t buffer_size, + void *static_handler_entry); +void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + size_t buffer_size); +uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode); +uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self); +void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self); +bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self); +void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self); +bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H diff --git a/circuitpython/shared-bindings/_bleio/Connection.c b/circuitpython/shared-bindings/_bleio/Connection.c new file mode 100644 index 0000000..7a85ccd --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Connection.c @@ -0,0 +1,260 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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/Connection.h" + +#include <string.h> +#include <stdio.h> + +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" + +//| class Connection: +//| """A BLE connection to another device. Used to discover and interact with services on the other +//| device. +//| +//| Usage:: +//| +//| import _bleio +//| +//| my_entry = None +//| for entry in _bleio.adapter.scan(2.5): +//| if entry.name is not None and entry.name == 'InterestingPeripheral': +//| my_entry = entry +//| break +//| +//| if not my_entry: +//| raise Exception("'InterestingPeripheral' not found") +//| +//| connection = _bleio.adapter.connect(my_entry.address, timeout=10)""" +//| + +void bleio_connection_ensure_connected(bleio_connection_obj_t *self) { + if (!common_hal_bleio_connection_get_connected(self)) { + mp_raise_ConnectionError(translate("Connection has been disconnected and can no longer be used. Create a new connection.")); + } +} + +//| def __init__(self) -> None: +//| """Connections cannot be made directly. Instead, to initiate a connection use `Adapter.connect`. +//| Connections may also be made when another device initiates a connection. To use a Connection +//| created by a peer, read the `Adapter.connections` property.""" +//| ... +//| +//| def disconnect(self) -> None: +//| """Disconnects from the remote peripheral. Does nothing if already disconnected.""" +//| ... +//| +STATIC mp_obj_t bleio_connection_disconnect(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + // common_hal_bleio_connection_disconnect() does nothing if already disconnected. + common_hal_bleio_connection_disconnect(self->connection); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_disconnect_obj, bleio_connection_disconnect); + + +//| def pair(self, *, bond: bool = True) -> None: +//| """Pair to the peer to improve security.""" +//| ... +//| +STATIC mp_obj_t bleio_connection_pair(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_bond }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bond, MP_ARG_BOOL, {.u_bool = true} }, + }; + + 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); + + bleio_connection_ensure_connected(self); + + common_hal_bleio_connection_pair(self->connection, args[ARG_bond].u_bool); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_connection_pair_obj, 1, bleio_connection_pair); + +//| def discover_remote_services(self, service_uuids_whitelist: Optional[Iterable[UUID]] = None) -> Tuple[Service, ...]: +//| """Do BLE discovery for all services or for the given service UUIDS, +//| to find their handles and characteristics, and return the discovered services. +//| `Connection.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 `_bleio.Service` objects provided by the remote peripheral.""" +//| ... +//| +STATIC mp_obj_t bleio_connection_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_connection_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); + + bleio_connection_ensure_connected(self); + + return MP_OBJ_FROM_PTR(common_hal_bleio_connection_discover_remote_services( + self, + args[ARG_service_uuids_whitelist].u_obj)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_connection_discover_remote_services_obj, 1, bleio_connection_discover_remote_services); + +//| connected: bool +//| """True if connected to the remote peer.""" +//| +STATIC mp_obj_t bleio_connection_get_connected(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_connection_get_connected(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_connected_obj, bleio_connection_get_connected); + +MP_PROPERTY_GETTER(bleio_connection_connected_obj, + (mp_obj_t)&bleio_connection_get_connected_obj); + + +//| paired: bool +//| """True if paired to the remote peer.""" +//| +STATIC mp_obj_t bleio_connection_get_paired(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_connection_get_paired(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_paired_obj, bleio_connection_get_paired); + +MP_PROPERTY_GETTER(bleio_connection_paired_obj, + (mp_obj_t)&bleio_connection_get_paired_obj); + + +//| connection_interval: float +//| """Time between transmissions in milliseconds. Will be multiple of 1.25ms. Lower numbers +//| increase speed and decrease latency but increase power consumption. +//| +//| When setting connection_interval, the peer may reject the new interval and +//| `connection_interval` will then remain the same. +//| +//| Apple has additional guidelines that dictate should be a multiple of 15ms except if HID is +//| available. When HID is available Apple devices may accept 11.25ms intervals.""" +//| +STATIC mp_obj_t bleio_connection_get_connection_interval(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_connection_ensure_connected(self); + return mp_obj_new_float(common_hal_bleio_connection_get_connection_interval(self->connection)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_connection_interval_obj, bleio_connection_get_connection_interval); + +//| max_packet_length: int +//| """The maximum number of data bytes that can be sent in a single transmission, +//| not including overhead bytes. +//| +//| This is the maximum number of bytes that can be sent in a notification, +//| which must be sent in a single packet. +//| But for a regular characteristic read or write, may be sent in multiple packets, +//| so this limit does not apply.""" +//| +STATIC mp_obj_t bleio_connection_get_max_packet_length(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_connection_ensure_connected(self); + return mp_obj_new_int(common_hal_bleio_connection_get_max_packet_length(self->connection)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_max_packet_length_obj, bleio_connection_get_max_packet_length); + + +STATIC mp_obj_t bleio_connection_set_connection_interval(mp_obj_t self_in, mp_obj_t interval_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_float_t interval = mp_obj_get_float(interval_in); + + bleio_connection_ensure_connected(self); + common_hal_bleio_connection_set_connection_interval(self->connection, interval); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_connection_set_connection_interval_obj, bleio_connection_set_connection_interval); + +MP_PROPERTY_GETSET(bleio_connection_connection_interval_obj, + (mp_obj_t)&bleio_connection_get_connection_interval_obj, + (mp_obj_t)&bleio_connection_set_connection_interval_obj); + +MP_PROPERTY_GETTER(bleio_connection_max_packet_length_obj, + (mp_obj_t)&bleio_connection_get_max_packet_length_obj); + +STATIC const mp_rom_map_elem_t bleio_connection_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_pair), MP_ROM_PTR(&bleio_connection_pair_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_connection_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_connection_discover_remote_services_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_connection_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_paired), MP_ROM_PTR(&bleio_connection_paired_obj) }, + { MP_ROM_QSTR(MP_QSTR_connection_interval), MP_ROM_PTR(&bleio_connection_connection_interval_obj) }, + { MP_ROM_QSTR(MP_QSTR_max_packet_length), MP_ROM_PTR(&bleio_connection_max_packet_length_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_connection_locals_dict, bleio_connection_locals_dict_table); + +const mp_obj_type_t bleio_connection_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Connection, + .locals_dict = (mp_obj_dict_t *)&bleio_connection_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_generic_unary_op, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/Connection.h b/circuitpython/shared-bindings/_bleio/Connection.h new file mode 100644 index 0000000..a5313a9 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Connection.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_CONNECTION_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CONNECTION_H + +#include "py/objtuple.h" +#include "common-hal/_bleio/Connection.h" +#include "common-hal/_bleio/Service.h" + +extern const mp_obj_type_t bleio_connection_type; + +void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond); +void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self); +bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self); +mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_internal_t *self); +bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self); +mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist); + +mp_float_t common_hal_bleio_connection_get_connection_interval(bleio_connection_internal_t *self); +void common_hal_bleio_connection_set_connection_interval(bleio_connection_internal_t *self, mp_float_t new_interval); + +void bleio_connection_ensure_connected(bleio_connection_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CONNECTION_H diff --git a/circuitpython/shared-bindings/_bleio/Descriptor.c b/circuitpython/shared-bindings/_bleio/Descriptor.c new file mode 100644 index 0000000..f2ef6b4 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Descriptor.c @@ -0,0 +1,216 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class Descriptor: +//| """Stores information about a BLE descriptor. +//| +//| Descriptors are attached to BLE characteristics and provide contextual +//| information about the characteristic.""" +//| +//| def __init__(self) -> None: +//| """There is no regular constructor for a Descriptor. A new local Descriptor can be created +//| and attached to a Characteristic by calling `add_to_characteristic()`. +//| Remote Descriptor objects are created by `Connection.discover_remote_services()` +//| as part of remote Characteristics in the remote Services that are discovered.""" +//| +//| @classmethod +//| def add_to_characteristic(cls, characteristic: Characteristic, uuid: UUID, *, read_perm: int = Attribute.OPEN, write_perm: int = Attribute.OPEN, max_length: int = 20, fixed_length: bool = False, initial_value: ReadableBuffer = b'') -> Descriptor: +//| """Create a new Descriptor object, and add it to this Service. +//| +//| :param Characteristic characteristic: The characteristic that will hold this descriptor +//| :param 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.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. +//| :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 descriptor 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 descriptor value is of fixed length. +//| :param ~circuitpython_typing.ReadableBuffer initial_value: The initial value for this descriptor. +//| +//| :return: the new Descriptor.""" +//| ... +//| +STATIC mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_characteristic, ARG_uuid, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { 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_QSTR_initial_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, + }; + + 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); + + bleio_characteristic_obj_t *characteristic = mp_arg_validate_type(args[ARG_characteristic].u_obj, &bleio_characteristic_type, MP_QSTR_characteristic); + + bleio_uuid_obj_t *uuid = mp_arg_validate_type(args[ARG_uuid].u_obj, &bleio_uuid_type, MP_QSTR_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); + + const mp_int_t max_length_int = args[ARG_max_length].u_int; + if (max_length_int < 0) { + mp_raise_ValueError(translate("max_length must be >= 0")); + } + const size_t max_length = (size_t)max_length_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + if (initial_value_bufinfo.len > max_length || + (fixed_length && initial_value_bufinfo.len != max_length)) { + mp_raise_ValueError(translate("initial_value length is wrong")); + } + + bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); + descriptor->base.type = &bleio_descriptor_type; + + // 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( + descriptor, characteristic, uuid, + read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo); + + common_hal_bleio_characteristic_add_descriptor(characteristic, descriptor); + + return MP_OBJ_FROM_PTR(descriptor); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_descriptor_add_to_characteristic_fun_obj, 1, bleio_descriptor_add_to_characteristic); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_descriptor_add_to_characteristic_obj, MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_fun_obj)); + +//| uuid: UUID +//| """The descriptor uuid. (read-only)""" +//| +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_uuid_obj_t *uuid = common_hal_bleio_descriptor_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_uuid_obj, bleio_descriptor_get_uuid); + +MP_PROPERTY_GETTER(bleio_descriptor_uuid_obj, + (mp_obj_t)&bleio_descriptor_get_uuid_obj); + +//| characteristic: Characteristic +//| """The Characteristic this Descriptor is a part of.""" +//| +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); + +MP_PROPERTY_GETTER(bleio_descriptor_characteristic_obj, + (mp_obj_t)&bleio_descriptor_get_characteristic_obj); + +//| value: bytearray +//| """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); + + uint8_t temp[512]; + size_t actual_len = common_hal_bleio_descriptor_get_value(self, temp, sizeof(temp)); + return mp_obj_new_bytearray(actual_len, temp); +} +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); + +MP_PROPERTY_GETSET(bleio_descriptor_value_obj, + (mp_obj_t)&bleio_descriptor_get_value_obj, + (mp_obj_t)&bleio_descriptor_set_value_obj); + +STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_to_characteristic), MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_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) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_descriptor_value_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_descriptor_locals_dict, bleio_descriptor_locals_dict_table); + +STATIC void bleio_descriptor_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Descriptor("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, "<Descriptor with Unregistered UUID>"); + } +} + +const mp_obj_type_t bleio_descriptor_type = { + { &mp_type_type }, + .name = MP_QSTR_Descriptor, + .print = bleio_descriptor_print, + .locals_dict = (mp_obj_dict_t *)&bleio_descriptor_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/Descriptor.h b/circuitpython/shared-bindings/_bleio/Descriptor.h new file mode 100644 index 0000000..bda381e --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Descriptor.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_DESCRIPTOR_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H + +#include "shared-module/_bleio/Attribute.h" +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Descriptor.h" +#include "common-hal/_bleio/UUID.h" + +extern const mp_obj_type_t bleio_descriptor_type; + +extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, 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, mp_buffer_info_t *initial_value_bufinfo); +extern bleio_uuid_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 size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t *buf, size_t len); +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 diff --git a/circuitpython/shared-bindings/_bleio/PacketBuffer.c b/circuitpython/shared-bindings/_bleio/PacketBuffer.c new file mode 100644 index 0000000..9e64661 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/PacketBuffer.c @@ -0,0 +1,239 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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/mperrno.h" +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-bindings/util.h" + +//| class PacketBuffer: +//| """Accumulates a Characteristic's incoming packets in a FIFO buffer and facilitates packet aware +//| outgoing writes. A packet's size is either the characteristic length or the maximum transmission +//| unit (MTU) minus overhead, whichever is smaller. The MTU can change so check `incoming_packet_length` +//| and `outgoing_packet_length` before creating a buffer to store data. +//| +//| When we're the server, we ignore all connections besides the first to subscribe to +//| notifications.""" +//| +//| def __init__(self, characteristic: Characteristic, *, buffer_size: int, max_packet_size: Optional[int] = None) -> None: +//| """Monitor the given Characteristic. Each time a new value is written to the Characteristic +//| add the newly-written bytes to a FIFO buffer. +//| +//| Monitor the given Characteristic. Each time a new value is written to the Characteristic +//| add the newly-written packet of bytes to a FIFO buffer. +//| +//| :param Characteristic characteristic: The Characteristic to monitor. +//| It may be a local Characteristic provided by a Peripheral Service, or a remote Characteristic +//| in a remote Service that a Central has connected to. +//| :param int buffer_size: Size of ring buffer (in packets of the Characteristic's maximum +//| length) that stores incoming packets coming from the peer. +//| :param int max_packet_size: Maximum size of packets. Overrides value from the characteristic. +//| (Remote characteristics may not have the correct length.)""" +//| ... +//| +STATIC mp_obj_t bleio_packet_buffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_characteristic, ARG_buffer_size, ARG_max_packet_size }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_max_packet_size, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none}}, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + bleio_characteristic_obj_t *characteristic = mp_arg_validate_type(args[ARG_characteristic].u_obj, &bleio_characteristic_type, MP_QSTR_characteristic); + + const mp_int_t buffer_size = args[ARG_buffer_size].u_int; + if (buffer_size < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_buffer_size); + } + + size_t max_packet_size = common_hal_bleio_characteristic_get_max_length(characteristic); + if (args[ARG_max_packet_size].u_obj != mp_const_none) { + max_packet_size = mp_obj_get_int(args[ARG_max_packet_size].u_obj); + } + + bleio_packet_buffer_obj_t *self = m_new_obj(bleio_packet_buffer_obj_t); + self->base.type = &bleio_packet_buffer_type; + + common_hal_bleio_packet_buffer_construct(self, characteristic, buffer_size, max_packet_size); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void check_for_deinit(bleio_packet_buffer_obj_t *self) { + if (common_hal_bleio_packet_buffer_deinited(self)) { + raise_deinited_error(); + } +} + +//| def readinto(self, buf: WriteableBuffer) -> int: +//| """Reads a single BLE packet into the ``buf``. Raises an exception if the next packet is longer +//| than the given buffer. Use `incoming_packet_length` to read the maximum length of a single packet. +//| +//| :return: number of bytes read and stored into ``buf`` +//| :rtype: int""" +//| ... +//| +STATIC mp_obj_t bleio_packet_buffer_readinto(mp_obj_t self_in, mp_obj_t buffer_obj) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buffer_obj, &bufinfo, MP_BUFFER_WRITE); + + mp_int_t size = common_hal_bleio_packet_buffer_readinto(self, bufinfo.buf, bufinfo.len); + if (size < 0) { + mp_raise_ValueError_varg(translate("Buffer too short by %d bytes"), size * -1); + } + + return MP_OBJ_NEW_SMALL_INT(size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_packet_buffer_readinto_obj, bleio_packet_buffer_readinto); + +//| def write(self, data: ReadableBuffer, *, header: Optional[bytes] = None) -> int: +//| """Writes all bytes from data into the same outgoing packet. The bytes from header are included +//| before data when the pending packet is currently empty. +//| +//| This does not block until the data is sent. It only blocks until the data is pending. +//| +//| :return: number of bytes written. May include header bytes when packet is empty. +//| :rtype: int""" +//| ... +//| +// TODO: Add a kwarg `merge=False` to dictate whether subsequent writes are merged into a pending +// one. +STATIC mp_obj_t bleio_packet_buffer_write(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_data, ARG_header }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_header, MP_ARG_KW_ONLY | 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); + + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + mp_buffer_info_t data_bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); + + mp_buffer_info_t header_bufinfo; + header_bufinfo.len = 0; + if (args[ARG_header].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_header].u_obj, &header_bufinfo, MP_BUFFER_READ); + } + + mp_int_t num_bytes_written = common_hal_bleio_packet_buffer_write( + self, data_bufinfo.buf, data_bufinfo.len, header_bufinfo.buf, header_bufinfo.len); + if (num_bytes_written < 0) { + // TODO: Raise an error if not connected. Right now the not-connected error + // is unreliable, because common_hal_bleio_packet_buffer_write() + // checks for conn_handle being set, but setting that + // can be delayed because conn_handle is discovered by spying on + // gatts write events, which may not have been sent yet. + // + // IDEAL: + // mp_raise_ConnectionError(translate("Not connected")); + // TEMPORARY: + num_bytes_written = 0; + } + return MP_OBJ_NEW_SMALL_INT(num_bytes_written); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_packet_buffer_write_obj, 1, bleio_packet_buffer_write); + +//| def deinit(self) -> None: +//| """Disable permanently.""" +//| ... +STATIC mp_obj_t bleio_packet_buffer_deinit(mp_obj_t self_in) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_bleio_packet_buffer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_packet_buffer_deinit_obj, bleio_packet_buffer_deinit); + +//| incoming_packet_length: int +//| """Maximum length in bytes of a packet we are reading.""" +//| +STATIC mp_obj_t bleio_packet_buffer_get_incoming_packet_length(mp_obj_t self_in) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t size = common_hal_bleio_packet_buffer_get_incoming_packet_length(self); + if (size < 0) { + mp_raise_ValueError(translate("No connection: length cannot be determined")); + } + return MP_OBJ_NEW_SMALL_INT(size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_packet_buffer_get_incoming_packet_length_obj, bleio_packet_buffer_get_incoming_packet_length); + +MP_PROPERTY_GETTER(bleio_packet_buffer_incoming_packet_length_obj, + (mp_obj_t)&bleio_packet_buffer_get_incoming_packet_length_obj); + +//| outgoing_packet_length: int +//| """Maximum length in bytes of a packet we are writing.""" +//| +STATIC mp_obj_t bleio_packet_buffer_get_outgoing_packet_length(mp_obj_t self_in) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t size = common_hal_bleio_packet_buffer_get_outgoing_packet_length(self); + if (size < 0) { + mp_raise_ValueError(translate("No connection: length cannot be determined")); + } + return MP_OBJ_NEW_SMALL_INT(size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_packet_buffer_get_outgoing_packet_length_obj, bleio_packet_buffer_get_outgoing_packet_length); + +MP_PROPERTY_GETTER(bleio_packet_buffer_outgoing_packet_length_obj, + (mp_obj_t)&bleio_packet_buffer_get_outgoing_packet_length_obj); + +STATIC const mp_rom_map_elem_t bleio_packet_buffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bleio_packet_buffer_deinit_obj) }, + + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bleio_packet_buffer_readinto_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&bleio_packet_buffer_write_obj) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_incoming_packet_length), MP_ROM_PTR(&bleio_packet_buffer_incoming_packet_length_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_outgoing_packet_length), MP_ROM_PTR(&bleio_packet_buffer_outgoing_packet_length_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_packet_buffer_locals_dict, bleio_packet_buffer_locals_dict_table); + + +const mp_obj_type_t bleio_packet_buffer_type = { + { &mp_type_type }, + .name = MP_QSTR_PacketBuffer, + .make_new = bleio_packet_buffer_make_new, + .locals_dict = (mp_obj_dict_t *)&bleio_packet_buffer_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/PacketBuffer.h b/circuitpython/shared-bindings/_bleio/PacketBuffer.h new file mode 100644 index 0000000..adead29 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/PacketBuffer.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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_PACKETBUFFER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PACKETBUFFER_H + +#include "common-hal/_bleio/PacketBuffer.h" + +extern const mp_obj_type_t bleio_packet_buffer_type; + +void common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + size_t buffer_size, size_t max_packet_size); +// Allocation free +void _common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + uint32_t *incoming_buffer, size_t incoming_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t outgoing_buffer_size, + void *static_handler_entry); +mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len); +mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len); +mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self); +mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_buffer_obj_t *self); +void common_hal_bleio_packet_buffer_flush(bleio_packet_buffer_obj_t *self); +bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self); +void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PACKETBUFFER_H diff --git a/circuitpython/shared-bindings/_bleio/ScanEntry.c b/circuitpython/shared-bindings/_bleio/ScanEntry.c new file mode 100644 index 0000000..d9434f3 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanEntry.c @@ -0,0 +1,152 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-module/_bleio/ScanEntry.h" + +//| class ScanEntry: +//| """Encapsulates information about a device that was received during scanning. It can be +//| advertisement or scan response data. This object may only be created by a `_bleio.ScanResults`: +//| it has no user-visible constructor.""" +//| + +//| def __init__(self) -> None: +//| """Cannot be instantiated directly. Use `_bleio.Adapter.start_scan`.""" +//| ... +//| +//| def matches(self, prefixes: ScanEntry, *, match_all: bool = True) -> bool: +//| """Returns True if the ScanEntry matches all prefixes when ``match_all`` is True. This is stricter +//| than the scan filtering which accepts any advertisements that match any of the prefixes +//| where ``match_all`` is False. +//| +//| ``all`` also works for ``match_all`` but will be removed in CircuitPython 8.""" +//| ... +//| +STATIC mp_obj_t bleio_scanentry_matches(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_prefixes, ARG_all, ARG_match_all }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_prefixes, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_all, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_match_all, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + }; + + 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); + + bool match_all = args[ARG_all].u_bool && args[ARG_match_all].u_bool; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_prefixes].u_obj, &bufinfo, MP_BUFFER_READ); + return mp_obj_new_bool(common_hal_bleio_scanentry_matches(self, bufinfo.buf, bufinfo.len, match_all)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_scanentry_matches_obj, 1, bleio_scanentry_matches); + +//| address: Address +//| """The address of the device (read-only), of type `_bleio.Address`.""" +//| +STATIC mp_obj_t bleio_scanentry_get_address(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_bleio_scanentry_get_address(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_address_obj, bleio_scanentry_get_address); + +MP_PROPERTY_GETTER(bleio_scanentry_address_obj, + (mp_obj_t)&bleio_scanentry_get_address_obj); + +//| advertisement_bytes: bytes +//| """All the advertisement data present in the packet, returned as a ``bytes`` object. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_advertisement_bytes(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_bleio_scanentry_get_advertisement_bytes(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_advertisement_bytes_obj, scanentry_get_advertisement_bytes); + +MP_PROPERTY_GETTER(bleio_scanentry_advertisement_bytes_obj, + (mp_obj_t)&bleio_scanentry_get_advertisement_bytes_obj); + +//| rssi: int +//| """The signal strength of the device at the time of the scan, in integer dBm. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_rssi(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_bleio_scanentry_get_rssi(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_rssi_obj, scanentry_get_rssi); + +MP_PROPERTY_GETTER(bleio_scanentry_rssi_obj, + (mp_obj_t)&bleio_scanentry_get_rssi_obj); + +//| connectable: bool +//| """True if the device can be connected to. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_connectable(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_bleio_scanentry_get_connectable(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_connectable_obj, scanentry_get_connectable); + +MP_PROPERTY_GETTER(bleio_scanentry_connectable_obj, + (mp_obj_t)&bleio_scanentry_get_connectable_obj); + +//| scan_response: bool +//| """True if the entry was a scan response. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_scan_response(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_bleio_scanentry_get_scan_response(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_scan_response_obj, scanentry_get_scan_response); + +MP_PROPERTY_GETTER(bleio_scanentry_scan_response_obj, + (mp_obj_t)&bleio_scanentry_get_scan_response_obj); + +STATIC const mp_rom_map_elem_t bleio_scanentry_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_scanentry_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_advertisement_bytes), MP_ROM_PTR(&bleio_scanentry_advertisement_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&bleio_scanentry_rssi_obj) }, + { MP_ROM_QSTR(MP_QSTR_connectable), MP_ROM_PTR(&bleio_scanentry_connectable_obj) }, + { MP_ROM_QSTR(MP_QSTR_scan_response), MP_ROM_PTR(&bleio_scanentry_scan_response_obj) }, + { MP_ROM_QSTR(MP_QSTR_matches), MP_ROM_PTR(&bleio_scanentry_matches_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_scanentry_locals_dict, bleio_scanentry_locals_dict_table); + +const mp_obj_type_t bleio_scanentry_type = { + { &mp_type_type }, + .name = MP_QSTR_ScanEntry, + .locals_dict = (mp_obj_dict_t *)&bleio_scanentry_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/ScanEntry.h b/circuitpython/shared-bindings/_bleio/ScanEntry.h new file mode 100644 index 0000000..b376433 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanEntry.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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_SCANENTRY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H + +#include "py/obj.h" +#include "shared-module/_bleio/ScanEntry.h" + +extern const mp_obj_type_t bleio_scanentry_type; + +mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self); +mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self); +mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self); +bool common_hal_bleio_scanentry_get_connectable(bleio_scanentry_obj_t *self); +bool common_hal_bleio_scanentry_get_scan_response(bleio_scanentry_obj_t *self); +bool common_hal_bleio_scanentry_matches(bleio_scanentry_obj_t *self, const uint8_t *prefixes, size_t prefixes_len, bool match_all); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H diff --git a/circuitpython/shared-bindings/_bleio/ScanResults.c b/circuitpython/shared-bindings/_bleio/ScanResults.c new file mode 100644 index 0000000..bee6cfa --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanResults.c @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/ScanResults.h" + +//| class ScanResults: +//| """Iterates over advertising data received while scanning. This object is always created +//| by a `_bleio.Adapter`: it has no user-visible constructor.""" +//| +STATIC mp_obj_t scanresults_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &bleio_scanresults_type)); + bleio_scanresults_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t scan_entry = common_hal_bleio_scanresults_next(self); + if (scan_entry != mp_const_none) { + return scan_entry; + } + return MP_OBJ_STOP_ITERATION; +} + +//| def __init__(self) -> None: +//| """Cannot be instantiated directly. Use `_bleio.Adapter.start_scan`.""" +//| ... +//| +//| def __iter__(self) -> Iterator[ScanEntry]: +//| """Returns itself since it is the iterator.""" +//| ... +//| +//| def __next__(self) -> ScanEntry: +//| """Returns the next `_bleio.ScanEntry`. Blocks if none have been received and scanning is still +//| active. Raises `StopIteration` if scanning is finished and no other results are available.""" +//| ... +//| + +const mp_obj_type_t bleio_scanresults_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_ScanResults, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = scanresults_iternext, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/ScanResults.h b/circuitpython/shared-bindings/_bleio/ScanResults.h new file mode 100644 index 0000000..a8c88c2 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanResults.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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_SCANRESULTS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H + +#include "py/obj.h" +#include "shared-module/_bleio/ScanResults.h" + +extern const mp_obj_type_t bleio_scanresults_type; + +mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H diff --git a/circuitpython/shared-bindings/_bleio/Service.c b/circuitpython/shared-bindings/_bleio/Service.c new file mode 100644 index 0000000..ddbdecd --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Service.c @@ -0,0 +1,154 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class Service: +//| """Stores information about a BLE service and its characteristics.""" +//| +//| def __init__(self, uuid: UUID, *, secondary: bool = False) -> None: +//| """Create a new Service identified by the specified UUID. It can be accessed by all +//| connections. This is known as a Service server. Client Service objects are created via +//| `Connection.discover_remote_services`. +//| +//| To mark the Service as secondary, pass `True` as :py:data:`secondary`. +//| +//| :param UUID uuid: The uuid of the service +//| :param bool secondary: If the service is a secondary one +//| +//| :return: the new Service""" +//| ... +//| +STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_uuid, ARG_secondary }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + bleio_uuid_obj_t *uuid = mp_arg_validate_type(args[ARG_uuid].u_obj, &bleio_uuid_type, MP_QSTR_uuid); + + const bool is_secondary = args[ARG_secondary].u_bool; + + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + + common_hal_bleio_service_construct(service, uuid, is_secondary); + + return MP_OBJ_FROM_PTR(service); +} + +//| characteristics: Tuple[Characteristic, ...] +//| """A tuple of :py:class:`Characteristic` designating the characteristics that are offered by +//| this service. (read-only)""" +//| +STATIC mp_obj_t bleio_service_get_characteristics(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(common_hal_bleio_service_get_characteristics(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_characteristics_obj, bleio_service_get_characteristics); + +MP_PROPERTY_GETTER(bleio_service_characteristics_obj, + (mp_obj_t)&bleio_service_get_characteristics_obj); + +//| remote: bool +//| """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); + +MP_PROPERTY_GETTER(bleio_service_remote_obj, + (mp_obj_t)&bleio_service_get_remote_obj); + +//| secondary: bool +//| """True if this is a secondary service. (read-only)""" +//| +STATIC mp_obj_t bleio_service_get_secondary(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_secondary(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_secondary_obj, bleio_service_get_secondary); + +MP_PROPERTY_GETTER(bleio_service_secondary_obj, + (mp_obj_t)&bleio_service_get_secondary_obj); + +//| uuid: Optional[UUID] +//| """The UUID of this service. (read-only) +//| +//| Will be ``None`` if the 128-bit UUID for this service is not known.""" +//| +STATIC mp_obj_t bleio_service_get_uuid(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_service_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_uuid_obj, bleio_service_get_uuid); + +MP_PROPERTY_GETTER(bleio_service_uuid_obj, + (mp_obj_t)&bleio_service_get_uuid_obj); + + +STATIC const mp_rom_map_elem_t bleio_service_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) }, + { MP_ROM_QSTR(MP_QSTR_secondary), MP_ROM_PTR(&bleio_service_secondary_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_remote), MP_ROM_PTR(&bleio_service_remote_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict_table); + +STATIC void bleio_service_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Service("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, "<Service with unregistered UUID>"); + } +} + +const mp_obj_type_t bleio_service_type = { + { &mp_type_type }, + .name = MP_QSTR_Service, + .make_new = bleio_service_make_new, + .print = bleio_service_print, + .locals_dict = (mp_obj_dict_t *)&bleio_service_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/Service.h b/circuitpython/shared-bindings/_bleio/Service.h new file mode 100644 index 0000000..eb1d29b --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Service.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SERVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H + +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Connection.h" +#include "common-hal/_bleio/Service.h" + +#include "py/objtuple.h" + +extern const mp_obj_type_t bleio_service_type; + +// Private version that doesn't allocate on the heap +extern uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t *characteristic_list); +extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary); +extern void common_hal_bleio_service_from_remote_service(bleio_service_obj_t *self, bleio_connection_obj_t *connection, bleio_uuid_obj_t *uuid, bool is_secondary); +extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self); +extern mp_obj_tuple_t *common_hal_bleio_service_get_characteristics(bleio_service_obj_t *self); +extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self); +extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self); +extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo, const char *user_description); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H diff --git a/circuitpython/shared-bindings/_bleio/UUID.c b/circuitpython/shared-bindings/_bleio/UUID.c new file mode 100644 index 0000000..599a89a --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/UUID.c @@ -0,0 +1,300 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class UUID: +//| """A 16-bit or 128-bit UUID. Can be used for services, characteristics, descriptors and more.""" +//| +//| def __init__(self, value: Union[int, ReadableBuffer, str]) -> None: +//| """Create a new UUID or UUID object encapsulating the uuid value. +//| The value can be one of: +//| +//| - an `int` value in range 0 to 0xFFFF (Bluetooth SIG 16-bit UUID) +//| - a buffer object (bytearray, bytes) of 16 bytes in little-endian order (128-bit UUID) +//| - a string of hex digits of the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +//| +//| Creating a 128-bit UUID registers the UUID with the onboard BLE software, and provides a +//| temporary 16-bit UUID that can be used in place of the full 128-bit UUID. +//| +//| :param value: The uuid value to encapsulate +//| :type value: int, ~circuitpython_typing.ReadableBuffer or str""" +//| ... +//| +STATIC mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + bleio_uuid_obj_t *self = m_new_obj(bleio_uuid_obj_t); + self->base.type = type; + + const mp_obj_t value = all_args[0]; + uint8_t uuid128[16]; + + if (mp_obj_is_int(value)) { + mp_int_t uuid16 = mp_obj_get_int(value); + if (uuid16 < 0 || uuid16 > 0xffff) { + mp_raise_ValueError(translate("UUID integer value must be 0-0xffff")); + } + + // NULL means no 128-bit value. + common_hal_bleio_uuid_construct(self, uuid16, NULL); + + } else { + if (mp_obj_is_str(value)) { + // 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + GET_STR_DATA_LEN(value, chars, len); + char hex[32]; + // Validate length, hyphens, and hex digits. + bool good_uuid = + len == 36 && chars[8] == '-' && chars[13] == '-' && chars[18] == '-' && chars[23] == '-'; + if (good_uuid) { + size_t hex_idx = 0; + for (size_t i = 0; i < len; i++) { + if (unichar_isxdigit(chars[i])) { + hex[hex_idx] = chars[i]; + hex_idx++; + } + } + good_uuid = hex_idx == 32; + } + if (!good_uuid) { + mp_raise_ValueError(translate("UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'")); + } + + size_t hex_idx = 0; + for (int i = 15; i >= 0; i--) { + uuid128[i] = (unichar_xdigit_value(hex[hex_idx]) << 4) | unichar_xdigit_value(hex[hex_idx + 1]); + hex_idx += 2; + } + } else { + // Last possibility is that it's a buf. + mp_buffer_info_t bufinfo; + if (!mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) { + mp_raise_ValueError(translate("UUID value is not str, int or byte buffer")); + } + + if (bufinfo.len != 16) { + mp_raise_ValueError(translate("Byte buffer must be 16 bytes.")); + } + + memcpy(uuid128, bufinfo.buf, 16); + } + + // Str and bytes both get constructed the same way here. + uint32_t uuid16 = (uuid128[13] << 8) | uuid128[12]; + uuid128[12] = 0; + uuid128[13] = 0; + common_hal_bleio_uuid_construct(self, uuid16, uuid128); + } + + return MP_OBJ_FROM_PTR(self); +} + +//| uuid16: int +//| """The 16-bit part of the UUID. (read-only) +//| +//| :type: int""" +//| +STATIC mp_obj_t bleio_uuid_get_uuid16(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid16_obj, bleio_uuid_get_uuid16); + +MP_PROPERTY_GETTER(bleio_uuid_uuid16_obj, + (mp_obj_t)&bleio_uuid_get_uuid16_obj); + +//| uuid128: bytes +//| """The 128-bit value of the UUID +//| Raises AttributeError if this is a 16-bit UUID. (read-only) +//| +//| :type: bytes""" +//| +STATIC mp_obj_t bleio_uuid_get_uuid128(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uint8_t uuid128[16]; + if (common_hal_bleio_uuid_get_size(self) != 128) { + mp_raise_AttributeError(translate("not a 128-bit UUID")); + } + common_hal_bleio_uuid_get_uuid128(self, uuid128); + return mp_obj_new_bytes(uuid128, 16); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid128_obj, bleio_uuid_get_uuid128); + +MP_PROPERTY_GETTER(bleio_uuid_uuid128_obj, + (mp_obj_t)&bleio_uuid_get_uuid128_obj); + +//| size: int +//| """128 if this UUID represents a 128-bit vendor-specific UUID. 16 if this UUID represents a +//| 16-bit Bluetooth SIG assigned UUID. (read-only) 32-bit UUIDs are not currently supported. +//| +//| :type: int""" +//| +STATIC mp_obj_t bleio_uuid_get_size(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_size(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_size_obj, bleio_uuid_get_size); + +MP_PROPERTY_GETTER(bleio_uuid_size_obj, + (mp_obj_t)&bleio_uuid_get_size_obj); + + +//| def pack_into(self, buffer: WriteableBuffer, offset: int = 0) -> None: +//| """Packs the UUID into the given buffer at the given offset.""" +//| ... +//| +STATIC mp_obj_t bleio_uuid_pack_into(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_buffer, ARG_offset }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + 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); + + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + + size_t offset = args[ARG_offset].u_int; + if (offset + common_hal_bleio_uuid_get_size(self) / 8 > bufinfo.len) { + mp_raise_ValueError(translate("Buffer + offset too small %d %d %d")); + } + + common_hal_bleio_uuid_pack_into(self, bufinfo.buf + offset); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_uuid_pack_into_obj, 1, bleio_uuid_pack_into); + +STATIC const mp_rom_map_elem_t bleio_uuid_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_uuid16), MP_ROM_PTR(&bleio_uuid_uuid16_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid128), MP_ROM_PTR(&bleio_uuid_uuid128_obj) }, + { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&bleio_uuid_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&bleio_uuid_pack_into_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_uuid_locals_dict, bleio_uuid_locals_dict_table); + +STATIC mp_obj_t bleio_uuid_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_HASH: + if (common_hal_bleio_uuid_get_size(self) == 16) { + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); + } else { + union { + uint8_t uuid128_bytes[16]; + uint16_t uuid128_uint16[8]; + } uuid128; + common_hal_bleio_uuid_get_uuid128(self, uuid128.uuid128_bytes); + int hash = 0; + for (size_t i = 0; i < MP_ARRAY_SIZE(uuid128.uuid128_uint16); i++) { + hash += uuid128.uuid128_uint16[i]; + } + return MP_OBJ_NEW_SMALL_INT(hash); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __eq__(self, other: object) -> bool: +//| """Two UUID objects are equal if their values match and they are both 128-bit or both 16-bit.""" +//| ... +//| +STATIC mp_obj_t bleio_uuid_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + // Two UUID's are equal if their uuid16 values match or their uuid128 values match. + case MP_BINARY_OP_EQUAL: + if (mp_obj_is_type(rhs_in, &bleio_uuid_type)) { + if (common_hal_bleio_uuid_get_size(lhs_in) == 16 && + common_hal_bleio_uuid_get_size(rhs_in) == 16) { + return mp_obj_new_bool(common_hal_bleio_uuid_get_uuid16(lhs_in) == + common_hal_bleio_uuid_get_uuid16(rhs_in)); + } + uint8_t lhs[16]; + uint8_t rhs[16]; + common_hal_bleio_uuid_get_uuid128(lhs_in, lhs); + common_hal_bleio_uuid_get_uuid128(rhs_in, rhs); + return mp_obj_new_bool(memcmp(lhs, rhs, sizeof(lhs)) == 0); + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t size = common_hal_bleio_uuid_get_size(self); + if (size == 16) { + mp_printf(print, "UUID(0x%04x)", common_hal_bleio_uuid_get_uuid16(self)); + } else { + uint8_t uuid128[16]; + common_hal_bleio_uuid_get_uuid128(self, uuid128); + mp_printf(print, "UUID('" + "%02x%02x%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x%02x%02x%02x%02x')", + uuid128[15], uuid128[14], uuid128[13], uuid128[12], + uuid128[11], uuid128[10], + uuid128[9], uuid128[8], + uuid128[7], uuid128[6], + uuid128[5], uuid128[4], uuid128[3], uuid128[2], uuid128[1], uuid128[0]); + } +} + +const mp_obj_type_t bleio_uuid_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_UUID, + .print = bleio_uuid_print, + .make_new = bleio_uuid_make_new, + .locals_dict = (mp_obj_dict_t *)&bleio_uuid_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = bleio_uuid_unary_op, + .binary_op = bleio_uuid_binary_op, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/UUID.h b/circuitpython/shared-bindings/_bleio/UUID.h new file mode 100644 index 0000000..d07e903 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/UUID.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_UUID_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H + +#include "common-hal/_bleio/UUID.h" + +void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); + +extern const mp_obj_type_t bleio_uuid_type; + +extern void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, const uint8_t uuid128[16]); +extern uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self); +extern void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]); +extern uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self); + +void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t *buf); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H diff --git a/circuitpython/shared-bindings/_bleio/__init__.c b/circuitpython/shared-bindings/_bleio/__init__.c new file mode 100644 index 0000000..72d9684 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/__init__.c @@ -0,0 +1,205 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 <stdarg.h> + +#include "py/objexcept.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/ScanResults.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| """Bluetooth Low Energy (BLE) communication +//| +//| The `_bleio` module provides necessary low-level functionality for communicating +//| using Bluetooth Low Energy (BLE). The '_' prefix indicates this module is meant +//| for internal use by libraries but not by the end user. Its API may change incompatibly +//| between minor versions of CircuitPython. +//| Please use the +//| `adafruit_ble <https://circuitpython.readthedocs.io/projects/ble/en/latest/>`_ +//| CircuitPython library instead, which builds on `_bleio`, and +//| provides higher-level convenience functionality, including predefined beacons, clients, +//| servers.""" +//| + +//| adapter: Adapter +//| """BLE Adapter used to manage device discovery and connections. +//| This object is the sole instance of `_bleio.Adapter`.""" +//| + +//| class BluetoothError(Exception): +//| """Catchall exception for Bluetooth related errors.""" +//| ... +MP_DEFINE_BLEIO_EXCEPTION(BluetoothError, Exception) +NORETURN void mp_raise_bleio_BluetoothError(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_BluetoothError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +//| class RoleError(BluetoothError): +//| """Raised when a resource is used as the mismatched role. For example, if a local CCCD is +//| attempted to be set but they can only be set when remote.""" +//| ... +//| +MP_DEFINE_BLEIO_EXCEPTION(RoleError, bleio_BluetoothError) +NORETURN void mp_raise_bleio_RoleError(const compressed_string_t *msg) { + mp_raise_msg(&mp_type_bleio_RoleError, msg); +} + +//| class SecurityError(BluetoothError): +//| """Raised when a security related error occurs.""" +//| ... +//| +MP_DEFINE_BLEIO_EXCEPTION(SecurityError, bleio_BluetoothError) +NORETURN void mp_raise_bleio_SecurityError(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_SecurityError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +// Called when _bleio is imported. +STATIC mp_obj_t bleio___init__(void) { +// HCI cannot be enabled on import, because we need to setup the HCI adapter first. + #if !CIRCUITPY_BLEIO_HCI + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true); + #endif + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(bleio___init___obj, bleio___init__); + + +// Need a forward reference due to mutual references. +#if CIRCUITPY_BLEIO_HCI +STATIC mp_obj_dict_t bleio_module_globals; +#endif + +//| def set_adapter(adapter: Optional[_bleio.Adapter]) -> None: +//| """Set the adapter to use for BLE, such as when using an HCI adapter. +//| Raises `NotImplementedError` when the adapter is a singleton and cannot be set.""" +//| ... +//| +mp_obj_t bleio_set_adapter(mp_obj_t adapter_obj) { + #if CIRCUITPY_BLEIO_HCI + if (adapter_obj != mp_const_none && !mp_obj_is_type(adapter_obj, &bleio_adapter_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), bleio_adapter_type.name); + } + + // Equivalent of: + // bleio.adapter = adapter_obj + mp_map_elem_t *elem = mp_map_lookup(&bleio_module_globals.map, MP_ROM_QSTR(MP_QSTR_adapter), MP_MAP_LOOKUP); + if (elem) { + elem->value = adapter_obj; + } + #else + mp_raise_NotImplementedError(translate("Not settable")); + #endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_set_adapter_obj, bleio_set_adapter); + +#if CIRCUITPY_BLEIO_HCI +// Make the module dictionary be in RAM, so that _bleio.adapter can be set. +// Use a local macro to define how table entries should be converted. +#define OBJ_FROM_PTR MP_OBJ_FROM_PTR +STATIC mp_map_elem_t bleio_module_globals_table[] = { +#else +#define OBJ_FROM_PTR MP_ROM_PTR +STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { + #endif + // Name must be the first entry so that the exception printing below is correct. + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__bleio) }, + { MP_ROM_QSTR(MP_QSTR_Adapter), OBJ_FROM_PTR(&bleio_adapter_type) }, + { MP_ROM_QSTR(MP_QSTR_Address), OBJ_FROM_PTR(&bleio_address_type) }, + { MP_ROM_QSTR(MP_QSTR_Attribute), OBJ_FROM_PTR(&bleio_attribute_type) }, + { MP_ROM_QSTR(MP_QSTR_Connection), OBJ_FROM_PTR(&bleio_connection_type) }, + { MP_ROM_QSTR(MP_QSTR_Characteristic), OBJ_FROM_PTR(&bleio_characteristic_type) }, + { MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), OBJ_FROM_PTR(&bleio_characteristic_buffer_type) }, + { MP_ROM_QSTR(MP_QSTR_Descriptor), OBJ_FROM_PTR(&bleio_descriptor_type) }, + { MP_ROM_QSTR(MP_QSTR_PacketBuffer), OBJ_FROM_PTR(&bleio_packet_buffer_type) }, + { MP_ROM_QSTR(MP_QSTR_ScanEntry), OBJ_FROM_PTR(&bleio_scanentry_type) }, + { MP_ROM_QSTR(MP_QSTR_ScanResults), OBJ_FROM_PTR(&bleio_scanresults_type) }, + { MP_ROM_QSTR(MP_QSTR_Service), OBJ_FROM_PTR(&bleio_service_type) }, + { MP_ROM_QSTR(MP_QSTR_UUID), OBJ_FROM_PTR(&bleio_uuid_type) }, + + #if CIRCUITPY_BLEIO_HCI + // For HCI, _bleio.adapter is settable, and starts as None. + { MP_ROM_QSTR(MP_QSTR_adapter), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_set_adapter), (mp_obj_t)&bleio_set_adapter_obj }, + #else + // For non-HCI _bleio.adapter is a fixed singleton, and is not settable. + // _bleio.set_adapter will raise NotImplementedError. + { MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_adapter), MP_ROM_PTR(&bleio_set_adapter_obj) }, + #endif + + // Errors + { MP_ROM_QSTR(MP_QSTR_BluetoothError), OBJ_FROM_PTR(&mp_type_bleio_BluetoothError) }, + { MP_ROM_QSTR(MP_QSTR_RoleError), OBJ_FROM_PTR(&mp_type_bleio_RoleError) }, + { MP_ROM_QSTR(MP_QSTR_SecurityError), OBJ_FROM_PTR(&mp_type_bleio_SecurityError) }, + + // Initialization + { MP_ROM_QSTR(MP_QSTR___init__), OBJ_FROM_PTR(&bleio___init___obj) }, +}; + +#if CIRCUITPY_BLEIO_HCI +// Module dict is mutable to allow setting _bleio.adapter. +STATIC MP_DEFINE_MUTABLE_DICT(bleio_module_globals, bleio_module_globals_table); +#else +STATIC MP_DEFINE_CONST_DICT(bleio_module_globals, bleio_module_globals_table); +#endif + +void bleio_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; + bool is_subclass = kind & PRINT_EXC_SUBCLASS; + if (!is_subclass && (k == PRINT_EXC)) { + mp_print_str(print, qstr_str(MP_OBJ_QSTR_VALUE(bleio_module_globals_table[0].value))); + mp_print_str(print, "."); + } + mp_obj_exception_print(print, o_in, kind); +} + +const mp_obj_module_t bleio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&bleio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR__bleio, bleio_module, CIRCUITPY_BLEIO); diff --git a/circuitpython/shared-bindings/_bleio/__init__.h b/circuitpython/shared-bindings/_bleio/__init__.h new file mode 100644 index 0000000..f34df30 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/__init__.h @@ -0,0 +1,83 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H + +#include "py/objlist.h" + +#include "shared-bindings/_bleio/Adapter.h" + +#include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/Adapter.h" + +extern bleio_adapter_obj_t common_hal_bleio_adapter_obj; + +void bleio_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); + +#define MP_DEFINE_BLEIO_EXCEPTION(exc_name, base_name) \ + const mp_obj_type_t mp_type_bleio_##exc_name = { \ + { &mp_type_type }, \ + .name = MP_QSTR_##exc_name, \ + .print = bleio_exception_print, \ + .make_new = mp_obj_exception_make_new, \ + .attr = mp_obj_exception_attr, \ + .parent = &mp_type_##base_name, \ + }; + +extern const mp_obj_type_t mp_type_bleio_BluetoothError; +extern const mp_obj_type_t mp_type_bleio_RoleError; +extern const mp_obj_type_t mp_type_bleio_SecurityError; + +// Resets all user created BLE state in preparation for the heap disappearing. +// It will maintain BLE workflow and connections. +void bleio_user_reset(void); + +// Completely resets the BLE stack including BLE connections. +void bleio_reset(void); + +extern mp_obj_t bleio_set_adapter(mp_obj_t adapter_obj); + +NORETURN void mp_raise_bleio_BluetoothError(const compressed_string_t *msg, ...); +NORETURN void mp_raise_bleio_RoleError(const compressed_string_t *msg); +NORETURN void mp_raise_bleio_SecurityError(const compressed_string_t *msg, ...); + +bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void); +void common_hal_bleio_check_connected(uint16_t conn_handle); + +uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); +void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); + +size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); +size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); + +void common_hal_bleio_gc_collect(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H |