From 1d9e489af3d00d737917b09c1f0d007705e970e9 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 23 Jul 2021 15:12:26 +1000 Subject: [PATCH] extmod/modbluetooth: Add send_update arg to gatts_write. This allows the write to trigger a notification or indication, but only to subscribed clients. This is different to gatts_notify/gatts_indicate, which will unconditionally notify/indicate. Signed-off-by: Jim Mussared --- docs/library/bluetooth.rst | 19 +++++++++++++------ extmod/btstack/modbluetooth_btstack.c | 5 ++++- extmod/modbluetooth.c | 14 ++++++++------ extmod/modbluetooth.h | 4 ++-- extmod/nimble/modbluetooth_nimble.c | 13 +++++++++++-- ports/zephyr/modbluetooth_zephyr.c | 5 ++++- 6 files changed, 42 insertions(+), 18 deletions(-) diff --git a/docs/library/bluetooth.rst b/docs/library/bluetooth.rst index f110bfb195..7ab4d6e88c 100644 --- a/docs/library/bluetooth.rst +++ b/docs/library/bluetooth.rst @@ -485,10 +485,14 @@ writes from a client to a given characteristic, use Reads the local value for this handle (which has either been written by :meth:`gatts_write ` or by a remote client). -.. method:: BLE.gatts_write(value_handle, data, /) +.. method:: BLE.gatts_write(value_handle, data, send_update=False, /) Writes the local value for this handle, which can be read by a client. + If *send_update* is ``True``, then any subscribed clients will be notified + (or indicated, depending on what they're subscribed to and which operations + the characteristic supports) about this write. + .. method:: BLE.gatts_notify(conn_handle, value_handle, data=None, /) Sends a notification request to a connected client. @@ -499,17 +503,20 @@ writes from a client to a given characteristic, use Otherwise, if *data* is ``None``, then the current local value (as set with :meth:`gatts_write `) will be sent. + **Note:** The notification will be sent regardless of the subscription + status of the client to this characteristic. + .. method:: BLE.gatts_indicate(conn_handle, value_handle, /) - Sends an indication request to a connected client. - - **Note:** This does not currently support sending a custom value, it will - always send the current local value (as set with :meth:`gatts_write - `). + Sends an indication request containing the characteristic's current value to + a connected client. On acknowledgment (or failure, e.g. timeout), the ``_IRQ_GATTS_INDICATE_DONE`` event will be raised. + **Note:** The indication will be sent regardless of the subscription + status of the client to this characteristic. + .. method:: BLE.gatts_set_buffer(value_handle, len, append=False, /) Sets the internal buffer size for a value in bytes. This will limit the diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 9c88878038..4e81e21fe2 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -1085,11 +1085,14 @@ int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *valu return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } -int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { +int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update) { DEBUG_printf("mp_bluetooth_gatts_write\n"); if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } + if (send_update) { + return MP_EOPNOTSUPP; + } return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index bf8d071a94..cb153f70e9 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -717,15 +717,17 @@ STATIC mp_obj_t bluetooth_ble_gatts_read(mp_obj_t self_in, mp_obj_t value_handle } STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gatts_read_obj, bluetooth_ble_gatts_read); -STATIC mp_obj_t bluetooth_ble_gatts_write(mp_obj_t self_in, mp_obj_t value_handle_in, mp_obj_t data) { - (void)self_in; +STATIC mp_obj_t bluetooth_ble_gatts_write(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo = {0}; - mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); - int err = mp_bluetooth_gatts_write(mp_obj_get_int(value_handle_in), bufinfo.buf, bufinfo.len); - bluetooth_handle_errno(err); + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + bool send_update = false; + if (n_args > 3) { + send_update = mp_obj_is_true(args[3]); + } + bluetooth_handle_errno(mp_bluetooth_gatts_write(mp_obj_get_int(args[1]), bufinfo.buf, bufinfo.len, send_update)); return MP_OBJ_NEW_SMALL_INT(bufinfo.len); } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_gatts_write_obj, bluetooth_ble_gatts_write); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_write_obj, 3, 4, bluetooth_ble_gatts_write); STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) { mp_int_t conn_handle = mp_obj_get_int(args[1]); diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index d126ad6c11..43519e5941 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -334,8 +334,8 @@ int mp_bluetooth_gatts_register_service_end(void); // Read the value from the local gatts db (likely this has been written by a central). int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len); -// Write a value to the local gatts db (ready to be queried by a central). -int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len); +// Write a value to the local gatts db (ready to be queried by a central). Optionally send notifications/indications. +int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update); // Notify the central that it should do a read. int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle); // Notify the central, including a data payload. (Note: does not set the gatts db value). diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index d7ad7e17c9..e4b4cb68af 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -512,6 +512,11 @@ STATIC int central_gap_event_cb(struct ble_gap_event *event, void *arg) { return 0; } + + case BLE_GAP_EVENT_SUBSCRIBE: { + DEBUG_printf("central_gap_event_cb: subscribe: handle=%d, reason=%d notify=%d indicate=%d \n", event->subscribe.attr_handle, event->subscribe.reason, event->subscribe.cur_notify, event->subscribe.cur_indicate); + return 0; + } } return commmon_gap_event_cb(event, arg); @@ -1004,11 +1009,15 @@ int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *valu return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, value, value_len); } -int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { +int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, value, value_len); + int err = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, value, value_len); + if (err == 0 && send_update) { + ble_gatts_chr_updated(value_handle); + } + return err; } // TODO: Could use ble_gatts_chr_updated to send to all subscribed centrals. diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c index 6b3a10d471..5753d71476 100644 --- a/ports/zephyr/modbluetooth_zephyr.c +++ b/ports/zephyr/modbluetooth_zephyr.c @@ -308,10 +308,13 @@ int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *valu return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); } -int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { +int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } + if (send_update) { + return MP_EOPNOTSUPP; + } return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); }