WIP: more Descriptor work; refactor gattc/gatts read/write

This commit is contained in:
Dan Halbert 2019-08-06 22:55:25 -04:00
parent 243334da75
commit d74c8b9425
15 changed files with 362 additions and 172 deletions

View File

@ -25,30 +25,23 @@
* THE SOFTWARE.
*/
#include <string.h>
#include <stdio.h>
#include "ble_drv.h"
#include "ble_gatts.h"
#include "nrf_soc.h"
#include "py/runtime.h"
#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/Service.h"
STATIC volatile bleio_characteristic_obj_t *m_read_characteristic;
static volatile bleio_characteristic_obj_t *m_read_characteristic;
STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) {
uint16_t cccd;
ble_gatts_value_t value = {
.p_value = (uint8_t*) &cccd,
.len = 2,
};
const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->cccd_handle, &value);
const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, cccd_handle, &value);
if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) {
@ -61,64 +54,39 @@ STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) {
return cccd;
}
STATIC void gatts_read(bleio_characteristic_obj_t *characteristic) {
// This might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because
// we can still read and write the local value.
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
STATIC void characteristic_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
switch (ble_evt->header.evt_id) {
mp_buffer_info_t bufinfo;
ble_gatts_value_t gatts_value = {
.p_value = NULL,
.len = 0,
};
// More events may be handled later, so keep this as a switch.
// Read once to find out what size buffer we need, then read again to fill buffer.
case BLE_GATTC_EVT_READ_RSP: {
ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
if (m_read_characteristic) {
m_read_characteristic->value = mp_obj_new_bytearray(response->len, response->data);
}
// Indicate to busy-wait loop that we've read the attribute value.
m_read_characteristic = NULL;
break;
}
uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value);
if (err_code == NRF_SUCCESS) {
characteristic->value_data = mp_obj_new_bytearray_of_zeros(gatts_value.len);
mp_get_buffer_raise(characteristic->value_data, &bufinfo, MP_BUFFER_WRITE);
gatts_value.p_value = bufinfo.buf;
// Read again, with the correct size of buffer.
err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value);
}
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code);
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) {
// This might be BLE_CONN_HANDLE_INVALID if we're not conected, but that's OK, because
// we can still read and write the local value.
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
ble_gatts_value_t gatts_value = {
.p_value = bufinfo->buf,
.len = bufinfo->len,
};
const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, characteristic->handle, &gatts_value);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code);
}
}
STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
uint16_t hvx_len = bufinfo->len;
ble_gatts_hvx_params_t hvx_params = {
.handle = characteristic->handle,
.handle = handle,
.type = hvx_type,
.offset = 0,
.p_len = &hvx_len,
.p_data = bufinfo->buf,
};
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
while (1) {
const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
if (err_code == NRF_SUCCESS) {
@ -134,21 +102,17 @@ STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp
// Some real error has occurred.
mp_raise_OSError_msg_varg(translate("Failed to notify or indicate attribute value, err 0x%04x"), err_code);
}
}
STATIC void check_connected(uint16_t conn_handle) {
if (conn_handle == BLE_CONN_HANDLE_INVALID) {
mp_raise_OSError_msg(translate("Not connected"));
}
}
STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) {
STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
check_connected(conn_handle);
common_hal_bleio_check_connected(conn_handle);
// Set to NULL in event loop after event.
m_read_characteristic = characteristic;
ble_drv_add_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic);
const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code);
@ -157,91 +121,54 @@ STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) {
while (m_read_characteristic != NULL) {
MICROPY_VM_HOOK_LOOP;
}
ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic);
}
STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
check_connected(conn_handle);
ble_gattc_write_params_t write_params = {
.write_op = (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE)
? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ,
.handle = characteristic->handle,
.p_value = bufinfo->buf,
.len = bufinfo->len,
};
while (1) {
uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params);
if (err_code == NRF_SUCCESS) {
break;
}
// Write with response will return NRF_ERROR_BUSY if the response has not been received.
// Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending.
if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) {
// We could wait for an event indicating the write is complete, but just retrying is easier.
MICROPY_VM_HOOK_LOOP;
continue;
}
// Some real error occurred.
mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code);
}
}
STATIC void characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) {
switch (ble_evt->header.evt_id) {
// More events may be handled later, so keep this as a switch.
case BLE_GATTC_EVT_READ_RSP: {
ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data);
// Indicate to busy-wait loop that we've read the characteristic.
m_read_characteristic = NULL;
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t security_mode, mp_obj_t descriptors) {
self->service = mp_const_none;
void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_obj_list_t *descriptor_list) {
self->service = MP_OBJ_NULL;
self->uuid = uuid;
self->value_data = mp_const_none;
self->props = props;
self->security_mode = security_mode;
self->descriptor_list = mp_obj_new_list_from_iter(descriptors);
self->value = mp_const_none;
self->handle = BLE_GATT_HANDLE_INVALID;
self->props = props;
self->read_perm = read_perm;
self->write_perm = write_perm;
self->descriptor_list = descriptor_list;
ble_drv_add_event_handler(characteristic_on_ble_evt, self);
for (size_t descriptor_idx = 0; descriptor_idx < descriptor_list->len; ++descriptor_idx) {
bleio_descriptor_obj_t *descriptor =
MP_OBJ_TO_PTR(descriptor_list->items[descriptor_idx]);
descriptor->characteristic = self;
}
}
mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) {
return self->descriptor_list;
}
bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) {
return self->service;
}
mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) {
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
if (common_hal_bleio_service_get_is_remote(self->service)) {
gattc_read(self);
// self->value is set by evt handler.
characteristic_gattc_read(self);
} else {
gatts_read(self);
self->value = common_hal_bleio_gatts_read(self->handle, conn_handle);
}
return self->value_data;
return self->value;
}
void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
if (common_hal_bleio_service_get_is_remote(self->service)) {
gattc_write(self, bufinfo);
// Last argument is true if write-no-reponse desired.
common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo,
(self->props & CHAR_PROP_WRITE_NO_RESPONSE));
} else {
bool sent = false;
uint16_t cccd = 0;
@ -249,26 +176,25 @@ void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self,
const bool notify = self->props & CHAR_PROP_NOTIFY;
const bool indicate = self->props & CHAR_PROP_INDICATE;
if (notify | indicate) {
cccd = get_cccd(self);
cccd = characteristic_get_cccd(self->cccd_handle, conn_handle);
}
// It's possible that both notify and indicate are set.
if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_NOTIFICATION);
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION);
sent = true;
}
if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_INDICATION);
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION);
sent = true;
}
if (!sent) {
gatts_write(self, bufinfo);
common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo);
}
}
}
bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) {
return self->uuid;
}
@ -286,14 +212,13 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self,
mp_raise_ValueError(translate("Can't set CCCD on local Characteristic"));
}
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
common_hal_bleio_check_connected(conn_handle);
uint16_t cccd_value =
(notify ? BLE_GATT_HVX_NOTIFICATION : 0) |
(indicate ? BLE_GATT_HVX_INDICATION : 0);
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
check_connected(conn_handle);
ble_gattc_write_params_t write_params = {
.write_op = BLE_GATT_OP_WRITE_REQ,
.handle = self->cccd_handle,

View File

@ -35,12 +35,14 @@
typedef struct {
mp_obj_base_t base;
// Will be MP_OBJ_NULL before being assigned to a Service.
bleio_service_obj_t *service;
bleio_uuid_obj_t *uuid;
mp_obj_t value_data;
mp_obj_t value;
uint16_t handle;
bleio_characteristic_properties_t props;
bleio_attribute_security_mode_t security_mode;
bleio_attribute_security_mode_t read_perm;
bleio_attribute_security_mode_t write_perm;
mp_obj_list_t *descriptor_list;
uint16_t user_desc_handle;
uint16_t cccd_handle;

View File

@ -26,13 +26,93 @@
* THE SOFTWARE.
*/
#include "common-hal/bleio/Descriptor.h"
#include "py/runtime.h"
#include "shared-bindings/bleio/__init__.h"
#include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/Service.h"
#include "shared-bindings/bleio/UUID.h"
void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid) {
static volatile bleio_descriptor_obj_t *m_read_descriptor;
void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm) {
self->characteristic = MP_OBJ_NULL;
self->uuid = uuid;
self->value = mp_const_none;
self->handle = BLE_CONN_HANDLE_INVALID;
self->read_perm = read_perm;
self->write_perm = write_perm;
}
bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) {
return self->uuid;
}
mp_obj_t common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) {
return self->characteristic;
}
STATIC void descriptor_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
switch (ble_evt->header.evt_id) {
// More events may be handled later, so keep this as a switch.
case BLE_GATTC_EVT_READ_RSP: {
ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
if (m_read_descriptor) {
m_read_descriptor->value = mp_obj_new_bytearray(response->len, response->data);
}
// Indicate to busy-wait loop that we've read the attribute value.
m_read_descriptor = NULL;
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled descriptor event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
STATIC void descriptor_gattc_read(bleio_descriptor_obj_t *descriptor) {
const uint16_t conn_handle =
common_hal_bleio_device_get_conn_handle(descriptor->characteristic->service->device);
common_hal_bleio_check_connected(conn_handle);
// Set to NULL in event loop after event.
m_read_descriptor = descriptor;
ble_drv_add_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor);
const uint32_t err_code = sd_ble_gattc_read(conn_handle, descriptor->handle, 0);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code);
}
while (m_read_descriptor != NULL) {
MICROPY_VM_HOOK_LOOP;
}
ble_drv_remove_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor);
}
mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self) {
if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
descriptor_gattc_read(self);
} else {
self->value = common_hal_bleio_gatts_read(
self->handle, common_hal_bleio_device_get_conn_handle(self->characteristic->service->device));
}
return self->value;
}
void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) {
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->characteristic->service->device);
if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
// false means WRITE_REQ, not write-no-response
common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false);
} else {
common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo);
}
}

View File

@ -36,11 +36,13 @@
typedef struct {
mp_obj_base_t base;
// Will be MP_OBJ_NULL before being assigned to a Characteristic.
bleio_characteristic_obj_t *characteristic;
bleio_uuid_obj_t *uuid;
mp_obj_t value_data;
mp_obj_t value;
uint16_t handle;
bleio_attribute_security_mode_t security_mode;
bleio_attribute_security_mode_t read_perm;
bleio_attribute_security_mode_t write_perm;
} bleio_descriptor_obj_t;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H

View File

@ -47,7 +47,6 @@ void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_ob
MP_OBJ_TO_PTR(characteristic_list->items[characteristic_idx]);
characteristic->service = self;
}
}
bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) {
@ -146,7 +145,7 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self)
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_attr_md.write_perm);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(descriptor->value_data, &bufinfo, MP_BUFFER_READ);
mp_get_buffer_raise(descriptor->value, &bufinfo, MP_BUFFER_READ);
ble_gatts_attr_t desc_attr = {
.p_uuid = &desc_uuid,

View File

@ -59,13 +59,19 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = {
},
};
void common_hal_bleio_check_connected(uint16_t conn_handle) {
if (conn_handle == BLE_CONN_HANDLE_INVALID) {
mp_raise_OSError_msg(translate("Not connected"));
}
}
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;
} 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;
return BLE_CONN_HANDLE_INVALID;
}
}
@ -213,7 +219,7 @@ STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, mp_ob
(gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0);
// Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler.
common_hal_bleio_characteristic_construct(characteristic, uuid, props, SEC_MODE_OPEN, mp_const_empty_tuple);
common_hal_bleio_characteristic_construct(characteristic, uuid, props, SEC_MODE_OPEN, SEC_MODE_OPEN, mp_const_empty_tuple);
characteristic->handle = gattc_char->handle_value;
characteristic->service = m_char_discovery_service;
@ -267,8 +273,7 @@ STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, mp_ob
// For now, just leave the UUID as NULL.
}
// TODO: can we find out security mode?
common_hal_bleio_descriptor_construct(descriptor, uuid, SEC_MODE_OPEN);
common_hal_bleio_descriptor_construct(descriptor, uuid, SEC_MODE_OPEN, SEC_MODE_OPEN);
descriptor->handle = gattc_desc->handle;
descriptor->characteristic = m_desc_discovery_characteristic;
@ -418,3 +423,79 @@ void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t
ble_drv_remove_event_handler(discovery_on_ble_evt, device);
}
// GATTS read of a Characteristic or Descriptor.
mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle) {
// conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because
// we can still read and write the local value.
mp_buffer_info_t bufinfo;
ble_gatts_value_t gatts_value = {
.p_value = NULL,
.len = 0,
};
// Read once to find out what size buffer we need, then read again to fill buffer.
mp_obj_t value = mp_const_none;
uint32_t err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value);
if (err_code == NRF_SUCCESS) {
value = mp_obj_new_bytearray_of_zeros(gatts_value.len);
mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_WRITE);
gatts_value.p_value = bufinfo.buf;
// Read again, with the correct size of buffer.
err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value);
}
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code);
}
return value;
}
void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) {
// conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because
// we can still read and write the local value.
ble_gatts_value_t gatts_value = {
.p_value = bufinfo->buf,
.len = bufinfo->len,
};
const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code);
}
}
void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) {
common_hal_bleio_check_connected(conn_handle);
ble_gattc_write_params_t write_params = {
.write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ,
.handle = handle,
.p_value = bufinfo->buf,
.len = bufinfo->len,
};
while (1) {
uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params);
if (err_code == NRF_SUCCESS) {
break;
}
// Write with response will return NRF_ERROR_BUSY if the response has not been received.
// Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending.
if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) {
// We could wait for an event indicating the write is complete, but just retrying is easier.
MICROPY_VM_HOOK_LOOP;
continue;
}
// Some real error occurred.
mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code);
}
}

View File

@ -83,7 +83,21 @@ STATIC mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args,
//| .. attribute:: address_bytes
//|
//| The bytes that make up the device address (read-only)
//| The bytes that make up the device address (read-only).
//|
//| Note that the ``bytes`` object returned is in little-endian order:
//| The least significant byte is ``address_bytes[0]``. So the address will
//| appear to be reversed if you print the raw ``bytes`` object. If you print
//| or use `str()` on the :py:class:`~bleio.Attribute` object itself, the address will be printed
//| in the expected order. For example:
//|
//| .. code-block:: pycon
//|
//| >>> import bleio
//| >>> bleio.adapter.address
//| <Address c8:1d:f5:ed:a8:35>
//| >>> bleio.adapter.address.address_bytes
//| b'5\xa8\xed\xf5\x1d\xc8'
//|
STATIC mp_obj_t bleio_address_get_address_bytes(mp_obj_t self_in) {
bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in);

View File

@ -37,12 +37,12 @@
//| =========================================================
//|
//| Definitions associated with all BLE attributes: characteristics, descriptors, etc.
//| `Attribute` is, notionally, a superclass of `Characteristic` and `Descriptor`,
//| :py:class:`~bleio.Attribute` is, notionally, a superclass of `Characteristic` and `Descriptor`,
//| but is not defined as a Python superclass of those classes.
//|
//| .. class:: Attribute()
//|
//| You cannot create an instance of `Attribute`.
//| You cannot create an instance of :py:class:`~bleio.Attribute`.
//|
STATIC const mp_rom_map_elem_t bleio_attribute_locals_dict_table[] = {

View File

@ -30,6 +30,7 @@
#include "py/runtime.h"
#include "shared-bindings/bleio/Attribute.h"
#include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/UUID.h"
//| .. currentmodule:: bleio
@ -41,24 +42,28 @@
//| and writing of the characteristic's value.
//|
//|
//| .. class:: Characteristic(uuid, *, properties=0, security_mode=`Attribute.OPEN`, descriptors=None)
//| .. class:: Characteristic(uuid, *, properties=0, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, descriptors=None)
//|
//| Create a new Characteristic object identified by the specified UUID.
//|
//| :param bleio.UUID uuid: The uuid of the characteristic
//| :param int properties: bitmask of these values bitwise-or'd together: `BROADCAST`, `INDICATE`,
//| `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`
//| :param int security_mode: one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`,
//| :param int read_perm: Specifies whether the characteristic can be read by a client, and if so, which
//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`,
//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`,
//| `Attribute.SIGNED_NO_MITM`, `Attribute.SIGNED_WITH_MITM`.
//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`.
//| :param int write_perm: Specifies whether the characteristic can be written by a client, and if so, which
//| security mode is required. Values allowed are the same as `read_perm`.
//| :param iterable descriptors: BLE descriptors for this characteristic.
//|
STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_uuid, ARG_properties, ARG_security_mode, ARG_descriptors };
enum { ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, ARG_descriptors };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_properties, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 0 } },
{ MP_QSTR_security_mode, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SEC_MODE_OPEN } },
{ MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SEC_MODE_OPEN } },
{ MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SEC_MODE_OPEN } },
{ MP_QSTR_descriptors, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
@ -77,8 +82,11 @@ STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t
mp_raise_ValueError(translate("Invalid properties"));
}
const bleio_attribute_security_mode_t security_mode = args[ARG_security_mode].u_int;
common_hal_bleio_attribute_security_mode_check_valid(security_mode);
const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(read_perm);
const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(write_perm);
mp_obj_t descriptors = args[ARG_descriptors].u_obj;
if (descriptors == mp_const_none) {
@ -88,7 +96,27 @@ STATIC mp_obj_t bleio_characteristic_make_new(const mp_obj_type_t *type, size_t
bleio_characteristic_obj_t *self = m_new_obj(bleio_characteristic_obj_t);
self->base.type = &bleio_characteristic_type;
common_hal_bleio_characteristic_construct(self, uuid, properties, security_mode, descriptors);
// If descriptors is not an iterable, an exception will be thrown.
mp_obj_iter_buf_t iter_buf;
mp_obj_t iterable = mp_getiter(args[ARG_descriptors].u_obj, &iter_buf);
// Copy the descriptors list and validate its items.
mp_obj_t desc_list_obj = mp_obj_new_list(0, NULL);
mp_obj_list_t *desc_list = MP_OBJ_TO_PTR(desc_list_obj);
mp_obj_t descriptor_obj;
while ((descriptor_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if (!MP_OBJ_IS_TYPE(descriptor_obj, &bleio_descriptor_type)) {
mp_raise_ValueError(translate("descriptors includes an object that is not a Descriptors"));
}
bleio_descriptor_obj_t *descriptor = MP_OBJ_TO_PTR(descriptor_obj);
if (common_hal_bleio_descriptor_get_characteristic(descriptor) != mp_const_none) {
mp_raise_ValueError(translate("Descriptor is already attached to a Characteristic"));
}
mp_obj_list_append(desc_list_obj, descriptor_obj);
}
common_hal_bleio_characteristic_construct(self, uuid, properties, read_perm, write_perm, desc_list);
return MP_OBJ_FROM_PTR(self);
}
@ -182,6 +210,24 @@ const mp_obj_property_t bleio_characteristic_descriptors_obj = {
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: service (read-only)
//|
//| The Service this Characteristic is a part of. None if not yet assigned to a Service.
//|
STATIC mp_obj_t bleio_characteristic_get_service(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_characteristic_get_service(self);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_service_obj, bleio_characteristic_get_service);
const mp_obj_property_t bleio_characteristic_service_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_characteristic_get_service_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. method:: set_cccd(*, notify=False, indicate=False)
//|
//| Set the remote characteristic's CCCD to enable or disable notification and indication.

View File

@ -34,12 +34,13 @@
extern const mp_obj_type_t bleio_characteristic_type;
extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t security_mode, mp_obj_t descriptors);
extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_obj_list_t *descriptor_list);
extern mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self);
extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo);
extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self);
extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self);
extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self);
extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H

View File

@ -42,15 +42,24 @@
//| information about the characteristic.
//|
//| .. class:: Descriptor(uuid, security_mode=`Attribute.OPEN`)
//| .. class:: Descriptor(uuid, *, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`)
//|
//| Create a new descriptor object with the UUID uuid
//|
//| :param bleio.UUID uuid: The uuid of the descriptor
//| :param int read_perm: Specifies whether the descriptor can be read by a client, and if so, which
//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`,
//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`,
//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`.
//| :param int write_perm: Specifies whether the descriptor can be written by a client, and if so, which
//| security mode is required. Values allowed are the same as `read_perm`.
//|
//| Create a new descriptor object with the UUID uuid and the given value, if any.
STATIC mp_obj_t bleio_descriptor_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_uuid, ARG_security_mode };
enum { ARG_uuid, ARG_read_perm, ARG_write_perm };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_security_mode, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SEC_MODE_OPEN } },
{ MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SEC_MODE_OPEN } },
{ MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SEC_MODE_OPEN } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -62,14 +71,17 @@ STATIC mp_obj_t bleio_descriptor_make_new(const mp_obj_type_t *type, size_t n_ar
mp_raise_ValueError(translate("Expected a UUID"));
}
const bleio_attribute_security_mode_t security_mode = args[ARG_security_mode].u_int;
common_hal_bleio_attribute_security_mode_check_valid(security_mode);
const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(read_perm);
const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int;
common_hal_bleio_attribute_security_mode_check_valid(write_perm);
bleio_descriptor_obj_t *self = m_new_obj(bleio_descriptor_obj_t);
self->base.type = type;
bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_arg);
common_hal_bleio_descriptor_construct(self, uuid, security_mode);
common_hal_bleio_descriptor_construct(self, uuid, read_perm, write_perm);
return MP_OBJ_FROM_PTR(self);
}
@ -93,6 +105,24 @@ const mp_obj_property_t bleio_descriptor_uuid_obj = {
(mp_obj_t)&mp_const_none_obj},
};
//| .. attribute:: characteristic (read-only)
//|
//| The Characteristic this Descriptor is a part of. None if not yet assigned to a Characteristic.
//|
STATIC mp_obj_t bleio_descriptor_get_characteristic(mp_obj_t self_in) {
bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_descriptor_get_characteristic(self);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_characteristic_obj, bleio_descriptor_get_characteristic);
const mp_obj_property_t bleio_descriptor_characteristic_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_descriptor_get_characteristic_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = {
// Properties
{ MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_descriptor_uuid_obj) },

View File

@ -34,8 +34,8 @@
extern const mp_obj_type_t bleio_descriptor_type;
extern mp_int_t common_hal_bleio_descriptor_get_handle(bleio_descriptor_obj_t *self);
extern mp_obj_t common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self);
extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t security_mode);
extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm);
extern bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self);
extern mp_obj_t common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H

View File

@ -96,6 +96,9 @@ STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args,
// The descriptor base UUID doesn't match the characteristic base UUID.
mp_raise_ValueError(translate("Characteristic UUID doesn't match Service UUID"));
}
if (common_hal_bleio_characteristic_get_service(characteristic) != MP_OBJ_NULL) {
mp_raise_ValueError(translate("Characteristic is already attached to a Service"));
}
mp_obj_list_append(char_list_obj, characteristic_obj);
}

View File

@ -59,6 +59,7 @@
//|
//| Address
//| Adapter
//| Attribute
//| Central
//| Characteristic
//| CharacteristicBuffer

View File

@ -38,9 +38,15 @@
extern const super_adapter_obj_t common_hal_bleio_adapter_obj;
extern void common_hal_bleio_check_connected(uint16_t conn_handle);
extern uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device);
extern mp_obj_list_t *common_hal_bleio_device_get_remote_services_list(mp_obj_t device);
extern void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist);
extern mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle);
extern void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo);
extern void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H