From a7a24096f469b1ee95242d4e7f58929cad3699ed Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 7 Dec 2018 16:52:47 -0500 Subject: bleio WIP: redo for more immutability; use sd_* routines for internal flash write --- shared-module/bleio/Characteristic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shared-module') diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h index 94c43f81e..977ec8197 100644 --- a/shared-module/bleio/Characteristic.h +++ b/shared-module/bleio/Characteristic.h @@ -39,7 +39,7 @@ typedef struct { struct { bool broadcast : 1; bool read : 1; - bool write_wo_resp : 1; + bool write_no_response : 1; bool write : 1; bool notify : 1; bool indicate : 1; -- cgit v1.2.3 From 4167bf5b241eec4e5a04c36d7f7c66d43a8bb921 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 27 Dec 2018 00:04:04 -0500 Subject: wip: advertising works, but not connection --- docs/drivers.rst | 2 +- ports/atmel-samd/Makefile | 7 ++- ports/nrf/Makefile | 14 +++-- ports/nrf/common-hal/bleio/Characteristic.c | 76 +++++++++++++---------- ports/nrf/common-hal/bleio/Scanner.c | 4 +- ports/nrf/common-hal/bleio/Service.c | 93 ++++++++++++++++------------- ports/nrf/common-hal/bleio/UUID.c | 4 +- ports/nrf/common-hal/bleio/__init__.c | 24 ++++++++ py/runtime.c | 8 +++ py/runtime.h | 1 + shared-bindings/bleio/Address.c | 2 +- shared-bindings/bleio/Descriptor.c | 10 +--- shared-bindings/bleio/Device.c | 2 +- shared-bindings/bleio/Service.c | 20 +++---- shared-bindings/bleio/Service.h | 3 +- shared-bindings/bleio/__init__.c | 4 +- shared-module/bleio/Service.h | 4 +- 17 files changed, 163 insertions(+), 115 deletions(-) (limited to 'shared-module') diff --git a/docs/drivers.rst b/docs/drivers.rst index 119706d33..fcd94bc3b 100644 --- a/docs/drivers.rst +++ b/docs/drivers.rst @@ -213,7 +213,7 @@ These provide functionality similar to `analogio`, `digitalio`, `pulseio`, and ` Adafruit SeeSaw ADS1x15 Analog-to-Digital Converter - Crickit Robotics Boards < + Crickit Robotics Boards DS2413 OneWire GPIO Expander FocalTech Capacitive Touch MCP230xx GPIO Expander diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 9a31eb42a..91795ab01 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -81,9 +81,10 @@ BASE_CFLAGS = \ -DCIRCUITPY_SAFE_RESTART_WORD=0xDEADBEEF \ --param max-inline-insns-single=500 - # Use these flags to debug build times and header includes. - # -ftime-report - # -H +# Use these flags to debug build times and header includes. +# -ftime-report +# -H + # NDEBUG disables assert() statements. This reduces code size pretty dramatically, per tannewt. ifeq ($(CHIP_FAMILY), samd21) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 8491e54a2..c427cdd26 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -79,12 +79,14 @@ CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_NRF5X -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_C #Debugging/Optimization ifeq ($(DEBUG), 1) -#ASMFLAGS += -g -gtabs+ -CFLAGS += -O1 -ggdb -LDFLAGS += -O1 + #ASMFLAGS += -g -gtabs+ + CFLAGS += -O1 -ggdb + LDFLAGS += -O1 + # You may want to enable these flags to make setting breakpoints easier. + CFLAGS += -fno-inline -fno-ipa-sra else -CFLAGS += -Os -DNDEBUG -LDFLAGS += -Os + CFLAGS += -Os -DNDEBUG + LDFLAGS += -Os endif LIBM_FILE_NAME = $(shell $(CC) $(CFLAGS) -print-file-name=libm.a) @@ -172,7 +174,7 @@ SRC_COMMON_HAL += \ bleio/Adapter.c \ bleio/Characteristic.c \ bleio/Descriptor.c \ - bleio/Device.c \ + bleio/LocalPeripheral.c \ bleio/Scanner.c \ bleio/Service.c \ bleio/UUID.c diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index 5a0025d1c..17656c269 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -30,16 +30,19 @@ #include "ble_drv.h" #include "ble_gatts.h" #include "nrf_soc.h" + #include "py/runtime.h" +#include "common-hal/bleio/__init__.h" #include "shared-module/bleio/Characteristic.h" -static volatile bleio_characteristic_obj_t *m_read_characteristic; -static volatile uint8_t m_tx_in_progress; -static nrf_mutex_t *m_write_mutex; + +STATIC volatile bleio_characteristic_obj_t *m_read_characteristic; +STATIC volatile uint8_t m_tx_in_progress; +STATIC nrf_mutex_t *m_write_mutex; + STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - bleio_device_obj_t *device = characteristic->service->device; - const uint16_t conn_handle = device->conn_handle; + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); ble_gatts_value_t gatts_value = { .p_value = bufinfo->buf, @@ -48,17 +51,17 @@ STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_in const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, characteristic->handle, &gatts_value); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to write gatts value")); + mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code); } } STATIC void gatts_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - bleio_device_obj_t *device = characteristic->service->device; uint16_t hvx_len = bufinfo->len; ble_gatts_hvx_params_t hvx_params = { .handle = characteristic->handle, .type = BLE_GATT_HVX_NOTIFICATION, + .offset = 0, .p_len = &hvx_len, .p_data = bufinfo->buf, }; @@ -69,25 +72,26 @@ STATIC void gatts_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_i #endif } - const uint32_t err_code = sd_ble_gatts_hvx(device->conn_handle, &hvx_params); + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); + const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to notify attribute value")); + mp_raise_OSError_msg_varg(translate("Failed to notify attribute value, err %0x04x"), err_code); } m_tx_in_progress += 1; } STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) { - bleio_service_obj_t *service = characteristic->service; - bleio_device_obj_t *device = service->device; + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); m_read_characteristic = characteristic; - const uint32_t err_code = sd_ble_gattc_read(device->conn_handle, characteristic->handle, 0); + const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to read attribute value")); + mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err %0x04x"), err_code); } +// while (m_read_characteristic != NULL) { #ifdef MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_LOOP @@ -96,7 +100,7 @@ STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) { } STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - bleio_device_obj_t *device = characteristic->service->device; + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); uint32_t err_code; ble_gattc_write_params_t write_params = { @@ -112,13 +116,13 @@ STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_in err_code = sd_mutex_acquire(m_write_mutex); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to acquire mutex")); + mp_raise_OSError_msg_varg(translate("Failed to acquire mutex, err 0x%04x"), err_code); } } - err_code = sd_ble_gattc_write(device->conn_handle, &write_params); + err_code = sd_ble_gattc_write(conn_handle, &write_params); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to write attribute value")); + mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code); } while (sd_mutex_acquire(m_write_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) { @@ -129,33 +133,28 @@ STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_in err_code = sd_mutex_release(m_write_mutex); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to release mutex")); + mp_raise_OSError_msg_varg(translate("Failed to release mutex, err 0x%04x"), err_code); } } STATIC void on_ble_evt(ble_evt_t *ble_evt, void *param) { switch (ble_evt->header.evt_id) { -#if (BLE_API_VERSION == 4) case BLE_GATTS_EVT_HVN_TX_COMPLETE: m_tx_in_progress -= ble_evt->evt.gatts_evt.params.hvn_tx_complete.count; break; -#else - case BLE_EVT_TX_COMPLETE: - m_tx_in_progress -= ble_evt->evt.common_evt.params.tx_complete.count; - break; -#endif case BLE_GATTC_EVT_READ_RSP: { ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data); + // Flag to busy-wait loop that we've read the characteristic. m_read_characteristic = NULL; break; } case BLE_GATTC_EVT_WRITE_RSP: + // Someone else can write now. sd_mutex_release(m_write_mutex); -// m_write_done = true; break; } } @@ -165,21 +164,34 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self) } void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self) { - gattc_read(self); + switch (common_hal_bleio_device_get_gatt_role(self->service->device)) { + case GATT_ROLE_CLIENT: + gattc_read(self); + break; + + default: + mp_raise_RuntimeError(translate("bad GATT role")); + break; + } } void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - const bleio_device_obj_t *device = self->service->device; - - if (device->is_peripheral) { - // TODO: Add indications + switch (common_hal_bleio_device_get_gatt_role(self->service->device)) { + case GATT_ROLE_SERVER: if (self->props.notify) { gatts_notify(self, bufinfo); } else { gatts_write(self, bufinfo); } - } else { + break; + + case GATT_ROLE_CLIENT: + // TODO: Add indications gattc_write(self, bufinfo); - } + break; + default: + mp_raise_RuntimeError(translate("bad GATT role")); + break; + } } diff --git a/ports/nrf/common-hal/bleio/Scanner.c b/ports/nrf/common-hal/bleio/Scanner.c index fd997a6d3..d2e19b5f9 100644 --- a/ports/nrf/common-hal/bleio/Scanner.c +++ b/ports/nrf/common-hal/bleio/Scanner.c @@ -72,7 +72,7 @@ STATIC void on_ble_evt(ble_evt_t *ble_evt, void *scanner_in) { #if (BLUETOOTH_SD == 140) const uint32_t err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to continue scanning")); + mp_raise_OSError_msg_varg(translate("Failed to continue scanning, err 0x%04x"), err_code); } #endif } @@ -98,7 +98,7 @@ void common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_int_t timeout) #endif if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to start scanning")); + mp_raise_OSError_msg_varg(translate("Failed to start scanning, err 0x%04x"), err_code); } if (timeout > 0) { diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c index 29d96e827..e6aab8563 100644 --- a/ports/nrf/common-hal/bleio/Service.c +++ b/ports/nrf/common-hal/bleio/Service.c @@ -27,58 +27,69 @@ #include "ble_drv.h" #include "ble.h" #include "py/runtime.h" +#include "common-hal/bleio/__init__.h" #include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/Adapter.h" -void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic) { - ble_gatts_char_md_t char_md = { - .char_props.broadcast = characteristic->props.broadcast, - .char_props.read = characteristic->props.read, - .char_props.write_wo_resp = characteristic->props.write_no_response, - .char_props.write = characteristic->props.write, - .char_props.notify = characteristic->props.notify, - .char_props.indicate = characteristic->props.indicate, - }; +void common_hal_bleio_service_construct(bleio_service_obj_t *self) { +} - ble_gatts_attr_md_t cccd_md = { - .vloc = BLE_GATTS_VLOC_STACK, - }; +// Call this after the Service has been added to the LocalPeripheral. +void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self) { + // Add all the characteristics. + const mp_obj_list_t *char_list = MP_OBJ_TO_PTR(self->char_list); + for (size_t char_idx = 0; char_idx < char_list->len; ++char_idx) { + bleio_characteristic_obj_t *characteristic = char_list->items[char_idx]; - if (char_md.char_props.notify || char_md.char_props.indicate) { - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm); + ble_gatts_char_md_t char_md = { + .char_props.broadcast = characteristic->props.broadcast, + .char_props.read = characteristic->props.read, + .char_props.write_wo_resp = characteristic->props.write_no_response, + .char_props.write = characteristic->props.write, + .char_props.notify = characteristic->props.notify, + .char_props.indicate = characteristic->props.indicate, + }; - char_md.p_cccd_md = &cccd_md; - } + ble_gatts_attr_md_t cccd_md = { + .vloc = BLE_GATTS_VLOC_STACK, + }; - ble_uuid_t uuid; - bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &uuid); + if (char_md.char_props.notify || char_md.char_props.indicate) { + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm); - ble_gatts_attr_md_t attr_md = { - .vloc = BLE_GATTS_VLOC_STACK, - .vlen = 1, - }; + char_md.p_cccd_md = &cccd_md; + } - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + ble_uuid_t uuid; + bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &uuid); - ble_gatts_attr_t attr_char_value = { - .p_uuid = &uuid, - .p_attr_md = &attr_md, - .init_len = sizeof(uint8_t), - .max_len = (BLE_GATT_ATT_MTU_DEFAULT - 3), - }; + ble_gatts_attr_md_t attr_md = { + .vloc = BLE_GATTS_VLOC_STACK, + .vlen = 1, + }; - ble_gatts_char_handles_t handles; + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); - uint32_t err_code; - err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &attr_char_value, &handles); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Could not add characteristic")); - } + ble_gatts_attr_t attr_char_value = { + .p_uuid = &uuid, + .p_attr_md = &attr_md, + .init_len = sizeof(uint8_t), + .max_len = GATT_MAX_DATA_LENGTH, + }; - characteristic->user_desc_handle = handles.user_desc_handle; - characteristic->cccd_handle = handles.cccd_handle; - characteristic->sccd_handle = handles.sccd_handle; - characteristic->handle = handles.value_handle; + ble_gatts_char_handles_t handles; + + uint32_t err_code; + err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &attr_char_value, &handles); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code); + } + + characteristic->user_desc_handle = handles.user_desc_handle; + characteristic->cccd_handle = handles.cccd_handle; + characteristic->sccd_handle = handles.sccd_handle; + characteristic->handle = handles.value_handle; + } } diff --git a/ports/nrf/common-hal/bleio/UUID.c b/ports/nrf/common-hal/bleio/UUID.c index 8aad85ce3..23e643433 100644 --- a/ports/nrf/common-hal/bleio/UUID.c +++ b/ports/nrf/common-hal/bleio/UUID.c @@ -51,7 +51,7 @@ void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, uint32_t uuid16, ui // Register this vendor-specific UUID. Bytes 12 and 13 will be zero. const uint32_t err_code = sd_ble_uuid_vs_add(&vs_uuid, &self->nrf_ble_uuid.type); if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Could not register Vendor-Specific UUID")); + mp_raise_OSError_msg_varg(translate("Failed to register Vendor-Specific UUID, err 0x%04x"), err_code); } } } @@ -70,7 +70,7 @@ bool common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[1 const uint32_t err_code = sd_ble_uuid_encode(&self->nrf_ble_uuid, &length, uuid128); if (err_code != NRF_SUCCESS) { - mp_raise_RuntimeError(translate("Could not decode ble_uuid")); + mp_raise_OSError_msg_varg(translate("Could not decode ble_uuid, err 0x%04x"), err_code); } // If not 16 bytes, this is not a 128-bit UUID, so return. return length == 16; diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c index 0cc1b7152..f795e889b 100644 --- a/ports/nrf/common-hal/bleio/__init__.c +++ b/ports/nrf/common-hal/bleio/__init__.c @@ -27,6 +27,8 @@ #include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/LocalPeripheral.h" +#include "common-hal/bleio/__init__.h" // The singleton bleio.Adapter object, bound to bleio.adapter // It currently only has properties and no state @@ -35,3 +37,25 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = { .type = &bleio_adapter_type, }, }; + +gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device) { + if (MP_OBJ_IS_TYPE(device, &bleio_local_peripheral_type)) { + return ((bleio_local_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; +// Does not exist yet. +// } else if (MP_OBJ_IS_TYPE(device, &bleio_local_central_type)) { +// return ((bleio_local_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; + } else { + return GATT_ROLE_NONE; + } +} + +uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { + if (MP_OBJ_IS_TYPE(device, &bleio_local_peripheral_type)) { + return ((bleio_local_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; +// Does not exist yet. +// } else if (MP_OBJ_IS_TYPE(device, &bleio_local_central_type)) { +// return ((bleio_local_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; + } else { + return 0; + } +} diff --git a/py/runtime.c b/py/runtime.c index 339978dad..fb62edf73 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1603,6 +1603,14 @@ NORETURN void mp_raise_OSError_msg(const compressed_string_t *msg) { mp_raise_msg(&mp_type_OSError, msg); } +NORETURN void mp_raise_OSError_msg_varg(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_OSError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + NORETURN void mp_raise_NotImplementedError(const compressed_string_t *msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } diff --git a/py/runtime.h b/py/runtime.h index 54f6a3270..ece226e7a 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -159,6 +159,7 @@ NORETURN void mp_raise_ImportError(const compressed_string_t *msg); NORETURN void mp_raise_IndexError(const compressed_string_t *msg); NORETURN void mp_raise_OSError(int errno_); NORETURN void mp_raise_OSError_msg(const compressed_string_t *msg); +NORETURN void mp_raise_OSError_msg_varg(const compressed_string_t *fmt, ...); NORETURN void mp_raise_NotImplementedError(const compressed_string_t *msg); NORETURN void mp_raise_recursion_depth(void); diff --git a/shared-bindings/bleio/Address.c b/shared-bindings/bleio/Address.c index d4c24e4e2..910938844 100644 --- a/shared-bindings/bleio/Address.c +++ b/shared-bindings/bleio/Address.c @@ -80,7 +80,7 @@ STATIC mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, enum { ARG_address }; static const mp_arg_t allowed_args[] = { - { ARG_address, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/shared-bindings/bleio/Descriptor.c b/shared-bindings/bleio/Descriptor.c index 3d6423314..4022632ad 100644 --- a/shared-bindings/bleio/Descriptor.c +++ b/shared-bindings/bleio/Descriptor.c @@ -81,7 +81,7 @@ STATIC mp_obj_t bleio_descriptor_make_new(const mp_obj_type_t *type, size_t n_ar enum { ARG_uuid }; static const mp_arg_t allowed_args[] = { - { ARG_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -99,13 +99,6 @@ STATIC mp_obj_t bleio_descriptor_make_new(const mp_obj_type_t *type, size_t n_ar return MP_OBJ_FROM_PTR(self); } -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); - mp_printf(print, "Descriptor(uuid="); - bleio_uuid_print(print, self->uuid, kind); - mp_printf(print, ", handle=%04x", common_hal_bleio_descriptor_get_handle(self)); -} - STATIC mp_obj_t bleio_descriptor_get_handle(mp_obj_t self_in) { bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -161,7 +154,6 @@ STATIC MP_DEFINE_CONST_DICT(bleio_descriptor_locals_dict, bleio_descriptor_local const mp_obj_type_t bleio_descriptor_type = { { &mp_type_type }, .name = MP_QSTR_Descriptor, - .print = bleio_descriptor_print, .make_new = bleio_descriptor_make_new, .locals_dict = (mp_obj_dict_t*)&bleio_descriptor_locals_dict }; diff --git a/shared-bindings/bleio/Device.c b/shared-bindings/bleio/Device.c index 94834ef7c..cf5eb6873 100644 --- a/shared-bindings/bleio/Device.c +++ b/shared-bindings/bleio/Device.c @@ -171,7 +171,7 @@ STATIC mp_obj_t bleio_device_make_new(const mp_obj_type_t *type, size_t n_args, enum { ARG_address, ARG_scan_entry }; static const mp_arg_t allowed_args[] = { - { ARG_address, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_address, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_scan_entry, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, }; diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c index 129a1d0b7..a0673bb2d 100644 --- a/shared-bindings/bleio/Service.c +++ b/shared-bindings/bleio/Service.c @@ -45,23 +45,16 @@ //| 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 //| -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); - - mp_printf(print, "Service("); - bleio_uuid_print(print, self->uuid, kind); - mp_printf(print, ")"); -} - 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 *pos_args) { mp_arg_check_num(n_args, n_kw, 2, 3, true); bleio_service_obj_t *self = m_new_obj(bleio_service_obj_t); self->char_list = mp_obj_new_list(0, NULL); self->base.type = &bleio_service_type; - self->device = NULL; + self->device = mp_const_none; self->handle = 0xFFFF; mp_map_t kw_args; @@ -69,7 +62,7 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, enum { ARG_uuid, ARG_characteristics, ARG_secondary }; static const mp_arg_t allowed_args[] = { - { ARG_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_characteristics, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; @@ -98,13 +91,17 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, } bleio_characteristic_obj_t *characteristic_ptr = MP_OBJ_TO_PTR(characteristic); if (common_hal_bleio_uuid_get_uuid128_reference(uuid) != - common_hal_bleio_uuid_get_uuid128_reference(characteristic_ptr->uuid)) { + common_hal_bleio_uuid_get_uuid128_reference(characteristic_ptr->uuid)) { // The descriptor base UUID doesn't match the characteristic base UUID. mp_raise_ValueError(translate("Characteristic UUID doesn't match Service UUID")); } characteristic_ptr->service = self; mp_obj_list_append(self->char_list, characteristic); } + + // Do port-specific initialization. + common_hal_bleio_service_construct(self); + return MP_OBJ_FROM_PTR(self); } @@ -155,7 +152,6 @@ STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict const mp_obj_type_t bleio_service_type = { { &mp_type_type }, .name = MP_QSTR_Service, - .print = bleio_service_print, .make_new = bleio_service_make_new, .locals_dict = (mp_obj_dict_t*)&bleio_service_locals_dict }; diff --git a/shared-bindings/bleio/Service.h b/shared-bindings/bleio/Service.h index 77c751829..389db3b2e 100644 --- a/shared-bindings/bleio/Service.h +++ b/shared-bindings/bleio/Service.h @@ -32,6 +32,7 @@ const mp_obj_type_t bleio_service_type; -extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic); +extern void common_hal_bleio_service_construct(bleio_service_obj_t *self); +extern void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index 46822603f..21242e07c 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -31,7 +31,7 @@ #include "shared-bindings/bleio/AdvertisementData.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/Device.h" +#include "shared-bindings/bleio/LocalPeripheral.h" #include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/Scanner.h" #include "shared-bindings/bleio/Service.h" @@ -76,7 +76,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) }, { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, - { MP_ROM_QSTR(MP_QSTR_Device), MP_ROM_PTR(&bleio_device_type) }, + { MP_ROM_QSTR(MP_QSTR_LocalPeripheral), MP_ROM_PTR(&bleio_local_peripheral_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, diff --git a/shared-module/bleio/Service.h b/shared-module/bleio/Service.h index ff506d3f3..540749793 100644 --- a/shared-module/bleio/Service.h +++ b/shared-module/bleio/Service.h @@ -28,14 +28,14 @@ #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SERVICE_H #include "common-hal/bleio/UUID.h" -#include "shared-module/bleio/Device.h" typedef struct { mp_obj_base_t base; uint16_t handle; bool is_secondary; bleio_uuid_obj_t *uuid; - bleio_device_obj_t *device; + // May be a LocalPeripheral, RemotePeripheral, etc. + mp_obj_t *device; mp_obj_t char_list; uint16_t start_handle; uint16_t end_handle; -- cgit v1.2.3 From 4d1f0ec07be5e9cacaf0072a0f987871b8505ec4 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 28 Dec 2018 22:55:29 -0500 Subject: Add Broadcaster. Reset correctly on reload. --- ports/nrf/Makefile | 1 + ports/nrf/bluetooth/ble_drv.c | 7 +- ports/nrf/bluetooth/ble_drv.h | 3 + ports/nrf/common-hal/bleio/Adapter.c | 20 +- ports/nrf/common-hal/bleio/Broadcaster.c | 113 +++++++++ ports/nrf/common-hal/bleio/Broadcaster.h | 47 ++++ ports/nrf/common-hal/bleio/LocalPeripheral.c | 329 +++++++++++++++++++++++++++ ports/nrf/common-hal/bleio/LocalPeripheral.h | 64 ++++++ ports/nrf/common-hal/bleio/__init__.c | 7 + ports/nrf/common-hal/bleio/__init__.h | 42 ++++ ports/nrf/supervisor/port.c | 3 + shared-bindings/bleio/Broadcaster.c | 140 ++++++++++++ shared-bindings/bleio/Broadcaster.h | 38 ++++ shared-bindings/bleio/LocalPeripheral.c | 250 ++++++++++++++++++++ shared-bindings/bleio/LocalPeripheral.h | 39 ++++ shared-bindings/bleio/__init__.c | 2 + shared-module/bleio/__init__.h | 38 ++++ 17 files changed, 1125 insertions(+), 18 deletions(-) create mode 100644 ports/nrf/common-hal/bleio/Broadcaster.c create mode 100644 ports/nrf/common-hal/bleio/Broadcaster.h create mode 100644 ports/nrf/common-hal/bleio/LocalPeripheral.c create mode 100644 ports/nrf/common-hal/bleio/LocalPeripheral.h create mode 100644 ports/nrf/common-hal/bleio/__init__.h create mode 100644 shared-bindings/bleio/Broadcaster.c create mode 100644 shared-bindings/bleio/Broadcaster.h create mode 100644 shared-bindings/bleio/LocalPeripheral.c create mode 100644 shared-bindings/bleio/LocalPeripheral.h create mode 100644 shared-module/bleio/__init__.h (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index c427cdd26..58db910c1 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -172,6 +172,7 @@ ifneq ($(SD), ) SRC_COMMON_HAL += \ bleio/__init__.c \ bleio/Adapter.c \ + bleio/Broadcaster.c \ bleio/Characteristic.c \ bleio/Descriptor.c \ bleio/LocalPeripheral.c \ diff --git a/ports/nrf/bluetooth/ble_drv.c b/ports/nrf/bluetooth/ble_drv.c index 0b6daf711..41d51cfb4 100644 --- a/ports/nrf/bluetooth/ble_drv.c +++ b/ports/nrf/bluetooth/ble_drv.c @@ -45,7 +45,12 @@ typedef struct event_handler { ble_drv_evt_handler_t func; } event_handler_t; -static event_handler_t *m_event_handlers; +static event_handler_t *m_event_handlers = NULL; + +void ble_drv_reset() { + // Linked-list members will be gc'd. + m_event_handlers = NULL; +} void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) { event_handler_t *handler = m_new_ll(event_handler_t, 1); diff --git a/ports/nrf/bluetooth/ble_drv.h b/ports/nrf/bluetooth/ble_drv.h index 0443b41b8..696aff1da 100644 --- a/ports/nrf/bluetooth/ble_drv.h +++ b/ports/nrf/bluetooth/ble_drv.h @@ -43,11 +43,14 @@ #define BLE_CONN_CFG_TAG_CUSTOM 1 #define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) +// 0.625 msecs (625 usecs) +#define ADV_INTERVAL_UNIT_FLOAT_SECS (0.000625) #define UNIT_0_625_MS (625) #define UNIT_10_MS (10000) typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*); +void ble_drv_reset(); void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param); #endif // MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_DRV_H diff --git a/ports/nrf/common-hal/bleio/Adapter.c b/ports/nrf/common-hal/bleio/Adapter.c index 318502e95..cd116711e 100644 --- a/ports/nrf/common-hal/bleio/Adapter.c +++ b/ports/nrf/common-hal/bleio/Adapter.c @@ -44,17 +44,11 @@ STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { STATIC uint32_t ble_stack_enable(void) { nrf_clock_lf_cfg_t clock_config = { .source = NRF_CLOCK_LF_SRC_XTAL, -#if (BLE_API_VERSION == 4) .accuracy = NRF_CLOCK_LF_ACCURACY_20_PPM -#else - .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM -#endif }; -#if (BLUETOOTH_SD == 140) // The SD takes over the POWER IRQ and will fail if the IRQ is already in use nrfx_power_uninit(); -#endif uint32_t err_code = sd_softdevice_enable(&clock_config, softdevice_assert_handler); if (err_code != NRF_SUCCESS) @@ -64,17 +58,10 @@ STATIC uint32_t ble_stack_enable(void) { if (err_code != NRF_SUCCESS) return err_code; - uint32_t app_ram_start; -#if (BLE_API_VERSION == 2) - ble_enable_params_t ble_enable_params = { - .gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT, - .gap_enable_params.central_conn_count = 1, - .gap_enable_params.periph_conn_count = 1, - }; + // Start with no event handlers, etc. + ble_drv_reset(); - app_ram_start = 0x200039c0; - err_code = sd_ble_enable(&ble_enable_params, &app_ram_start); -#else + uint32_t app_ram_start; app_ram_start = 0x20004000; ble_cfg_t ble_conf; @@ -100,7 +87,6 @@ STATIC uint32_t ble_stack_enable(void) { return err_code; err_code = sd_ble_enable(&app_ram_start); -#endif return err_code; } diff --git a/ports/nrf/common-hal/bleio/Broadcaster.c b/ports/nrf/common-hal/bleio/Broadcaster.c new file mode 100644 index 000000000..508ede1d6 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Broadcaster.c @@ -0,0 +1,113 @@ +/* + * 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 + +#include "ble.h" +#include "ble_drv.h" +#include "ble_hci.h" +#include "nrf_soc.h" +#include "py/runtime.h" + +#include "common-hal/bleio/Broadcaster.h" +#include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/Broadcaster.h" + +static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; + +STATIC void check_data_fit(size_t pos, size_t data_len) { + if (pos + data_len >= BLE_GAP_ADV_SET_DATA_SIZE_MAX) { + mp_raise_ValueError(translate("Data too large for advertisement packet")); + } +} + +void common_hal_bleio_broadcaster_construct(bleio_broadcaster_obj_t *self, mp_float_t interval) { + common_hal_bleio_adapter_set_enabled(true); // TODO -- Do this somewhere else maybe bleio __init__ + const mp_float_t min = BLE_GAP_ADV_INTERVAL_MIN * ADV_INTERVAL_UNIT_FLOAT_SECS; + const mp_float_t max = BLE_GAP_ADV_INTERVAL_MAX * ADV_INTERVAL_UNIT_FLOAT_SECS; + + if (interval < min || interval > max) { + // Would like to print range using the constants above, but vargs would convert to double. + mp_raise_ValueError(translate("interval not in range 0.0020 to 10.24")); + } + self->interval = interval; +} + + +void common_hal_bleio_broadcaster_start_advertising(bleio_broadcaster_obj_t *self, mp_buffer_info_t *data) { + size_t adv_data_pos = 0; + uint32_t err_code; + + // Build up advertising packet. + check_data_fit(adv_data_pos, 1 + 1 + 1); + self->adv_data[adv_data_pos++] = 2; + self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_FLAGS; + self->adv_data[adv_data_pos++] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + + // Data is always send as manufacturer-specific data + check_data_fit(adv_data_pos, 1 + 1 + data->len); + self->adv_data[adv_data_pos++] = 1 + data->len; + self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA; + memcpy(&(self->adv_data[adv_data_pos]), data->buf, data->len); + adv_data_pos += data->len; + + ble_gap_adv_params_t m_adv_params = { + .interval = (uint32_t) (self->interval / ADV_INTERVAL_UNIT_FLOAT_SECS), + .properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED, + .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED, + .filter_policy = BLE_GAP_ADV_FP_ANY, + .primary_phy = BLE_GAP_PHY_1MBPS, + }; + + common_hal_bleio_broadcaster_stop_advertising(self); + + const ble_gap_adv_data_t ble_gap_adv_data = { + .adv_data.p_data = self->adv_data, + .adv_data.len = adv_data_pos, + }; + + err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params); + if (err_code == NRF_SUCCESS) { + err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_CUSTOM); + } + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_broadcaster_stop_advertising(bleio_broadcaster_obj_t *self) { + + if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) { + return; + } + + const uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle); + + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { + mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code); + } +} diff --git a/ports/nrf/common-hal/bleio/Broadcaster.h b/ports/nrf/common-hal/bleio/Broadcaster.h new file mode 100644 index 000000000..72f93a443 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Broadcaster.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_COMMON_HAL_BLEIO_BROADCASTER_H +#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_BROADCASTER_H + +#include "ble.h" + +#include "shared-module/bleio/__init__.h" +#include "shared-module/bleio/Address.h" + +typedef struct { + mp_obj_base_t base; + // In seconds. + mp_float_t interval; + // The advertising data buffer is held by us, not by the SD, so we must + // maintain it and not change it. If we need to change its contents during advertising, + // there are tricks to get the SD to notice (see DevZone - TBS). + uint8_t adv_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; + +} bleio_broadcaster_obj_t; + +#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_BROADCASTER_H diff --git a/ports/nrf/common-hal/bleio/LocalPeripheral.c b/ports/nrf/common-hal/bleio/LocalPeripheral.c new file mode 100644 index 000000000..2a502c8dd --- /dev/null +++ b/ports/nrf/common-hal/bleio/LocalPeripheral.c @@ -0,0 +1,329 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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 +#include + +#include "ble.h" +#include "ble_drv.h" +#include "ble_hci.h" +#include "nrf_soc.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/LocalPeripheral.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" + +#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) +#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS) +#define BLE_SLAVE_LATENCY 0 +#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) + +#define BLE_ADV_LENGTH_FIELD_SIZE 1 +#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 +#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 + +static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; + +STATIC void check_data_fit(size_t pos, size_t data_len) { + if (pos + data_len >= BLE_GAP_ADV_SET_DATA_SIZE_MAX) { + mp_raise_ValueError(translate("Data too large for advertisement packet")); + } +} + +STATIC uint32_t add_services_to_advertisement(bleio_local_peripheral_obj_t *self, size_t* adv_data_pos_p, size_t uuid_len) { + uint32_t uuids_total_size = 0; + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); + uint32_t err_code = NRF_SUCCESS; + + check_data_fit(*adv_data_pos_p, 1 + 1); + + // Remember where length byte is; fill in later when we know the size. + const size_t length_pos = *adv_data_pos_p; + (*adv_data_pos_p)++; + + self->adv_data[(*adv_data_pos_p)++] = (uuid_len == 16) + ? BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE + : BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE; + + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + uint8_t encoded_size = 0; + + // Skip services of the wrong length and secondary services. + if (common_hal_bleio_uuid_get_size(service->uuid) != uuid_len || service->is_secondary) { + continue; + } + + ble_uuid_t uuid; + bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); + + err_code = sd_ble_uuid_encode(&uuid, &encoded_size, &(self->adv_data[*adv_data_pos_p])); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + check_data_fit(*adv_data_pos_p, encoded_size); + uuids_total_size += encoded_size; + (*adv_data_pos_p) += encoded_size; + } + + self->adv_data[length_pos] = 1 + uuids_total_size; // 1 for the field type. + return err_code; +} + + + +STATIC uint32_t set_advertisement_data(bleio_local_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) { + common_hal_bleio_adapter_set_enabled(true); + + size_t adv_data_pos = 0; + uint32_t err_code; + + GET_STR_DATA_LEN(self->name, name_data, name_len); + if (name_len > 0) { + ble_gap_conn_sec_mode_t sec_mode; + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); + + // We'll add the name after everything else, shortening it if necessary. + err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len); + if (err_code != NRF_SUCCESS) { + return err_code; + } + } + + if (raw_data->len != 0) { + // User-supplied advertising packet. + check_data_fit(adv_data_pos, raw_data->len); + memcpy(&(self->adv_data[adv_data_pos]), raw_data->buf, raw_data->len); + adv_data_pos += raw_data->len; + } else { + // Build up advertising packet. + check_data_fit(adv_data_pos, 1 + 1 + 1); + self->adv_data[adv_data_pos++] = 2; + self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_FLAGS; + self->adv_data[adv_data_pos++] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + + // The 16-bit ids and 128-bit ids are grouped together by length, so find it whether we have + // 16 and/or 128-bit service UUIDs. + + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); + if (service_list->len > 0) { + bool has_128bit_services = false; + bool has_16bit_services = false; + + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + + if (service->is_secondary) { + continue; + } + + switch (common_hal_bleio_uuid_get_size(service->uuid)) { + case 16: + has_16bit_services = true; + break; + case 128: + has_128bit_services = true; + break; + } + } + + // Add 16-bit service UUID's in a group, then 128-bit service UUID's. + + if (has_16bit_services) { + err_code = add_services_to_advertisement(self, &adv_data_pos, 16); + if (err_code != NRF_SUCCESS) { + return err_code; + } + } + + if (has_128bit_services) { + err_code = add_services_to_advertisement(self, &adv_data_pos, 128); + if (err_code != NRF_SUCCESS) { + return err_code; + } + } + } + + // Always include TX power. + check_data_fit(adv_data_pos, 1 + 1 + 1); + self->adv_data[adv_data_pos++] = 1 + 1; + self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_TX_POWER_LEVEL; + self->adv_data[adv_data_pos++] = 0; // TODO - allow power level to be set later. + + // We need room for at least a one-character name. + check_data_fit(adv_data_pos, 1 + 1 + 1); + + // How big a name can we fit? + size_t bytes_left = BLE_GAP_ADV_SET_DATA_SIZE_MAX - adv_data_pos - 1 - 1; + size_t partial_name_len = MIN(bytes_left, name_len); + self->adv_data[adv_data_pos++] = 1 + partial_name_len; + self->adv_data[adv_data_pos++] = (partial_name_len == name_len) + ? BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME + : BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME; + memcpy(&(self->adv_data[adv_data_pos]), name_data, partial_name_len); + adv_data_pos += partial_name_len; + } // end of advertising packet construction + + static ble_gap_adv_params_t m_adv_params = { + .interval = MSEC_TO_UNITS(1000, UNIT_0_625_MS), + .properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED, + .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED, + .filter_policy = BLE_GAP_ADV_FP_ANY, + .primary_phy = BLE_GAP_PHY_1MBPS, + }; + + if (!connectable) { + m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED; + } + + common_hal_bleio_local_peripheral_stop_advertising(self); + + const ble_gap_adv_data_t ble_gap_adv_data = { + .adv_data.p_data = self->adv_data, + .adv_data.len = adv_data_pos, + }; + + err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_CUSTOM); + + return err_code; +} + +STATIC void on_ble_evt(ble_evt_t *ble_evt, void *self_in) { + bleio_local_peripheral_obj_t *self = (bleio_local_peripheral_obj_t*)self_in; + + switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_CONNECTED: { + // Central has connected. + ble_gap_conn_params_t conn_params; + self->conn_handle = ble_evt->evt.gap_evt.conn_handle; + sd_ble_gap_ppcp_get(&conn_params); + sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params); + break; + } + + case BLE_GAP_EVT_DISCONNECTED: + // Central has disconnected. + self->conn_handle = BLE_CONN_HANDLE_INVALID; + break; + + case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { + ble_gap_phys_t const phys = { + .rx_phys = BLE_GAP_PHY_AUTO, + .tx_phys = BLE_GAP_PHY_AUTO, + }; + sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys); + break; + } + + case BLE_GAP_EVT_ADV_SET_TERMINATED: + // Someday may handle timeouts or limit reached. + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { + ble_gap_evt_conn_param_update_request_t *request = &ble_evt->evt.gap_evt.params.conn_param_update_request; + sd_ble_gap_conn_param_update(self->conn_handle, &request->conn_params); + break; + } + + case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: { + sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT); + break; + } + + } +} + + +void common_hal_bleio_local_peripheral_construct(bleio_local_peripheral_obj_t *self) { + common_hal_bleio_adapter_set_enabled(true); // TODO -- Do this somewhere else maybe bleio __init__ + + self->gatt_role = GATT_ROLE_NONE; + self->conn_handle = BLE_CONN_HANDLE_INVALID; + + // Add all the services. + + mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); + for (size_t service_idx = 0; service_idx < service_list->len; ++service_idx) { + bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[service_idx]); + + ble_uuid_t uuid; + bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); + + uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; + if (service->is_secondary) { + service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; + } + + const uint32_t err_code = sd_ble_gatts_service_add(service_type, &uuid, &service->handle); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to add service, err 0x%04x"), err_code); + } + + // Once the service has been registered, its characteristics can be added. + common_hal_bleio_service_add_all_characteristics(service); + } +} + + +bool common_hal_bleio_local_peripheral_get_connected(bleio_local_peripheral_obj_t *self) { + return self->conn_handle != BLE_CONN_HANDLE_INVALID; +} + +void common_hal_bleio_local_peripheral_start_advertising(bleio_local_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) { + if (connectable) { + ble_drv_add_event_handler(on_ble_evt, self); + } + + const uint32_t err_code = set_advertisement_data(self, connectable, raw_data); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_local_peripheral_stop_advertising(bleio_local_peripheral_obj_t *self) { + + if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) + return; + + const uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle); + + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { + mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code); + } +} diff --git a/ports/nrf/common-hal/bleio/LocalPeripheral.h b/ports/nrf/common-hal/bleio/LocalPeripheral.h new file mode 100644 index 000000000..8d84867ae --- /dev/null +++ b/ports/nrf/common-hal/bleio/LocalPeripheral.h @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H +#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H + +#include + +#include "ble.h" + +#include "shared-module/bleio/__init__.h" +#include "shared-module/bleio/Address.h" + +// typedef struct { +// mp_obj_base_t base; +// bool is_peripheral; +// mp_obj_t name; +// bleio_address_obj_t address; +// volatile uint16_t conn_handle; +// mp_obj_t service_list; +// mp_obj_t notif_handler; +// mp_obj_t conn_handler; +// } bleio_device_obj_t; + +typedef struct { + mp_obj_base_t base; + mp_obj_t name; + gatt_role_t gatt_role; + volatile uint16_t conn_handle; + mp_obj_t service_list; + mp_obj_t notif_handler; + mp_obj_t conn_handler; + // The advertising data buffer is held by us, not by the SD, so we must + // maintain it and not change it. If we need to change its contents during advertising, + // there are tricks to get the SD to notice (see DevZone - TBS). + uint8_t adv_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; + +} bleio_local_peripheral_obj_t; + +#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c index f795e889b..5a8a7ff16 100644 --- a/ports/nrf/common-hal/bleio/__init__.c +++ b/ports/nrf/common-hal/bleio/__init__.c @@ -30,6 +30,13 @@ #include "shared-bindings/bleio/LocalPeripheral.h" #include "common-hal/bleio/__init__.h" +// Turn off BLE on a reset or reload. +void bleio_reset() { + if (common_hal_bleio_adapter_get_enabled()) { + common_hal_bleio_adapter_set_enabled(false); + } +} + // The singleton bleio.Adapter object, bound to bleio.adapter // It currently only has properties and no state const super_adapter_obj_t common_hal_bleio_adapter_obj = { diff --git a/ports/nrf/common-hal/bleio/__init__.h b/ports/nrf/common-hal/bleio/__init__.h new file mode 100644 index 000000000..9e044f37c --- /dev/null +++ b/ports/nrf/common-hal/bleio/__init__.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H +#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H + +#include "shared-bindings/bleio/__init__.h" +#include "shared-bindings/bleio/Adapter.h" + +#include "shared-module/bleio/__init__.h" + +// We assume variable length data. +// 20 bytes max (23 - 3). +#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3) + +gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device); +uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); + +#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index ece5bae29..cf0434793 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -38,6 +38,7 @@ #include "shared-module/gamepad/__init__.h" #include "common-hal/microcontroller/Pin.h" +#include "common-hal/bleio/__init__.h" #include "common-hal/busio/I2C.h" #include "common-hal/busio/SPI.h" #include "common-hal/pulseio/PWMOut.h" @@ -86,6 +87,8 @@ void reset_port(void) { pulseout_reset(); timers_reset(); + bleio_reset(); + reset_all_pins(); } diff --git a/shared-bindings/bleio/Broadcaster.c b/shared-bindings/bleio/Broadcaster.c new file mode 100644 index 000000000..e24d157ec --- /dev/null +++ b/shared-bindings/bleio/Broadcaster.c @@ -0,0 +1,140 @@ +/* + * 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 "ble_drv.h" +#include "py/runtime.h" + +#include "shared-bindings/bleio/Broadcaster.h" + +//| .. currentmodule:: bleio +//| +//| :class:`Broadcaster` -- A GAP Broadcaster +//| ========================================================= +//| +//| Implement a BLE broadcaster which sends data in advertising packets and does not connect. +//| Used for beacons and other one-way data transmission. +//| +//| Usage:: +//| +//| import bleio +//| import time +//| +//| +//| # Broadcast once a second. +//| broadcaster = bleio.Broadcaster(interval=1) +//| i = 0 +//| data = bytearray(1) +//| # Broadcast a byte of data that's incremented once a minute +//| while True: +//| data[0] = i +//| bytearray +//| broadcaster.start_advertising(data) +//| time.sleep(60) +//| i += 1 +//| +//| .. class:: Broadcaster(interval=1) +//| +//| Create a new Broadcaster object. + +//| :param float interval: how often to broadcast +//| + +STATIC mp_obj_t bleio_broadcaster_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 0, 1, true); + bleio_broadcaster_obj_t *self = m_new_obj(bleio_broadcaster_obj_t); + self->base.type = &bleio_broadcaster_type; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + enum { ARG_interval }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_interval, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + + // Do port-specific initialization. interval will be validated. + common_hal_bleio_broadcaster_construct(self, interval); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: start_advertising(data) +//| +//| Start advertising the given manufacturer-specific data. +//| +//| :param buf data: Send data bytes in advertising packets, labeled as manufacturer-specific data +//| +STATIC mp_obj_t bleio_broadcaster_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_broadcaster_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, 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); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + + common_hal_bleio_broadcaster_start_advertising(self, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_broadcaster_start_advertising_obj, 0, bleio_broadcaster_start_advertising); + +//| .. method:: stop_advertising() +//| +//| Stop sending advertising packets. +STATIC mp_obj_t bleio_broadcaster_stop_advertising(mp_obj_t self_in) { + bleio_broadcaster_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_broadcaster_stop_advertising(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_broadcaster_stop_advertising_obj, bleio_broadcaster_stop_advertising); + +STATIC const mp_rom_map_elem_t bleio_broadcaster_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_broadcaster_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_broadcaster_stop_advertising_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_broadcaster_locals_dict, bleio_broadcaster_locals_dict_table); + +const mp_obj_type_t bleio_broadcaster_type = { + { &mp_type_type }, + .name = MP_QSTR_LocalPeripheral, + .make_new = bleio_broadcaster_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_broadcaster_locals_dict +}; diff --git a/shared-bindings/bleio/Broadcaster.h b/shared-bindings/bleio/Broadcaster.h new file mode 100644 index 000000000..8aa125af9 --- /dev/null +++ b/shared-bindings/bleio/Broadcaster.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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_BROADCASTER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_BROADCASTER_H + +#include "common-hal/bleio/Broadcaster.h" + +extern const mp_obj_type_t bleio_broadcaster_type; + +extern void common_hal_bleio_broadcaster_construct(bleio_broadcaster_obj_t *self, mp_float_t interval); +extern void common_hal_bleio_broadcaster_start_advertising(bleio_broadcaster_obj_t *self, mp_buffer_info_t *data); +extern void common_hal_bleio_broadcaster_stop_advertising(bleio_broadcaster_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_BROADCASTER_H diff --git a/shared-bindings/bleio/LocalPeripheral.c b/shared-bindings/bleio/LocalPeripheral.c new file mode 100644 index 000000000..a40e0c043 --- /dev/null +++ b/shared-bindings/bleio/LocalPeripheral.c @@ -0,0 +1,250 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Glenn Ruben Bakke + * 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 +#include + +#include "ble_drv.h" +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" + +#include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/AddressType.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/LocalPeripheral.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" +#include "shared-module/bleio/AdvertisementData.h" +#include "shared-module/bleio/ScanEntry.h" + +#include "common-hal/bleio/LocalPeripheral.h" + +// TODO: Add unique MAC address part to name +static const char default_name[] = "CIRCUITPY"; + +//| .. currentmodule:: bleio +//| +//| :class:`LocalPeripheral` -- A BLE peripheral device +//| ========================================================= +//| +//| Implement a BLE peripheral which runs locally. +//| Set up using the supplied services, and then allow advertising to be started and stopped. +//| +//| Usage:: +//| +//| import bleio +//| +//| # Create a Characteristic. +//| chara = bleio.Characteristic(bleio.UUID(0x2919), read=True, notify=True) +//| +//| # Create a Service providing that one Characteristic. +//| serv = bleio.Service(bleio.UUID(0x180f), [chara]) +//| +//| # Create a peripheral and start it up. +//| periph = bleio.LocalPeripheral([service]) +//| periph.start_advertising() +//| +//| while not periph.connected(): +//| # Wait for connection. +//| pass +//| +//| .. class:: LocalPeripheral(services, *, name='CIRCUITPY') +//| +//| Create a new LocalPeripheral object. + +//| :param iterable services: the Service objects representing services available from this peripheral. +//| :param str name: The name used when advertising this peripheral +//| + +STATIC mp_obj_t bleio_local_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 0, 1, true); + bleio_local_peripheral_obj_t *self = m_new_obj(bleio_local_peripheral_obj_t); + self->base.type = &bleio_local_peripheral_type; + self->service_list = mp_obj_new_list(0, NULL); + self->notif_handler = mp_const_none; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + enum { ARG_services, ARG_name }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_services, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_name, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // If services is not an iterable, an exception will be thrown. + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(args[ARG_services].u_obj, &iter_buf); + 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("services includes an object that is not a Service")); + } + bleio_service_obj_t *service_ptr = MP_OBJ_TO_PTR(service); + service_ptr->device = MP_OBJ_FROM_PTR(self); + mp_obj_list_append(self->service_list, service); + } + + const mp_obj_t name = args[ARG_name].u_obj; + if (name == mp_const_none) { + self->name = mp_obj_new_str(default_name, strlen(default_name)); + } else if (MP_OBJ_IS_STR(name)) { + self->name = name; + } else { + mp_raise_ValueError(translate("name must be a string")); + } + + // Do port-specific initialization. + common_hal_bleio_local_peripheral_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. attribute:: connected +//| +//| True if connected to a BLE Central device. +//| +STATIC mp_obj_t bleio_local_peripheral_get_connected(mp_obj_t self_in) { + bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Return list as a tuple so user won't be able to change it. + return mp_obj_new_bool(common_hal_bleio_local_peripheral_get_connected(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_connected_obj, bleio_local_peripheral_get_connected); + +const mp_obj_property_t bleio_local_peripheral_connected_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_local_peripheral_get_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: services +//| +//| A `tuple` of `bleio.Service` that are offered by this peripheral. (read-only) +//| +STATIC mp_obj_t bleio_local_peripheral_get_services(mp_obj_t self_in) { + bleio_local_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 = MP_OBJ_TO_PTR(self->service_list); + return mp_obj_new_tuple(service_list->len, service_list->items); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_services_obj, bleio_local_peripheral_get_services); + +const mp_obj_property_t bleio_local_peripheral_services_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_local_peripheral_get_services_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: name +//| +//| The peripheral's name, included when advertising. (read-only) +//| +STATIC mp_obj_t bleio_local_peripheral_get_name(mp_obj_t self_in) { + bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return self->name; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_name_obj, bleio_local_peripheral_get_name); + +const mp_obj_property_t bleio_local_peripheral_name_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_local_peripheral_get_name_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. method:: start_advertising(*, connectable=True, data=None) +//| +//| Starts advertising the peripheral. The peripheral's name and +//| services are included in the advertisement packets. +//| +//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral. +//| :param buf data: If not None, then send data bytes in advertising packets. +//| +STATIC mp_obj_t bleio_local_peripheral_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_connectable, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_data, 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 bufinfo = { 0 }; + if (args[ARG_data].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + } + + common_hal_bleio_local_peripheral_start_advertising(self, args[ARG_connectable].u_bool, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_local_peripheral_start_advertising_obj, 0, bleio_local_peripheral_start_advertising); + +//| .. method:: stop_advertising() +//| +//| Stop sending advertising packets. +STATIC mp_obj_t bleio_local_peripheral_stop_advertising(mp_obj_t self_in) { + bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_local_peripheral_stop_advertising(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_stop_advertising_obj, bleio_local_peripheral_stop_advertising); + +STATIC const mp_rom_map_elem_t bleio_local_peripheral_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_local_peripheral_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_local_peripheral_stop_advertising_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_local_peripheral_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_local_peripheral_name_obj) }, + { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_local_peripheral_services_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_local_peripheral_locals_dict, bleio_local_peripheral_locals_dict_table); + +const mp_obj_type_t bleio_local_peripheral_type = { + { &mp_type_type }, + .name = MP_QSTR_LocalPeripheral, + .make_new = bleio_local_peripheral_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_local_peripheral_locals_dict +}; diff --git a/shared-bindings/bleio/LocalPeripheral.h b/shared-bindings/bleio/LocalPeripheral.h new file mode 100644 index 000000000..bd6d5cbbb --- /dev/null +++ b/shared-bindings/bleio/LocalPeripheral.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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_LOCALPERIPHERAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_LOCALPERIPHERAL_H + +#include "common-hal/bleio/LocalPeripheral.h" + +extern const mp_obj_type_t bleio_local_peripheral_type; + +extern void common_hal_bleio_local_peripheral_construct(bleio_local_peripheral_obj_t *self); +extern bool common_hal_bleio_local_peripheral_get_connected(bleio_local_peripheral_obj_t *self); +extern void common_hal_bleio_local_peripheral_start_advertising(bleio_local_peripheral_obj_t *device, bool connectable, mp_buffer_info_t *raw_data); +extern void common_hal_bleio_local_peripheral_stop_advertising(bleio_local_peripheral_obj_t *device); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_LOCALPERIPHERAL_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index 21242e07c..7e36eaa46 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -29,6 +29,7 @@ #include "shared-bindings/bleio/Address.h" #include "shared-bindings/bleio/AddressType.h" #include "shared-bindings/bleio/AdvertisementData.h" +#include "shared-bindings/bleio/Broadcaster.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/LocalPeripheral.h" @@ -74,6 +75,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) }, { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, { MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) }, + { MP_ROM_QSTR(MP_QSTR_Broadcaster), MP_ROM_PTR(&bleio_broadcaster_type) }, { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, { MP_ROM_QSTR(MP_QSTR_LocalPeripheral), MP_ROM_PTR(&bleio_local_peripheral_type) }, diff --git a/shared-module/bleio/__init__.h b/shared-module/bleio/__init__.h new file mode 100644 index 000000000..07d148594 --- /dev/null +++ b/shared-module/bleio/__init__.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H + +typedef enum { + GATT_ROLE_NONE, + GATT_ROLE_SERVER, + GATT_ROLE_CLIENT, +} gatt_role_t; + +extern void bleio_reset(void); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H -- cgit v1.2.3 From ef39e72c7c3c3e68f73f670b7f8b7f08e813be39 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 29 Dec 2018 13:55:10 -0500 Subject: free event handlers on reset; fix typo in Broadcaster --- ports/nrf/Makefile | 2 +- ports/nrf/bluetooth/ble_drv.c | 7 ++++++- ports/nrf/common-hal/bleio/Service.c | 2 +- ports/nrf/common-hal/bleio/__init__.c | 23 ----------------------- ports/nrf/common-hal/bleio/__init__.h | 1 - shared-bindings/bleio/Broadcaster.c | 2 +- shared-bindings/bleio/__init__.c | 4 ++-- shared-module/bleio/Service.h | 2 +- shared-module/bleio/__init__.h | 6 ------ 9 files changed, 12 insertions(+), 37 deletions(-) (limited to 'shared-module') diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 58db910c1..f7e4a2988 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -175,7 +175,7 @@ SRC_COMMON_HAL += \ bleio/Broadcaster.c \ bleio/Characteristic.c \ bleio/Descriptor.c \ - bleio/LocalPeripheral.c \ + bleio/Peripheral.c \ bleio/Scanner.c \ bleio/Service.c \ bleio/UUID.c diff --git a/ports/nrf/bluetooth/ble_drv.c b/ports/nrf/bluetooth/ble_drv.c index 41d51cfb4..57a433f70 100644 --- a/ports/nrf/bluetooth/ble_drv.c +++ b/ports/nrf/bluetooth/ble_drv.c @@ -48,7 +48,12 @@ typedef struct event_handler { static event_handler_t *m_event_handlers = NULL; void ble_drv_reset() { - // Linked-list members will be gc'd. + event_handler_t *handler = m_event_handlers; + while (handler != NULL) { + event_handler_t *next = handler->next; + m_free(handler); + handler = next; + } m_event_handlers = NULL; } diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c index e6aab8563..c913c02ae 100644 --- a/ports/nrf/common-hal/bleio/Service.c +++ b/ports/nrf/common-hal/bleio/Service.c @@ -34,7 +34,7 @@ void common_hal_bleio_service_construct(bleio_service_obj_t *self) { } -// Call this after the Service has been added to the LocalPeripheral. +// Call this after the Service has been added to the Peripheral. void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self) { // Add all the characteristics. const mp_obj_list_t *char_list = MP_OBJ_TO_PTR(self->char_list); diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c index 5a8a7ff16..9429aaf44 100644 --- a/ports/nrf/common-hal/bleio/__init__.c +++ b/ports/nrf/common-hal/bleio/__init__.c @@ -27,7 +27,6 @@ #include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/LocalPeripheral.h" #include "common-hal/bleio/__init__.h" // Turn off BLE on a reset or reload. @@ -44,25 +43,3 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = { .type = &bleio_adapter_type, }, }; - -gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device) { - if (MP_OBJ_IS_TYPE(device, &bleio_local_peripheral_type)) { - return ((bleio_local_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; -// Does not exist yet. -// } else if (MP_OBJ_IS_TYPE(device, &bleio_local_central_type)) { -// return ((bleio_local_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; - } else { - return GATT_ROLE_NONE; - } -} - -uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { - if (MP_OBJ_IS_TYPE(device, &bleio_local_peripheral_type)) { - return ((bleio_local_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; -// Does not exist yet. -// } else if (MP_OBJ_IS_TYPE(device, &bleio_local_central_type)) { -// return ((bleio_local_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; - } else { - return 0; - } -} diff --git a/ports/nrf/common-hal/bleio/__init__.h b/ports/nrf/common-hal/bleio/__init__.h index 9e044f37c..aed5df5e7 100644 --- a/ports/nrf/common-hal/bleio/__init__.h +++ b/ports/nrf/common-hal/bleio/__init__.h @@ -36,7 +36,6 @@ // 20 bytes max (23 - 3). #define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3) -gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device); uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); #endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H diff --git a/shared-bindings/bleio/Broadcaster.c b/shared-bindings/bleio/Broadcaster.c index e24d157ec..ae4f28b74 100644 --- a/shared-bindings/bleio/Broadcaster.c +++ b/shared-bindings/bleio/Broadcaster.c @@ -134,7 +134,7 @@ STATIC MP_DEFINE_CONST_DICT(bleio_broadcaster_locals_dict, bleio_broadcaster_loc const mp_obj_type_t bleio_broadcaster_type = { { &mp_type_type }, - .name = MP_QSTR_LocalPeripheral, + .name = MP_QSTR_Broadcaster, .make_new = bleio_broadcaster_make_new, .locals_dict = (mp_obj_dict_t*)&bleio_broadcaster_locals_dict }; diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index 7e36eaa46..b35907159 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -32,7 +32,7 @@ #include "shared-bindings/bleio/Broadcaster.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/LocalPeripheral.h" +#include "shared-bindings/bleio/PeripheralServer.h" #include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/Scanner.h" #include "shared-bindings/bleio/Service.h" @@ -78,7 +78,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Broadcaster), MP_ROM_PTR(&bleio_broadcaster_type) }, { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, - { MP_ROM_QSTR(MP_QSTR_LocalPeripheral), MP_ROM_PTR(&bleio_local_peripheral_type) }, + { MP_ROM_QSTR(MP_QSTR_PeripheralServer), MP_ROM_PTR(&bleio_peripheral_server_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, diff --git a/shared-module/bleio/Service.h b/shared-module/bleio/Service.h index 540749793..7fee57f1d 100644 --- a/shared-module/bleio/Service.h +++ b/shared-module/bleio/Service.h @@ -34,7 +34,7 @@ typedef struct { uint16_t handle; bool is_secondary; bleio_uuid_obj_t *uuid; - // May be a LocalPeripheral, RemotePeripheral, etc. + // May be a PeripheralServer, CentralClient, etc. mp_obj_t *device; mp_obj_t char_list; uint16_t start_handle; diff --git a/shared-module/bleio/__init__.h b/shared-module/bleio/__init__.h index 07d148594..f8f0e0660 100644 --- a/shared-module/bleio/__init__.h +++ b/shared-module/bleio/__init__.h @@ -27,12 +27,6 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H -typedef enum { - GATT_ROLE_NONE, - GATT_ROLE_SERVER, - GATT_ROLE_CLIENT, -} gatt_role_t; - extern void bleio_reset(void); #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H -- cgit v1.2.3 From 1dc3957e729a7bf4d6476c587f140e3d42fd1bd5 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 30 Dec 2018 22:31:51 -0500 Subject: LocalPeripheral is now Peripheral; more work on basic GATTS support; UART not working yet --- ports/nrf/common-hal/bleio/Characteristic.c | 84 ++++++- ports/nrf/common-hal/bleio/LocalPeripheral.c | 329 -------------------------- ports/nrf/common-hal/bleio/LocalPeripheral.h | 64 ----- ports/nrf/common-hal/bleio/Peripheral.c | 333 +++++++++++++++++++++++++++ ports/nrf/common-hal/bleio/Peripheral.h | 53 +++++ ports/nrf/common-hal/bleio/__init__.c | 23 ++ ports/nrf/common-hal/bleio/__init__.h | 1 + py/obj.h | 1 + py/objarray.c | 6 + shared-bindings/bleio/Characteristic.c | 4 +- shared-bindings/bleio/Characteristic.h | 4 +- shared-bindings/bleio/LocalPeripheral.c | 250 -------------------- shared-bindings/bleio/LocalPeripheral.h | 39 ---- shared-bindings/bleio/Peripheral.c | 250 ++++++++++++++++++++ shared-bindings/bleio/Peripheral.h | 40 ++++ shared-bindings/bleio/__init__.c | 4 +- shared-module/bleio/Service.h | 2 +- shared-module/bleio/__init__.h | 6 + 18 files changed, 795 insertions(+), 698 deletions(-) delete mode 100644 ports/nrf/common-hal/bleio/LocalPeripheral.c delete mode 100644 ports/nrf/common-hal/bleio/LocalPeripheral.h create mode 100644 ports/nrf/common-hal/bleio/Peripheral.c create mode 100644 ports/nrf/common-hal/bleio/Peripheral.h delete mode 100644 shared-bindings/bleio/LocalPeripheral.c delete mode 100644 shared-bindings/bleio/LocalPeripheral.h create mode 100644 shared-bindings/bleio/Peripheral.c create mode 100644 shared-bindings/bleio/Peripheral.h (limited to 'shared-module') diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index 17656c269..7a32e4928 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -35,13 +35,63 @@ #include "common-hal/bleio/__init__.h" #include "shared-module/bleio/Characteristic.h" - +// TODO - should these be per object?? ***** STATIC volatile bleio_characteristic_obj_t *m_read_characteristic; STATIC volatile uint8_t m_tx_in_progress; STATIC nrf_mutex_t *m_write_mutex; +STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) { + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); + uint16_t cccd; + ble_gatts_value_t value = { + .p_value = (uint8_t*) &cccd, + .len = 2, + }; + + const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->cccd_handle, &value); + + + if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) { + // CCCD is not set, so say that neither Notify nor Indicate is enabled. + cccd = 0; + } else if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read CCD value, err 0x%04x"), err_code); + } + + return cccd; +} + +STATIC void gatts_read(bleio_characteristic_obj_t *characteristic) { + // This might be BLE_CONN_HANDLE_INVALID if we're not conected, but that's OK, because + // we can still read and write the local value. + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); + + mp_buffer_info_t bufinfo; + ble_gatts_value_t gatts_value = { + .p_value = NULL, + .len = 0, + }; + + // Read once to find out what size buffer we need, then read again to fill buffer. + + uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value); + if (err_code == NRF_SUCCESS) { + characteristic->value_data = mp_obj_new_bytearray_of_zeros(gatts_value.len); + mp_get_buffer_raise(characteristic->value_data, &bufinfo, MP_BUFFER_WRITE); + + // Read again, with the correct size of buffer. + err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value); + } + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code); + } +} + STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { + // This might be BLE_CONN_HANDLE_INVALID if we're not conected, but that's OK, because + // we can still read and write the local value. const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); ble_gatts_value_t gatts_value = { @@ -55,12 +105,12 @@ STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_in } } -STATIC void gatts_notify(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { +STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo, uint16_t hvx_type) { uint16_t hvx_len = bufinfo->len; ble_gatts_hvx_params_t hvx_params = { .handle = characteristic->handle, - .type = BLE_GATT_HVX_NOTIFICATION, + .type = hvx_type, .offset = 0, .p_len = &hvx_len, .p_data = bufinfo->buf, @@ -163,30 +213,46 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self) ble_drv_add_event_handler(on_ble_evt, NULL); } -void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self) { +void common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) { switch (common_hal_bleio_device_get_gatt_role(self->service->device)) { case GATT_ROLE_CLIENT: gattc_read(self); break; + case GATT_ROLE_SERVER: + gatts_read(self); + break; + default: mp_raise_RuntimeError(translate("bad GATT role")); break; } } -void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { +void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { + bool sent = false; + uint16_t cccd = 0; + switch (common_hal_bleio_device_get_gatt_role(self->service->device)) { case GATT_ROLE_SERVER: - if (self->props.notify) { - gatts_notify(self, bufinfo); - } else { + if (self->props.notify || self->props.indicate) { + cccd = get_cccd(self); + } + // It's possible that both notify and indicate are set. + if (self->props.notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) { + gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_NOTIFICATION); + sent = true; + } + if (self->props.indicate && (cccd & BLE_GATT_HVX_INDICATION)) { + gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_INDICATION); + sent = true; + } + if (!sent) { gatts_write(self, bufinfo); } break; case GATT_ROLE_CLIENT: - // TODO: Add indications gattc_write(self, bufinfo); break; diff --git a/ports/nrf/common-hal/bleio/LocalPeripheral.c b/ports/nrf/common-hal/bleio/LocalPeripheral.c deleted file mode 100644 index 2a502c8dd..000000000 --- a/ports/nrf/common-hal/bleio/LocalPeripheral.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * 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 -#include - -#include "ble.h" -#include "ble_drv.h" -#include "ble_hci.h" -#include "nrf_soc.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/LocalPeripheral.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) -#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS) -#define BLE_SLAVE_LATENCY 0 -#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) - -#define BLE_ADV_LENGTH_FIELD_SIZE 1 -#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 -#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 - -static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; - -STATIC void check_data_fit(size_t pos, size_t data_len) { - if (pos + data_len >= BLE_GAP_ADV_SET_DATA_SIZE_MAX) { - mp_raise_ValueError(translate("Data too large for advertisement packet")); - } -} - -STATIC uint32_t add_services_to_advertisement(bleio_local_peripheral_obj_t *self, size_t* adv_data_pos_p, size_t uuid_len) { - uint32_t uuids_total_size = 0; - const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); - uint32_t err_code = NRF_SUCCESS; - - check_data_fit(*adv_data_pos_p, 1 + 1); - - // Remember where length byte is; fill in later when we know the size. - const size_t length_pos = *adv_data_pos_p; - (*adv_data_pos_p)++; - - self->adv_data[(*adv_data_pos_p)++] = (uuid_len == 16) - ? BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE - : BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE; - - for (size_t i = 0; i < service_list->len; ++i) { - const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); - uint8_t encoded_size = 0; - - // Skip services of the wrong length and secondary services. - if (common_hal_bleio_uuid_get_size(service->uuid) != uuid_len || service->is_secondary) { - continue; - } - - ble_uuid_t uuid; - bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); - - err_code = sd_ble_uuid_encode(&uuid, &encoded_size, &(self->adv_data[*adv_data_pos_p])); - if (err_code != NRF_SUCCESS) { - return err_code; - } - - check_data_fit(*adv_data_pos_p, encoded_size); - uuids_total_size += encoded_size; - (*adv_data_pos_p) += encoded_size; - } - - self->adv_data[length_pos] = 1 + uuids_total_size; // 1 for the field type. - return err_code; -} - - - -STATIC uint32_t set_advertisement_data(bleio_local_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) { - common_hal_bleio_adapter_set_enabled(true); - - size_t adv_data_pos = 0; - uint32_t err_code; - - GET_STR_DATA_LEN(self->name, name_data, name_len); - if (name_len > 0) { - ble_gap_conn_sec_mode_t sec_mode; - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); - - // We'll add the name after everything else, shortening it if necessary. - err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len); - if (err_code != NRF_SUCCESS) { - return err_code; - } - } - - if (raw_data->len != 0) { - // User-supplied advertising packet. - check_data_fit(adv_data_pos, raw_data->len); - memcpy(&(self->adv_data[adv_data_pos]), raw_data->buf, raw_data->len); - adv_data_pos += raw_data->len; - } else { - // Build up advertising packet. - check_data_fit(adv_data_pos, 1 + 1 + 1); - self->adv_data[adv_data_pos++] = 2; - self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_FLAGS; - self->adv_data[adv_data_pos++] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; - - // The 16-bit ids and 128-bit ids are grouped together by length, so find it whether we have - // 16 and/or 128-bit service UUIDs. - - const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); - if (service_list->len > 0) { - bool has_128bit_services = false; - bool has_16bit_services = false; - - for (size_t i = 0; i < service_list->len; ++i) { - const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); - - if (service->is_secondary) { - continue; - } - - switch (common_hal_bleio_uuid_get_size(service->uuid)) { - case 16: - has_16bit_services = true; - break; - case 128: - has_128bit_services = true; - break; - } - } - - // Add 16-bit service UUID's in a group, then 128-bit service UUID's. - - if (has_16bit_services) { - err_code = add_services_to_advertisement(self, &adv_data_pos, 16); - if (err_code != NRF_SUCCESS) { - return err_code; - } - } - - if (has_128bit_services) { - err_code = add_services_to_advertisement(self, &adv_data_pos, 128); - if (err_code != NRF_SUCCESS) { - return err_code; - } - } - } - - // Always include TX power. - check_data_fit(adv_data_pos, 1 + 1 + 1); - self->adv_data[adv_data_pos++] = 1 + 1; - self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_TX_POWER_LEVEL; - self->adv_data[adv_data_pos++] = 0; // TODO - allow power level to be set later. - - // We need room for at least a one-character name. - check_data_fit(adv_data_pos, 1 + 1 + 1); - - // How big a name can we fit? - size_t bytes_left = BLE_GAP_ADV_SET_DATA_SIZE_MAX - adv_data_pos - 1 - 1; - size_t partial_name_len = MIN(bytes_left, name_len); - self->adv_data[adv_data_pos++] = 1 + partial_name_len; - self->adv_data[adv_data_pos++] = (partial_name_len == name_len) - ? BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME - : BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME; - memcpy(&(self->adv_data[adv_data_pos]), name_data, partial_name_len); - adv_data_pos += partial_name_len; - } // end of advertising packet construction - - static ble_gap_adv_params_t m_adv_params = { - .interval = MSEC_TO_UNITS(1000, UNIT_0_625_MS), - .properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED, - .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED, - .filter_policy = BLE_GAP_ADV_FP_ANY, - .primary_phy = BLE_GAP_PHY_1MBPS, - }; - - if (!connectable) { - m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED; - } - - common_hal_bleio_local_peripheral_stop_advertising(self); - - const ble_gap_adv_data_t ble_gap_adv_data = { - .adv_data.p_data = self->adv_data, - .adv_data.len = adv_data_pos, - }; - - err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params); - if (err_code != NRF_SUCCESS) { - return err_code; - } - - err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_CUSTOM); - - return err_code; -} - -STATIC void on_ble_evt(ble_evt_t *ble_evt, void *self_in) { - bleio_local_peripheral_obj_t *self = (bleio_local_peripheral_obj_t*)self_in; - - switch (ble_evt->header.evt_id) { - case BLE_GAP_EVT_CONNECTED: { - // Central has connected. - ble_gap_conn_params_t conn_params; - self->conn_handle = ble_evt->evt.gap_evt.conn_handle; - sd_ble_gap_ppcp_get(&conn_params); - sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params); - break; - } - - case BLE_GAP_EVT_DISCONNECTED: - // Central has disconnected. - self->conn_handle = BLE_CONN_HANDLE_INVALID; - break; - - case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { - ble_gap_phys_t const phys = { - .rx_phys = BLE_GAP_PHY_AUTO, - .tx_phys = BLE_GAP_PHY_AUTO, - }; - sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys); - break; - } - - case BLE_GAP_EVT_ADV_SET_TERMINATED: - // Someday may handle timeouts or limit reached. - break; - - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: - sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); - break; - - case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { - ble_gap_evt_conn_param_update_request_t *request = &ble_evt->evt.gap_evt.params.conn_param_update_request; - sd_ble_gap_conn_param_update(self->conn_handle, &request->conn_params); - break; - } - - case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: { - sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT); - break; - } - - } -} - - -void common_hal_bleio_local_peripheral_construct(bleio_local_peripheral_obj_t *self) { - common_hal_bleio_adapter_set_enabled(true); // TODO -- Do this somewhere else maybe bleio __init__ - - self->gatt_role = GATT_ROLE_NONE; - self->conn_handle = BLE_CONN_HANDLE_INVALID; - - // Add all the services. - - mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); - for (size_t service_idx = 0; service_idx < service_list->len; ++service_idx) { - bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[service_idx]); - - ble_uuid_t uuid; - bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); - - uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; - if (service->is_secondary) { - service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; - } - - const uint32_t err_code = sd_ble_gatts_service_add(service_type, &uuid, &service->handle); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to add service, err 0x%04x"), err_code); - } - - // Once the service has been registered, its characteristics can be added. - common_hal_bleio_service_add_all_characteristics(service); - } -} - - -bool common_hal_bleio_local_peripheral_get_connected(bleio_local_peripheral_obj_t *self) { - return self->conn_handle != BLE_CONN_HANDLE_INVALID; -} - -void common_hal_bleio_local_peripheral_start_advertising(bleio_local_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) { - if (connectable) { - ble_drv_add_event_handler(on_ble_evt, self); - } - - const uint32_t err_code = set_advertisement_data(self, connectable, raw_data); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code); - } -} - -void common_hal_bleio_local_peripheral_stop_advertising(bleio_local_peripheral_obj_t *self) { - - if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) - return; - - const uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle); - - if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { - mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code); - } -} diff --git a/ports/nrf/common-hal/bleio/LocalPeripheral.h b/ports/nrf/common-hal/bleio/LocalPeripheral.h deleted file mode 100644 index 8d84867ae..000000000 --- a/ports/nrf/common-hal/bleio/LocalPeripheral.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H -#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H - -#include - -#include "ble.h" - -#include "shared-module/bleio/__init__.h" -#include "shared-module/bleio/Address.h" - -// typedef struct { -// mp_obj_base_t base; -// bool is_peripheral; -// mp_obj_t name; -// bleio_address_obj_t address; -// volatile uint16_t conn_handle; -// mp_obj_t service_list; -// mp_obj_t notif_handler; -// mp_obj_t conn_handler; -// } bleio_device_obj_t; - -typedef struct { - mp_obj_base_t base; - mp_obj_t name; - gatt_role_t gatt_role; - volatile uint16_t conn_handle; - mp_obj_t service_list; - mp_obj_t notif_handler; - mp_obj_t conn_handler; - // The advertising data buffer is held by us, not by the SD, so we must - // maintain it and not change it. If we need to change its contents during advertising, - // there are tricks to get the SD to notice (see DevZone - TBS). - uint8_t adv_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; - -} bleio_local_peripheral_obj_t; - -#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H diff --git a/ports/nrf/common-hal/bleio/Peripheral.c b/ports/nrf/common-hal/bleio/Peripheral.c new file mode 100644 index 000000000..d2eef596d --- /dev/null +++ b/ports/nrf/common-hal/bleio/Peripheral.c @@ -0,0 +1,333 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * 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 +#include + +#include "ble.h" +#include "ble_drv.h" +#include "ble_hci.h" +#include "nrf_soc.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Peripheral.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" + +#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) +#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS) +#define BLE_SLAVE_LATENCY 0 +#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) + +#define BLE_ADV_LENGTH_FIELD_SIZE 1 +#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 +#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 + +static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; + +STATIC void check_data_fit(size_t pos, size_t data_len) { + if (pos + data_len >= BLE_GAP_ADV_SET_DATA_SIZE_MAX) { + mp_raise_ValueError(translate("Data too large for advertisement packet")); + } +} + +STATIC uint32_t add_services_to_advertisement(bleio_peripheral_obj_t *self, size_t* adv_data_pos_p, size_t uuid_len) { + uint32_t uuids_total_size = 0; + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); + uint32_t err_code = NRF_SUCCESS; + + check_data_fit(*adv_data_pos_p, 1 + 1); + + // Remember where length byte is; fill in later when we know the size. + const size_t length_pos = *adv_data_pos_p; + (*adv_data_pos_p)++; + + self->adv_data[(*adv_data_pos_p)++] = (uuid_len == 16) + ? BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE + : BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE; + + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + uint8_t encoded_size = 0; + + // Skip services of the wrong length and secondary services. + if (common_hal_bleio_uuid_get_size(service->uuid) != uuid_len || service->is_secondary) { + continue; + } + + ble_uuid_t uuid; + bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); + + err_code = sd_ble_uuid_encode(&uuid, &encoded_size, &(self->adv_data[*adv_data_pos_p])); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + check_data_fit(*adv_data_pos_p, encoded_size); + uuids_total_size += encoded_size; + (*adv_data_pos_p) += encoded_size; + } + + self->adv_data[length_pos] = 1 + uuids_total_size; // 1 for the field type. + return err_code; +} + + + +STATIC uint32_t set_advertisement_data(bleio_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) { + common_hal_bleio_adapter_set_enabled(true); + + size_t adv_data_pos = 0; + uint32_t err_code; + + GET_STR_DATA_LEN(self->name, name_data, name_len); + if (name_len > 0) { + ble_gap_conn_sec_mode_t sec_mode; + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); + + // We'll add the name after everything else, shortening it if necessary. + err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len); + if (err_code != NRF_SUCCESS) { + return err_code; + } + } + + if (raw_data->len != 0) { + // User-supplied advertising packet. + check_data_fit(adv_data_pos, raw_data->len); + memcpy(&(self->adv_data[adv_data_pos]), raw_data->buf, raw_data->len); + adv_data_pos += raw_data->len; + } else { + // Build up advertising packet. + check_data_fit(adv_data_pos, 1 + 1 + 1); + self->adv_data[adv_data_pos++] = 2; + self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_FLAGS; + self->adv_data[adv_data_pos++] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + + // The 16-bit ids and 128-bit ids are grouped together by length, so find it whether we have + // 16 and/or 128-bit service UUIDs. + + const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); + if (service_list->len > 0) { + bool has_128bit_services = false; + bool has_16bit_services = false; + + for (size_t i = 0; i < service_list->len; ++i) { + const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]); + + if (service->is_secondary) { + continue; + } + + switch (common_hal_bleio_uuid_get_size(service->uuid)) { + case 16: + has_16bit_services = true; + break; + case 128: + has_128bit_services = true; + break; + } + } + + // Add 16-bit service UUID's in a group, then 128-bit service UUID's. + + if (has_16bit_services) { + err_code = add_services_to_advertisement(self, &adv_data_pos, 16); + if (err_code != NRF_SUCCESS) { + return err_code; + } + } + + if (has_128bit_services) { + err_code = add_services_to_advertisement(self, &adv_data_pos, 128); + if (err_code != NRF_SUCCESS) { + return err_code; + } + } + } + + // Always include TX power. + check_data_fit(adv_data_pos, 1 + 1 + 1); + self->adv_data[adv_data_pos++] = 1 + 1; + self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_TX_POWER_LEVEL; + self->adv_data[adv_data_pos++] = 0; // TODO - allow power level to be set later. + + // We need room for at least a one-character name. + check_data_fit(adv_data_pos, 1 + 1 + 1); + + // How big a name can we fit? + size_t bytes_left = BLE_GAP_ADV_SET_DATA_SIZE_MAX - adv_data_pos - 1 - 1; + size_t partial_name_len = MIN(bytes_left, name_len); + self->adv_data[adv_data_pos++] = 1 + partial_name_len; + self->adv_data[adv_data_pos++] = (partial_name_len == name_len) + ? BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME + : BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME; + memcpy(&(self->adv_data[adv_data_pos]), name_data, partial_name_len); + adv_data_pos += partial_name_len; + } // end of advertising packet construction + + static ble_gap_adv_params_t m_adv_params = { + .interval = MSEC_TO_UNITS(1000, UNIT_0_625_MS), + .properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED, + .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED, + .filter_policy = BLE_GAP_ADV_FP_ANY, + .primary_phy = BLE_GAP_PHY_1MBPS, + }; + + if (!connectable) { + m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED; + } + + common_hal_bleio_peripheral_stop_advertising(self); + + const ble_gap_adv_data_t ble_gap_adv_data = { + .adv_data.p_data = self->adv_data, + .adv_data.len = adv_data_pos, + }; + + err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_CUSTOM); + + return err_code; +} + +STATIC void on_ble_evt(ble_evt_t *ble_evt, void *self_in) { + bleio_peripheral_obj_t *self = (bleio_peripheral_obj_t*)self_in; + + switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_CONNECTED: { + // Central has connected. + ble_gap_conn_params_t conn_params; + self->conn_handle = ble_evt->evt.gap_evt.conn_handle; + sd_ble_gap_ppcp_get(&conn_params); + sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params); + break; + } + + case BLE_GAP_EVT_DISCONNECTED: + // Central has disconnected. + self->conn_handle = BLE_CONN_HANDLE_INVALID; + break; + + case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { + ble_gap_phys_t const phys = { + .rx_phys = BLE_GAP_PHY_AUTO, + .tx_phys = BLE_GAP_PHY_AUTO, + }; + sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys); + break; + } + + case BLE_GAP_EVT_ADV_SET_TERMINATED: + // Someday may handle timeouts or limit reached. + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { + ble_gap_evt_conn_param_update_request_t *request = &ble_evt->evt.gap_evt.params.conn_param_update_request; + sd_ble_gap_conn_param_update(self->conn_handle, &request->conn_params); + break; + } + + case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: { + sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT); + break; + } + + default: + mp_printf(&mp_plat_print, "Unhandled event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + + +void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self) { + common_hal_bleio_adapter_set_enabled(true); // TODO -- Do this somewhere else maybe bleio __init__ + + self->gatt_role = GATT_ROLE_SERVER; + self->conn_handle = BLE_CONN_HANDLE_INVALID; + + // Add all the services. + + mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list); + for (size_t service_idx = 0; service_idx < service_list->len; ++service_idx) { + bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[service_idx]); + + ble_uuid_t uuid; + bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); + + uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; + if (service->is_secondary) { + service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; + } + + const uint32_t err_code = sd_ble_gatts_service_add(service_type, &uuid, &service->handle); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to add service, err 0x%04x"), err_code); + } + + // Once the service has been registered, its characteristics can be added. + common_hal_bleio_service_add_all_characteristics(service); + } +} + + +bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) { + return self->conn_handle != BLE_CONN_HANDLE_INVALID; +} + +void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) { + if (connectable) { + ble_drv_add_event_handler(on_ble_evt, self); + } + + const uint32_t err_code = set_advertisement_data(self, connectable, raw_data); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *self) { + + if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) + return; + + const uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle); + + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { + mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code); + } +} diff --git a/ports/nrf/common-hal/bleio/Peripheral.h b/ports/nrf/common-hal/bleio/Peripheral.h new file mode 100644 index 000000000..b255fe9f4 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Peripheral.h @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_COMMON_HAL_BLEIO_PERIPHERAL_H +#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_PERIPHERAL_H + +#include + +#include "ble.h" + +#include "shared-module/bleio/__init__.h" +#include "shared-module/bleio/Address.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t name; + gatt_role_t gatt_role; + volatile uint16_t conn_handle; + mp_obj_t service_list; + mp_obj_t notif_handler; + mp_obj_t conn_handler; + // The advertising data buffer is held by us, not by the SD, so we must + // maintain it and not change it. If we need to change its contents during advertising, + // there are tricks to get the SD to notice (see DevZone - TBS). + uint8_t adv_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; + +} bleio_peripheral_obj_t; + +#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_PERIPHERAL_H diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c index 9429aaf44..cfb701019 100644 --- a/ports/nrf/common-hal/bleio/__init__.c +++ b/ports/nrf/common-hal/bleio/__init__.c @@ -27,6 +27,7 @@ #include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/Peripheral.h" #include "common-hal/bleio/__init__.h" // Turn off BLE on a reset or reload. @@ -43,3 +44,25 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = { .type = &bleio_adapter_type, }, }; + +gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device) { + if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { + return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; +// Does not exist yet. +// } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { +// return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; + } else { + return GATT_ROLE_NONE; + } +} + +uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { + if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { + return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; +// Does not exist yet. +// } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { +// return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; + } else { + return 0; + } +} diff --git a/ports/nrf/common-hal/bleio/__init__.h b/ports/nrf/common-hal/bleio/__init__.h index aed5df5e7..9e044f37c 100644 --- a/ports/nrf/common-hal/bleio/__init__.h +++ b/ports/nrf/common-hal/bleio/__init__.h @@ -36,6 +36,7 @@ // 20 bytes max (23 - 3). #define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3) +gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device); uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); #endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H diff --git a/py/obj.h b/py/obj.h index 8b6772873..ece3b0ee0 100644 --- a/py/obj.h +++ b/py/obj.h @@ -640,6 +640,7 @@ mp_obj_t mp_obj_new_str_via_qstr(const char* data, size_t len); mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr); mp_obj_t mp_obj_new_bytes(const byte* data, size_t len); mp_obj_t mp_obj_new_bytearray(size_t n, void *items); +mp_obj_t mp_obj_new_bytearray_of_zeros(size_t n); mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items); #if MICROPY_PY_BUILTINS_FLOAT mp_obj_t mp_obj_new_int_from_float(mp_float_t val); diff --git a/py/objarray.c b/py/objarray.c index 69ff6f328..0c8c7d855 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -611,6 +611,12 @@ mp_obj_t mp_obj_new_bytearray(size_t n, void *items) { return MP_OBJ_FROM_PTR(o); } +mp_obj_t mp_obj_new_bytearray_of_zeros(size_t n) { + mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n); + memset(o->items, 0, n); + return MP_OBJ_FROM_PTR(o); +} + // Create bytearray which references specified memory area mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) { mp_obj_array_t *o = m_new_obj(mp_obj_array_t); diff --git a/shared-bindings/bleio/Characteristic.c b/shared-bindings/bleio/Characteristic.c index 4fb71de47..d6ae4dfe4 100644 --- a/shared-bindings/bleio/Characteristic.c +++ b/shared-bindings/bleio/Characteristic.c @@ -233,7 +233,7 @@ const mp_obj_property_t bleio_characteristic_uuid_obj = { STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) { bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_bleio_characteristic_read_value(self); + common_hal_bleio_characteristic_get_value(self); return self->value_data; } @@ -245,7 +245,7 @@ STATIC mp_obj_t bleio_characteristic_set_value(mp_obj_t self_in, mp_obj_t value_ mp_buffer_info_t bufinfo; mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); - common_hal_bleio_characteristic_write_value(self, &bufinfo); + common_hal_bleio_characteristic_set_value(self, &bufinfo); return mp_const_none; } diff --git a/shared-bindings/bleio/Characteristic.h b/shared-bindings/bleio/Characteristic.h index 0e99e97cc..aec60ebbc 100644 --- a/shared-bindings/bleio/Characteristic.h +++ b/shared-bindings/bleio/Characteristic.h @@ -32,7 +32,7 @@ extern const mp_obj_type_t bleio_characteristic_type; extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self); -extern void common_hal_bleio_characteristic_read_value(bleio_characteristic_obj_t *self); -extern void common_hal_bleio_characteristic_write_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); +extern void common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self); +extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H diff --git a/shared-bindings/bleio/LocalPeripheral.c b/shared-bindings/bleio/LocalPeripheral.c deleted file mode 100644 index a40e0c043..000000000 --- a/shared-bindings/bleio/LocalPeripheral.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016 Glenn Ruben Bakke - * 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 -#include - -#include "ble_drv.h" -#include "py/objarray.h" -#include "py/objproperty.h" -#include "py/objstr.h" -#include "py/runtime.h" - -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/AddressType.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/LocalPeripheral.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" -#include "shared-module/bleio/AdvertisementData.h" -#include "shared-module/bleio/ScanEntry.h" - -#include "common-hal/bleio/LocalPeripheral.h" - -// TODO: Add unique MAC address part to name -static const char default_name[] = "CIRCUITPY"; - -//| .. currentmodule:: bleio -//| -//| :class:`LocalPeripheral` -- A BLE peripheral device -//| ========================================================= -//| -//| Implement a BLE peripheral which runs locally. -//| Set up using the supplied services, and then allow advertising to be started and stopped. -//| -//| Usage:: -//| -//| import bleio -//| -//| # Create a Characteristic. -//| chara = bleio.Characteristic(bleio.UUID(0x2919), read=True, notify=True) -//| -//| # Create a Service providing that one Characteristic. -//| serv = bleio.Service(bleio.UUID(0x180f), [chara]) -//| -//| # Create a peripheral and start it up. -//| periph = bleio.LocalPeripheral([service]) -//| periph.start_advertising() -//| -//| while not periph.connected(): -//| # Wait for connection. -//| pass -//| -//| .. class:: LocalPeripheral(services, *, name='CIRCUITPY') -//| -//| Create a new LocalPeripheral object. - -//| :param iterable services: the Service objects representing services available from this peripheral. -//| :param str name: The name used when advertising this peripheral -//| - -STATIC mp_obj_t bleio_local_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { - mp_arg_check_num(n_args, n_kw, 0, 1, true); - bleio_local_peripheral_obj_t *self = m_new_obj(bleio_local_peripheral_obj_t); - self->base.type = &bleio_local_peripheral_type; - self->service_list = mp_obj_new_list(0, NULL); - self->notif_handler = mp_const_none; - - mp_map_t kw_args; - mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); - - enum { ARG_services, ARG_name }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_services, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_name, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // If services is not an iterable, an exception will be thrown. - mp_obj_iter_buf_t iter_buf; - mp_obj_t iterable = mp_getiter(args[ARG_services].u_obj, &iter_buf); - 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("services includes an object that is not a Service")); - } - bleio_service_obj_t *service_ptr = MP_OBJ_TO_PTR(service); - service_ptr->device = MP_OBJ_FROM_PTR(self); - mp_obj_list_append(self->service_list, service); - } - - const mp_obj_t name = args[ARG_name].u_obj; - if (name == mp_const_none) { - self->name = mp_obj_new_str(default_name, strlen(default_name)); - } else if (MP_OBJ_IS_STR(name)) { - self->name = name; - } else { - mp_raise_ValueError(translate("name must be a string")); - } - - // Do port-specific initialization. - common_hal_bleio_local_peripheral_construct(self); - - return MP_OBJ_FROM_PTR(self); -} - -//| .. attribute:: connected -//| -//| True if connected to a BLE Central device. -//| -STATIC mp_obj_t bleio_local_peripheral_get_connected(mp_obj_t self_in) { - bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - // Return list as a tuple so user won't be able to change it. - return mp_obj_new_bool(common_hal_bleio_local_peripheral_get_connected(self)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_connected_obj, bleio_local_peripheral_get_connected); - -const mp_obj_property_t bleio_local_peripheral_connected_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_local_peripheral_get_connected_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: services -//| -//| A `tuple` of `bleio.Service` that are offered by this peripheral. (read-only) -//| -STATIC mp_obj_t bleio_local_peripheral_get_services(mp_obj_t self_in) { - bleio_local_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 = MP_OBJ_TO_PTR(self->service_list); - return mp_obj_new_tuple(service_list->len, service_list->items); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_services_obj, bleio_local_peripheral_get_services); - -const mp_obj_property_t bleio_local_peripheral_services_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_local_peripheral_get_services_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: name -//| -//| The peripheral's name, included when advertising. (read-only) -//| -STATIC mp_obj_t bleio_local_peripheral_get_name(mp_obj_t self_in) { - bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return self->name; -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_name_obj, bleio_local_peripheral_get_name); - -const mp_obj_property_t bleio_local_peripheral_name_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_local_peripheral_get_name_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. method:: start_advertising(*, connectable=True, data=None) -//| -//| Starts advertising the peripheral. The peripheral's name and -//| services are included in the advertisement packets. -//| -//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral. -//| :param buf data: If not None, then send data bytes in advertising packets. -//| -STATIC mp_obj_t bleio_local_peripheral_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_connectable, ARG_data }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, - { MP_QSTR_data, 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 bufinfo = { 0 }; - if (args[ARG_data].u_obj != mp_const_none) { - mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); - } - - common_hal_bleio_local_peripheral_start_advertising(self, args[ARG_connectable].u_bool, &bufinfo); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_local_peripheral_start_advertising_obj, 0, bleio_local_peripheral_start_advertising); - -//| .. method:: stop_advertising() -//| -//| Stop sending advertising packets. -STATIC mp_obj_t bleio_local_peripheral_stop_advertising(mp_obj_t self_in) { - bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - common_hal_bleio_local_peripheral_stop_advertising(self); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_stop_advertising_obj, bleio_local_peripheral_stop_advertising); - -STATIC const mp_rom_map_elem_t bleio_local_peripheral_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_local_peripheral_start_advertising_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_local_peripheral_stop_advertising_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_local_peripheral_connected_obj) }, - { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_local_peripheral_name_obj) }, - { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_local_peripheral_services_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_local_peripheral_locals_dict, bleio_local_peripheral_locals_dict_table); - -const mp_obj_type_t bleio_local_peripheral_type = { - { &mp_type_type }, - .name = MP_QSTR_LocalPeripheral, - .make_new = bleio_local_peripheral_make_new, - .locals_dict = (mp_obj_dict_t*)&bleio_local_peripheral_locals_dict -}; diff --git a/shared-bindings/bleio/LocalPeripheral.h b/shared-bindings/bleio/LocalPeripheral.h deleted file mode 100644 index bd6d5cbbb..000000000 --- a/shared-bindings/bleio/LocalPeripheral.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * 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_LOCALPERIPHERAL_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_LOCALPERIPHERAL_H - -#include "common-hal/bleio/LocalPeripheral.h" - -extern const mp_obj_type_t bleio_local_peripheral_type; - -extern void common_hal_bleio_local_peripheral_construct(bleio_local_peripheral_obj_t *self); -extern bool common_hal_bleio_local_peripheral_get_connected(bleio_local_peripheral_obj_t *self); -extern void common_hal_bleio_local_peripheral_start_advertising(bleio_local_peripheral_obj_t *device, bool connectable, mp_buffer_info_t *raw_data); -extern void common_hal_bleio_local_peripheral_stop_advertising(bleio_local_peripheral_obj_t *device); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_LOCALPERIPHERAL_H diff --git a/shared-bindings/bleio/Peripheral.c b/shared-bindings/bleio/Peripheral.c new file mode 100644 index 000000000..1027bbb1f --- /dev/null +++ b/shared-bindings/bleio/Peripheral.c @@ -0,0 +1,250 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Glenn Ruben Bakke + * 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 +#include + +#include "ble_drv.h" +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" + +#include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/AddressType.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Peripheral.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" +#include "shared-module/bleio/AdvertisementData.h" +#include "shared-module/bleio/ScanEntry.h" + +#include "common-hal/bleio/Peripheral.h" + +// TODO: Add unique MAC address part to name +static const char default_name[] = "CIRCUITPY"; + +//| .. currentmodule:: bleio +//| +//| :class:`Peripheral` -- A BLE peripheral device +//| ========================================================= +//| +//| Implement a BLE peripheral which runs locally. +//| Set up using the supplied services, and then allow advertising to be started and stopped. +//| +//| Usage:: +//| +//| import bleio +//| +//| # Create a Characteristic. +//| chara = bleio.Characteristic(bleio.UUID(0x2919), read=True, notify=True) +//| +//| # Create a Service providing that one Characteristic. +//| serv = bleio.Service(bleio.UUID(0x180f), [chara]) +//| +//| # Create a peripheral and start it up. +//| periph = bleio.Peripheral([service]) +//| periph.start_advertising() +//| +//| while not periph.connected(): +//| # Wait for connection. +//| pass +//| +//| .. class:: Peripheral(services, *, name='CIRCUITPY') +//| +//| Create a new Peripheral object. + +//| :param iterable services: the Service objects representing services available from this peripheral. +//| :param str name: The name used when advertising this peripheral +//| + +STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 0, 1, true); + bleio_peripheral_obj_t *self = m_new_obj(bleio_peripheral_obj_t); + self->base.type = &bleio_peripheral_type; + self->service_list = mp_obj_new_list(0, NULL); + self->notif_handler = mp_const_none; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + enum { ARG_services, ARG_name }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_services, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_name, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // If services is not an iterable, an exception will be thrown. + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(args[ARG_services].u_obj, &iter_buf); + 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("services includes an object that is not a Service")); + } + bleio_service_obj_t *service_ptr = MP_OBJ_TO_PTR(service); + service_ptr->device = MP_OBJ_FROM_PTR(self); + mp_obj_list_append(self->service_list, service); + } + + const mp_obj_t name = args[ARG_name].u_obj; + if (name == mp_const_none) { + self->name = mp_obj_new_str(default_name, strlen(default_name)); + } else if (MP_OBJ_IS_STR(name)) { + self->name = name; + } else { + mp_raise_ValueError(translate("name must be a string")); + } + + // Do port-specific initialization. + common_hal_bleio_peripheral_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. attribute:: connected +//| +//| True if connected to a BLE Central device. +//| +STATIC mp_obj_t bleio_peripheral_get_connected(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. + return mp_obj_new_bool(common_hal_bleio_peripheral_get_connected(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_connected_obj, bleio_peripheral_get_connected); + +const mp_obj_property_t bleio_peripheral_connected_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_peripheral_get_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: services +//| +//| A `tuple` of `bleio.Service` that are offered by this peripheral. (read-only) +//| +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 = MP_OBJ_TO_PTR(self->service_list); + return mp_obj_new_tuple(service_list->len, service_list->items); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services); + +const mp_obj_property_t bleio_peripheral_services_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_peripheral_get_services_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: name +//| +//| The peripheral's name, included when advertising. (read-only) +//| +STATIC mp_obj_t bleio_peripheral_get_name(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return self->name; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_name_obj, bleio_peripheral_get_name); + +const mp_obj_property_t bleio_peripheral_name_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_peripheral_get_name_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. method:: start_advertising(*, connectable=True, data=None) +//| +//| Starts advertising the peripheral. The peripheral's name and +//| services are included in the advertisement packets. +//| +//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral. +//| :param buf data: If not None, then send data bytes in advertising packets. +//| +STATIC mp_obj_t bleio_peripheral_start_advertising(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_connectable, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_data, 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 bufinfo = { 0 }; + if (args[ARG_data].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + } + + common_hal_bleio_peripheral_start_advertising(self, args[ARG_connectable].u_bool, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_start_advertising_obj, 0, bleio_peripheral_start_advertising); + +//| .. method:: stop_advertising() +//| +//| Stop sending advertising packets. +STATIC mp_obj_t bleio_peripheral_stop_advertising(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_peripheral_stop_advertising(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_stop_advertising_obj, bleio_peripheral_stop_advertising); + +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) }, + + // 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) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table); + +const mp_obj_type_t bleio_peripheral_type = { + { &mp_type_type }, + .name = MP_QSTR_Peripheral, + .make_new = bleio_peripheral_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_peripheral_locals_dict +}; diff --git a/shared-bindings/bleio/Peripheral.h b/shared-bindings/bleio/Peripheral.h new file mode 100644 index 000000000..02a5c1ef8 --- /dev/null +++ b/shared-bindings/bleio/Peripheral.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H + +#include "common-hal/bleio/Peripheral.h" + +extern const mp_obj_type_t bleio_peripheral_type; + +extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self); +extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self); +extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, mp_buffer_info_t *raw_data); +extern void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *device); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index b35907159..d30d59942 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -32,7 +32,7 @@ #include "shared-bindings/bleio/Broadcaster.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/PeripheralServer.h" +#include "shared-bindings/bleio/Peripheral.h" #include "shared-bindings/bleio/ScanEntry.h" #include "shared-bindings/bleio/Scanner.h" #include "shared-bindings/bleio/Service.h" @@ -78,7 +78,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Broadcaster), MP_ROM_PTR(&bleio_broadcaster_type) }, { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, - { MP_ROM_QSTR(MP_QSTR_PeripheralServer), MP_ROM_PTR(&bleio_peripheral_server_type) }, + { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&bleio_peripheral_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, diff --git a/shared-module/bleio/Service.h b/shared-module/bleio/Service.h index 7fee57f1d..828d4cdc8 100644 --- a/shared-module/bleio/Service.h +++ b/shared-module/bleio/Service.h @@ -34,7 +34,7 @@ typedef struct { uint16_t handle; bool is_secondary; bleio_uuid_obj_t *uuid; - // May be a PeripheralServer, CentralClient, etc. + // May be a Peripheral, Central, etc. mp_obj_t *device; mp_obj_t char_list; uint16_t start_handle; diff --git a/shared-module/bleio/__init__.h b/shared-module/bleio/__init__.h index f8f0e0660..07d148594 100644 --- a/shared-module/bleio/__init__.h +++ b/shared-module/bleio/__init__.h @@ -27,6 +27,12 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H +typedef enum { + GATT_ROLE_NONE, + GATT_ROLE_SERVER, + GATT_ROLE_CLIENT, +} gatt_role_t; + extern void bleio_reset(void); #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H -- cgit v1.2.3 From f66f55b4ed4a008fd328a377acbf731e5f22d6de Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 7 Jan 2019 22:46:20 -0500 Subject: add CharacteristicBuffer; UART seems to work! --- ports/nrf/.gitignore | 4 +- ports/nrf/Makefile | 1 + ports/nrf/bluetooth/ble_drv.c | 14 +++ ports/nrf/bluetooth/ble_drv.h | 1 + ports/nrf/bluetooth/download_ble_stack.sh | 32 ------ ports/nrf/common-hal/bleio/Characteristic.c | 50 +++++---- ports/nrf/common-hal/bleio/Characteristic.h | 46 +++++++++ ports/nrf/common-hal/bleio/CharacteristicBuffer.c | 75 ++++++++++++++ ports/nrf/common-hal/bleio/CharacteristicBuffer.h | 41 ++++++++ ports/nrf/common-hal/bleio/Peripheral.c | 2 +- ports/nrf/common-hal/bleio/Service.c | 5 + shared-bindings/bleio/Characteristic.c | 22 ++-- shared-bindings/bleio/Characteristic.h | 4 +- shared-bindings/bleio/CharacteristicBuffer.c | 119 ++++++++++++++++++++++ shared-bindings/bleio/CharacteristicBuffer.h | 39 +++++++ shared-bindings/bleio/__init__.c | 2 + shared-module/bleio/Characteristic.h | 17 +--- 17 files changed, 395 insertions(+), 79 deletions(-) delete mode 100755 ports/nrf/bluetooth/download_ble_stack.sh create mode 100644 ports/nrf/common-hal/bleio/Characteristic.h create mode 100644 ports/nrf/common-hal/bleio/CharacteristicBuffer.c create mode 100644 ports/nrf/common-hal/bleio/CharacteristicBuffer.h create mode 100644 shared-bindings/bleio/CharacteristicBuffer.c create mode 100644 shared-bindings/bleio/CharacteristicBuffer.h (limited to 'shared-module') diff --git a/ports/nrf/.gitignore b/ports/nrf/.gitignore index fc9f24b50..cda23c7a9 100644 --- a/ports/nrf/.gitignore +++ b/ports/nrf/.gitignore @@ -1,8 +1,8 @@ # Old Nordic soft devices that don't allow redistribution ######################################################### -bluetooth/s132_nrf52_2.0.1/ +drivers/bluetooth/s132_nrf52_2.0.1/ -!bluetooth/*/*.hex +!drivers/bluetooth/*/*.hex # Build files ##################### diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index f7e4a2988..2e345b9c7 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -174,6 +174,7 @@ SRC_COMMON_HAL += \ bleio/Adapter.c \ bleio/Broadcaster.c \ bleio/Characteristic.c \ + bleio/CharacteristicBuffer.c \ bleio/Descriptor.c \ bleio/Peripheral.c \ bleio/Scanner.c \ diff --git a/ports/nrf/bluetooth/ble_drv.c b/ports/nrf/bluetooth/ble_drv.c index 7082b1e59..cdc56319b 100644 --- a/ports/nrf/bluetooth/ble_drv.c +++ b/ports/nrf/bluetooth/ble_drv.c @@ -71,6 +71,20 @@ void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) { m_event_handlers = handler; } +void ble_drv_remove_event_handler(ble_drv_evt_handler_t func, void *param) { + event_handler_t *it = m_event_handlers; + event_handler_t **prev = &m_event_handlers; + while (it != NULL) { + if ((it->func == func) && (it->param == param)) { + // Splice out the matching handler. + *prev = it->next; + return; + } + prev = &(it->next); + it = it->next; + } +} + void SD_EVT_IRQHandler(void) { uint32_t evt_id; while (sd_evt_get(&evt_id) != NRF_ERROR_NOT_FOUND) { diff --git a/ports/nrf/bluetooth/ble_drv.h b/ports/nrf/bluetooth/ble_drv.h index 696aff1da..3d1f551c5 100644 --- a/ports/nrf/bluetooth/ble_drv.h +++ b/ports/nrf/bluetooth/ble_drv.h @@ -52,5 +52,6 @@ typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*); void ble_drv_reset(); void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param); +void ble_drv_remove_event_handler(ble_drv_evt_handler_t func, void *param); #endif // MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_DRV_H diff --git a/ports/nrf/bluetooth/download_ble_stack.sh b/ports/nrf/bluetooth/download_ble_stack.sh deleted file mode 100755 index 7e35c0680..000000000 --- a/ports/nrf/bluetooth/download_ble_stack.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -function download_s132_nrf52_2_0_1 -{ - echo "" - echo "####################################" - echo "### Downloading s132_nrf52_2.0.1 ###" - echo "####################################" - echo "" - - mkdir -p "${1}/s132_nrf52_2.0.1" - cd "${1}/s132_nrf52_2.0.1" - wget https://www.nordicsemi.com/api/sitecore/Products/MedialibraryZipDownload2 --post-data="ids=863031714A574444AADFE444EBE5BA9B|&fileName=DeviceDownload" -O temp.zip - unzip -u temp.zip - unzip -u s132nrf52201.zip - rm temp.zip s132nrf52201.zip - cd - -} - -SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -if [ $# -eq 0 ]; then - echo "No Bluetooth LE stack defined, downloading all." - download_s132_nrf52_2_0_1 "${SCRIPT_DIR}" -else - case $1 in - "s132_nrf52_2_0_1" ) - download_s132_nrf52_2_0_1 "${SCRIPT_DIR}" ;; - esac -fi - -exit 0 diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index a955395bd..bebdb31fa 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -33,6 +33,7 @@ #include "py/runtime.h" #include "common-hal/bleio/__init__.h" +#include "common-hal/bleio/Characteristic.h" #include "shared-module/bleio/Characteristic.h" // TODO - should these be per object?? ***** @@ -190,28 +191,41 @@ STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_in STATIC void characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) { switch (ble_evt->header.evt_id) { - case BLE_GATTS_EVT_HVN_TX_COMPLETE: - m_tx_in_progress -= ble_evt->evt.gatts_evt.params.hvn_tx_complete.count; - break; - - case BLE_GATTC_EVT_READ_RSP: - { - ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; - m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data); - // Flag to busy-wait loop that we've read the characteristic. - m_read_characteristic = NULL; - break; - } + case BLE_GATTS_EVT_HVN_TX_COMPLETE: + m_tx_in_progress -= ble_evt->evt.gatts_evt.params.hvn_tx_complete.count; + break; + + case BLE_GATTC_EVT_READ_RSP: + { + ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; + m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data); + // Flag to busy-wait loop that we've read the characteristic. + m_read_characteristic = NULL; + break; + } + + case BLE_GATTC_EVT_WRITE_RSP: + // Someone else can write now. + sd_mutex_release(m_write_mutex); + break; - case BLE_GATTC_EVT_WRITE_RSP: - // Someone else can write now. - sd_mutex_release(m_write_mutex); - break; + // For debugging. + default: + mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id); + break; } + } -void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self) { - ble_drv_add_event_handler(characteristic_on_ble_evt, NULL); +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props) { + self->service = NULL; + self->uuid = uuid; + self->value_data = NULL; + self->props = props; + self->handle = BLE_GATT_HANDLE_INVALID; + + ble_drv_add_event_handler(characteristic_on_ble_evt, self); + } void common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) { diff --git a/ports/nrf/common-hal/bleio/Characteristic.h b/ports/nrf/common-hal/bleio/Characteristic.h new file mode 100644 index 000000000..bce1eec1d --- /dev/null +++ b/ports/nrf/common-hal/bleio/Characteristic.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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_COMMON_HAL_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTIC_H + +#include "shared-module/bleio/Characteristic.h" +#include "shared-module/bleio/Service.h" +#include "common-hal/bleio/UUID.h" + +typedef struct { + mp_obj_base_t base; + bleio_service_obj_t *service; + bleio_uuid_obj_t *uuid; + mp_obj_t value_data; + uint16_t handle; + bleio_characteristic_properties_t props; + uint16_t user_desc_handle; + uint16_t cccd_handle; + uint16_t sccd_handle; +} bleio_characteristic_obj_t; + +#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTIC_H diff --git a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c new file mode 100644 index 000000000..4bd3e147f --- /dev/null +++ b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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 +#include + +#include "ble_drv.h" +#include "ble_gatts.h" +#include "nrf_soc.h" + +#include "py/runtime.h" + +#include "common-hal/bleio/__init__.h" +#include "common-hal/bleio/CharacteristicBuffer.h" + +STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) { + bleio_characteristic_buffer_obj_t *self = (bleio_characteristic_buffer_obj_t *) param; + switch (ble_evt->header.evt_id) { + case BLE_GATTS_EVT_WRITE: { + ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write; + // Event handle must match the handle for my characteristic. + if (evt_write->handle == self->characteristic->handle) { + // Push all the data onto the ring buffer. + for (size_t i = 0; i < evt_write->len; i++) { + ringbuf_put(&self->ringbuf, evt_write->data[i]); + } + break; + } + } + } + +} + +// Assumes that buffer_size has been validated before call. +void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size) { + + self->characteristic = characteristic; + // This is a macro. + ringbuf_alloc(&self->ringbuf, buffer_size); + + ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self); + +} + +// Returns a uint8_t byte value, or -1 if no data is available. +int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self) { + return ringbuf_get(&self->ringbuf); +} + +void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) { + ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, self); +} diff --git a/ports/nrf/common-hal/bleio/CharacteristicBuffer.h b/ports/nrf/common-hal/bleio/CharacteristicBuffer.h new file mode 100644 index 000000000..f9ff7dd66 --- /dev/null +++ b/ports/nrf/common-hal/bleio/CharacteristicBuffer.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H +#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H + +#include "py/ringbuf.h" + +#include "shared-bindings/bleio/Characteristic.h" + +typedef struct { + mp_obj_base_t base; + bleio_characteristic_obj_t *characteristic; + // Ring buffer storing consecutive incoming values. + ringbuf_t ringbuf; +} bleio_characteristic_buffer_obj_t; + +#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H diff --git a/ports/nrf/common-hal/bleio/Peripheral.c b/ports/nrf/common-hal/bleio/Peripheral.c index 7d5d6a6a6..0f2506de0 100644 --- a/ports/nrf/common-hal/bleio/Peripheral.c +++ b/ports/nrf/common-hal/bleio/Peripheral.c @@ -268,7 +268,7 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } default: - mp_printf(&mp_plat_print, "Unhandled event: 0x%04x\n", ble_evt->header.evt_id); + mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id); break; } } diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c index c913c02ae..26a1f1cff 100644 --- a/ports/nrf/common-hal/bleio/Service.c +++ b/ports/nrf/common-hal/bleio/Service.c @@ -28,6 +28,7 @@ #include "ble.h" #include "py/runtime.h" #include "common-hal/bleio/__init__.h" +#include "common-hal/bleio/Characteristic.h" #include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/Adapter.h" @@ -87,6 +88,10 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self) mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code); } + if (characteristic->handle != BLE_GATT_HANDLE_INVALID) { + mp_raise_ValueError(translate("Characteristic already in use by another Service.")); + } + characteristic->user_desc_handle = handles.user_desc_handle; characteristic->cccd_handle = handles.cccd_handle; characteristic->sccd_handle = handles.sccd_handle; diff --git a/shared-bindings/bleio/Characteristic.c b/shared-bindings/bleio/Characteristic.c index 1d414b6d3..0a3fada13 100644 --- a/shared-bindings/bleio/Characteristic.c +++ b/shared-bindings/bleio/Characteristic.c @@ -55,13 +55,13 @@ STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t mp_arg_check_num(n_args, n_kw, 1, 1, true); bleio_characteristic_obj_t *self = m_new_obj(bleio_characteristic_obj_t); self->base.type = &bleio_characteristic_type; - self->service = NULL; - self->value_data = NULL; mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); - enum { ARG_uuid, ARG_broadcast, ARG_indicate, ARG_notify, ARG_read, ARG_write, ARG_write_no_response }; + enum { + ARG_uuid, ARG_broadcast, ARG_indicate, ARG_notify, ARG_read, ARG_write, ARG_write_no_response, + }; static const mp_arg_t allowed_args[] = { { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_broadcast, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, @@ -83,14 +83,16 @@ STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t self->uuid = MP_OBJ_TO_PTR(uuid); - self->props.broadcast = args[ARG_broadcast].u_bool; - self->props.indicate = args[ARG_indicate].u_bool; - self->props.notify = args[ARG_notify].u_bool; - self->props.read = args[ARG_read].u_bool; - self->props.write = args[ARG_write].u_bool; - self->props.write_no_response = args[ARG_write_no_response].u_bool; + bleio_characteristic_properties_t properties; + + properties.broadcast = args[ARG_broadcast].u_bool; + properties.indicate = args[ARG_indicate].u_bool; + properties.notify = args[ARG_notify].u_bool; + properties.read = args[ARG_read].u_bool; + properties.write = args[ARG_write].u_bool; + properties.write_no_response = args[ARG_write_no_response].u_bool; - common_hal_bleio_characteristic_construct(self); + common_hal_bleio_characteristic_construct(self, uuid, properties); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/bleio/Characteristic.h b/shared-bindings/bleio/Characteristic.h index aec60ebbc..206cbcd40 100644 --- a/shared-bindings/bleio/Characteristic.h +++ b/shared-bindings/bleio/Characteristic.h @@ -27,11 +27,11 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H -#include "shared-module/bleio/Characteristic.h" +#include "common-hal/bleio/Characteristic.h" extern const mp_obj_type_t bleio_characteristic_type; -extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self); +extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props); extern void common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self); extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); diff --git a/shared-bindings/bleio/CharacteristicBuffer.c b/shared-bindings/bleio/CharacteristicBuffer.c new file mode 100644 index 000000000..770594d3a --- /dev/null +++ b/shared-bindings/bleio/CharacteristicBuffer.c @@ -0,0 +1,119 @@ +/* + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/bleio/CharacteristicBuffer.h" +#include "shared-bindings/bleio/UUID.h" + +//| .. currentmodule:: bleio +//| +//| :class:`CharacteristicBuffer` -- GATT Service incoming values buffer. +//| ========================================================= +//| +//| Accumulates a Characteristic's incoming values in a FIFO buffer. +//| +//| .. class:: CharacteristicBuffer(Characteristic, buffer_size=0) +//| +//| Create a new Characteristic object identified by the specified UUID. +//| +//| :param bleio.Characteristic characteristic: The characteristic to monitor +//| :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 *pos_args) { + mp_arg_check_num(n_args, n_kw, 1, 2, true); + bleio_characteristic_buffer_obj_t *self = m_new_obj(bleio_characteristic_buffer_obj_t); + self->base.type = &bleio_characteristic_buffer_type; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + + enum { ARG_characteristic, ARG_buffer_size, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer_size, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_t characteristic = args[ARG_characteristic].u_obj; + const int buffer_size = args[ARG_buffer_size].u_int; + + if (buffer_size < 1) { + mp_raise_ValueError(translate("buffer_size must be >= 1")); + } + + if (!MP_OBJ_IS_TYPE(characteristic, &bleio_characteristic_type)) { + mp_raise_ValueError(translate("Expected a Characteristic")); + } + + self->characteristic = MP_OBJ_TO_PTR(characteristic); + + common_hal_bleio_characteristic_buffer_construct(self, self->characteristic, buffer_size); + + return MP_OBJ_FROM_PTR(self); +} + + +//| .. method:: read() +//| +//| Read a single byte from the buffer. If no character is available, return None. +STATIC mp_obj_t bleio_characteristic_buffer_read(mp_obj_t self_in) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int byte = common_hal_bleio_characteristic_buffer_read(self); + if (byte == -1) { + return mp_const_none; + } + + return MP_OBJ_NEW_SMALL_INT(byte); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_read_obj, bleio_characteristic_buffer_read); + +//| .. method:: deinit() +//| +//| 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_read), MP_ROM_PTR(&bleio_characteristic_buffer_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bleio_characteristic_buffer_deinit_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_buffer_locals_dict, bleio_characteristic_buffer_locals_dict_table); + +const mp_obj_type_t bleio_characteristic_buffer_type = { + { &mp_type_type }, + .name = MP_QSTR_CharacteristicBuffer, + .make_new = bleio_characteristic_buffer_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_characteristic_buffer_locals_dict +}; diff --git a/shared-bindings/bleio/CharacteristicBuffer.h b/shared-bindings/bleio/CharacteristicBuffer.h new file mode 100644 index 000000000..c5d7580da --- /dev/null +++ b/shared-bindings/bleio/CharacteristicBuffer.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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_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; + +extern void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size); +// Returns a uint8_t byte value, or -1 if no data is available. +int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self); +int common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c index d30d59942..e23efe36c 100644 --- a/shared-bindings/bleio/__init__.c +++ b/shared-bindings/bleio/__init__.c @@ -31,6 +31,7 @@ #include "shared-bindings/bleio/AdvertisementData.h" #include "shared-bindings/bleio/Broadcaster.h" #include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/CharacteristicBuffer.h" #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/Peripheral.h" #include "shared-bindings/bleio/ScanEntry.h" @@ -77,6 +78,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) }, { MP_ROM_QSTR(MP_QSTR_Broadcaster), MP_ROM_PTR(&bleio_broadcaster_type) }, { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, + { MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), MP_ROM_PTR(&bleio_characteristic_buffer_type) }, { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&bleio_peripheral_type) }, { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h index 977ec8197..958837e64 100644 --- a/shared-module/bleio/Characteristic.h +++ b/shared-module/bleio/Characteristic.h @@ -27,26 +27,15 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H -#include "shared-module/bleio/Service.h" -#include "common-hal/bleio/UUID.h" - +// Flags for each characteristic property. Common across ports. typedef struct { - mp_obj_base_t base; - bleio_service_obj_t *service; - bleio_uuid_obj_t *uuid; - mp_obj_t value_data; - uint16_t handle; - struct { bool broadcast : 1; bool read : 1; bool write_no_response : 1; bool write : 1; bool notify : 1; bool indicate : 1; - } props; - uint16_t user_desc_handle; - uint16_t cccd_handle; - uint16_t sccd_handle; -} bleio_characteristic_obj_t; +} bleio_characteristic_properties_t; + #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H -- cgit v1.2.3