From 28ca05ccdcf674ad128f357ac8f54a1931560331 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 27 Jul 2019 13:20:59 -0400 Subject: allow discovery from central or peripheral --- shared-bindings/bleio/Central.c | 77 +++++++++++++++++++++++++++----------- shared-bindings/bleio/Central.h | 2 +- shared-bindings/bleio/Peripheral.c | 72 +++++++++++++++++++++++++++++------ shared-bindings/bleio/Peripheral.h | 2 +- shared-bindings/bleio/Service.c | 22 +++++++++++ shared-bindings/bleio/Service.h | 1 + shared-bindings/bleio/__init__.h | 11 ++++++ 7 files changed, 151 insertions(+), 36 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bleio/Central.c b/shared-bindings/bleio/Central.c index fc00bce5c..70b270c65 100644 --- a/shared-bindings/bleio/Central.c +++ b/shared-bindings/bleio/Central.c @@ -34,6 +34,7 @@ #include "py/objproperty.h" #include "py/objstr.h" #include "py/runtime.h" +#include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/Characteristic.h" @@ -56,12 +57,16 @@ //| //| my_entry = None //| for entry in entries: -//| if entry.name is not None and entry.name == 'MyPeripheral': +//| if entry.name is not None and entry.name == 'InterestingPeripheral': //| my_entry = entry //| break //| -//| central = bleio.Central(my_entry.address) -//| central.connect(10.0) # timeout after 10 seconds +//| if not my_entry: +//| raise Exception("'InterestingPeripheral' not found") +//| +//| central = bleio.Central() +//| central.connect(my_entry.address, 10) # timeout after 10 seconds +//| central.discover_remote_services() //| //| .. class:: Central() @@ -79,24 +84,11 @@ STATIC mp_obj_t bleio_central_make_new(const mp_obj_type_t *type, size_t n_args, return MP_OBJ_FROM_PTR(self); } -//| .. method:: connect(address, timeout, *, service_uuids=None) -//| Attempts a connection to the remote peripheral. If the connection is successful, -//| Do BLE discovery for the listed services, to find their handles and characteristics. -//| The attribute `remote_services` will contain a list of all discovered services. +//| .. method:: connect(address, timeout, *, service_uuids_whitelist=None) +//| Attempts a connection to the remote peripheral. //| //| :param bleio.Address address: The address of the peripheral to connect to //| :param float/int timeout: Try to connect for timeout seconds. -//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services -//| provided by the peripheral that you want to use. -//| The peripheral may provide more services, but services not listed are ignored. -//| If a service in service_uuids is not found during discovery, it will not -//| appear in `remote_services`. -//| -//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow. -//| -//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you -//| you must have already created a :py:class:~`UUID` object for that UUID in order for the -//| service or characteristic to be discovered. (This restriction may be lifted in the future.) //| STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); @@ -105,7 +97,6 @@ STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args static const mp_arg_t allowed_args[] = { { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_service_uuids_whitelist, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -119,7 +110,7 @@ STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); // common_hal_bleio_central_connect() will validate that services is an iterable or None. - common_hal_bleio_central_connect(self, address, timeout, args[ARG_service_uuids_whitelist].u_obj); + common_hal_bleio_central_connect(self, address, timeout); return mp_const_none; } @@ -139,6 +130,47 @@ STATIC mp_obj_t bleio_central_disconnect(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_disconnect_obj, bleio_central_disconnect); +//| .. method:: discover_remote_services(service_uuids_whitelist=None) +//| Do BLE discovery for all services or for the given service UUIDS, +//| to find their handles and characteristics. +//| The attribute `remote_services` will contain a list of all discovered services. +//| `Central.connected` must be True. +//| +//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services +//| provided by the peripheral that you want to use. +//| The peripheral may provide more services, but services not listed are ignored. +//| If a service in service_uuids_whitelist is not found during discovery, it will not +//| appear in `remote_services`. +//| +//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow. +//| +//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you +//| you must have already created a :py:class:~`UUID` object for that UUID in order for the +//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered +//| for use. (This restriction may be lifted in the future.) +//| +STATIC mp_obj_t bleio_central_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_service_uuids_whitelist }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (!common_hal_bleio_central_get_connected(self)) { + mp_raise_ValueError(translate("Not connected")); + } + + common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), + args[ARG_service_uuids_whitelist].u_obj); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_discover_remote_services_obj, 1, bleio_central_discover_remote_services); + //| .. attribute:: connected //| //| True if connected to a remove peripheral. @@ -181,8 +213,9 @@ const mp_obj_property_t bleio_central_remote_services_obj = { STATIC const mp_rom_map_elem_t bleio_central_locals_dict_table[] = { // Methods - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) }, - { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_central_discover_remote_services_obj) }, // Properties { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_central_connected_obj) }, diff --git a/shared-bindings/bleio/Central.h b/shared-bindings/bleio/Central.h index 0e84037f9..1d7fb6248 100644 --- a/shared-bindings/bleio/Central.h +++ b/shared-bindings/bleio/Central.h @@ -34,7 +34,7 @@ extern const mp_obj_type_t bleio_central_type; extern void common_hal_bleio_central_construct(bleio_central_obj_t *self); -extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout, mp_obj_t service_uuids); +extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout); extern void common_hal_bleio_central_disconnect(bleio_central_obj_t *self); extern bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self); extern mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self); diff --git a/shared-bindings/bleio/Peripheral.c b/shared-bindings/bleio/Peripheral.c index 1302c9652..69bc72d92 100644 --- a/shared-bindings/bleio/Peripheral.c +++ b/shared-bindings/bleio/Peripheral.c @@ -35,6 +35,7 @@ #include "py/objstr.h" #include "py/runtime.h" +#include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Peripheral.h" @@ -107,15 +108,15 @@ STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_ar self->base.type = &bleio_peripheral_type; // Copy the services list and validate its items. - mp_obj_t service_list_obj = mp_obj_new_list(0, NULL); - mp_obj_list_t *service_list = MP_OBJ_FROM_PTR(service_list_obj); + mp_obj_t services_list_obj = mp_obj_new_list(0, NULL); + mp_obj_list_t *services_list = MP_OBJ_FROM_PTR(services_list_obj); mp_obj_t service; while ((service = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { if (!MP_OBJ_IS_TYPE(service, &bleio_service_type)) { mp_raise_ValueError(translate("non-Service found in services")); } - mp_obj_list_append(service_list, service); + mp_obj_list_append(services_list, service); } const mp_obj_t name = args[ARG_name].u_obj; @@ -128,7 +129,7 @@ STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_ar mp_raise_ValueError(translate("name must be a string")); } - common_hal_bleio_peripheral_construct(self, service_list, name_str); + common_hal_bleio_peripheral_construct(self, services_list, name_str); return MP_OBJ_FROM_PTR(self); } @@ -158,8 +159,8 @@ const mp_obj_property_t bleio_peripheral_connected_obj = { STATIC mp_obj_t bleio_peripheral_get_services(mp_obj_t self_in) { bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); // Return list as a tuple so user won't be able to change it. - mp_obj_list_t *service_list = common_hal_bleio_peripheral_get_service_list(self); - return mp_obj_new_tuple(service_list->len, service_list->items); + mp_obj_list_t *services_list = common_hal_bleio_peripheral_get_services_list(self); + return mp_obj_new_tuple(services_list->len, services_list->items); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services); @@ -265,16 +266,63 @@ STATIC mp_obj_t bleio_peripheral_disconnect(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_disconnect_obj, bleio_peripheral_disconnect); +//| .. method:: discover_remote_services(service_uuids_whitelist=None) +//| Do BLE discovery for all services or for the given service UUIDS, +//| to find their handles and characteristics. +//| The attribute `remote_services` will contain a list of all discovered services. +//| `Peripheral.connected` must be True. +//| +//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services +//| provided by the peripheral that you want to use. +//| The peripheral may provide more services, but services not listed are ignored. +//| If a service in service_uuids_whitelist is not found during discovery, it will not +//| appear in `remote_services`. +//| +//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow. +//| +//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you +//| you must have already created a :py:class:~`UUID` object for that UUID in order for the +//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered +//| for use. (This restriction may be lifted in the future.) +//| +//| Thought it is unusual for a peripheral to act as a BLE client, it can do so, and +//| needs to be able to do discovery on its peer (a central). +//| Examples include a peripheral accessing a central that provides Current Time Service, +//| Apple Notification Center Service, or Battery Service. +//| +STATIC mp_obj_t bleio_peripheral_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_service_uuids_whitelist }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (!common_hal_bleio_peripheral_get_connected(self)) { + mp_raise_ValueError(translate("Not connected")); + } + + common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), + args[ARG_service_uuids_whitelist].u_obj); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_discover_remote_services_obj, 1, bleio_peripheral_discover_remote_services); + STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = { // Methods - { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) }, - { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_peripheral_discover_remote_services_obj) }, // Properties - { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) }, - { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) }, - { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) }, + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) }, + { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) }, }; STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table); diff --git a/shared-bindings/bleio/Peripheral.h b/shared-bindings/bleio/Peripheral.h index 846250a5f..7dad0bccd 100644 --- a/shared-bindings/bleio/Peripheral.h +++ b/shared-bindings/bleio/Peripheral.h @@ -33,7 +33,7 @@ extern const mp_obj_type_t bleio_peripheral_type; extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *service_list, mp_obj_t name); -extern mp_obj_list_t *common_hal_bleio_peripheral_get_service_list(bleio_peripheral_obj_t *self); +extern mp_obj_list_t *common_hal_bleio_peripheral_get_services_list(bleio_peripheral_obj_t *self); extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self); extern mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self); extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, float interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo); diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c index db0606991..b4aeab2bd 100644 --- a/shared-bindings/bleio/Service.c +++ b/shared-bindings/bleio/Service.c @@ -43,12 +43,16 @@ //| .. class:: Service(uuid, characteristics, *, secondary=False) //| //| Create a new Service object identified by the specified UUID. +//| //| To mark the service as secondary, pass `True` as :py:data:`secondary`. //| //| :param bleio.UUID uuid: The uuid of the service //| :param iterable characteristics: the Characteristic objects for this service //| :param bool secondary: If the service is a secondary one //| +//| A Service may be remote (:py:data:`remote` is ``True``), but a remote Service +//| cannot be constructed directly. It is created by `Central.discover_remote_services()` +//| or `Peripheral.discover_remote_services()`. STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_uuid, ARG_characteristics, ARG_secondary }; @@ -119,6 +123,24 @@ const mp_obj_property_t bleio_service_characteristics_obj = { (mp_obj_t)&mp_const_none_obj }, }; +//| .. attribute:: remote +//| +//| True if this is a service provided by a remote device. (read-only) +//| +STATIC mp_obj_t bleio_service_get_remote(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_service_get_is_remote(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_remote_obj, bleio_service_get_remote); + +const mp_obj_property_t bleio_service_remote_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_service_get_remote_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + //| .. attribute:: secondary //| //| True if this is a secondary service. (read-only) diff --git a/shared-bindings/bleio/Service.h b/shared-bindings/bleio/Service.h index f646c81a9..716c2e8a9 100644 --- a/shared-bindings/bleio/Service.h +++ b/shared-bindings/bleio/Service.h @@ -35,6 +35,7 @@ const mp_obj_type_t bleio_service_type; extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, mp_obj_list_t *characteristic_list, bool is_secondary); extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self); extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self); +extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self); extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self); extern void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self); diff --git a/shared-bindings/bleio/__init__.h b/shared-bindings/bleio/__init__.h index 4aa6dd45b..dd6e55204 100644 --- a/shared-bindings/bleio/__init__.h +++ b/shared-bindings/bleio/__init__.h @@ -29,8 +29,19 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H +#include "py/objlist.h" + +#include "shared-bindings/bleio/__init__.h" +#include "shared-bindings/bleio/Adapter.h" + +#include "shared-module/bleio/__init__.h" #include "common-hal/bleio/Adapter.h" extern const super_adapter_obj_t common_hal_bleio_adapter_obj; +extern uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); +extern mp_obj_list_t *common_hal_bleio_device_get_remote_services_list(mp_obj_t device); +extern void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); + + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H -- cgit v1.2.3