aboutsummaryrefslogtreecommitdiffstats
path: root/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp')
-rwxr-xr-xarduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp747
1 files changed, 747 insertions, 0 deletions
diff --git a/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp b/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp
new file mode 100755
index 0000000..6474e39
--- /dev/null
+++ b/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp
@@ -0,0 +1,747 @@
+/**************************************************************************/
+/*!
+ @file BLECharacteristic.cpp
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holders nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+#include "bluefruit.h"
+
+void BLECharacteristic::_init(void)
+{
+ _is_temp = false;
+ _max_len = BLE_GATT_ATT_MTU_DEFAULT-3;
+ _service = NULL;
+
+ _usr_descriptor = NULL;
+ varclr(&_report_ref_desc);
+ varclr(&_format_desc);
+
+ varclr(&_properties);
+
+ varclr(&_attr_meta);
+ _attr_meta.read_perm = _attr_meta.write_perm = BLE_SECMODE_OPEN;
+ _attr_meta.vlen = 1;
+ _attr_meta.vloc = BLE_GATTS_VLOC_STACK;
+
+ _handles.value_handle = BLE_GATT_HANDLE_INVALID;
+ _handles.user_desc_handle = BLE_GATT_HANDLE_INVALID;
+ _handles.sccd_handle = BLE_GATT_HANDLE_INVALID;
+ _handles.cccd_handle = BLE_GATT_HANDLE_INVALID;
+
+ _long_wr.buffer = NULL;
+ _long_wr.bufsize = 0;
+ _long_wr.count = 0;
+
+ _rd_authorize_cb = NULL;
+ _wr_authorize_cb = NULL;
+ _wr_cb = NULL;
+ _cccd_wr_cb = NULL;
+}
+
+BLECharacteristic::BLECharacteristic(void)
+ : uuid()
+{
+ _init();
+}
+
+BLECharacteristic::BLECharacteristic(BLEUuid bleuuid)
+ : uuid(bleuuid)
+{
+ _init();
+}
+
+void BLECharacteristic::setUuid(BLEUuid bleuuid)
+{
+ uuid = bleuuid;
+}
+
+BLEService& BLECharacteristic::parentService (void)
+{
+ return *_service;
+}
+
+/**
+ * Destructor
+ */
+BLECharacteristic::~BLECharacteristic()
+{
+// Bluefruit.Gatt._removeCharacteristic(this);
+}
+
+/**
+ * Must be set when Charactertistic is declared locally (e.g insdie function)
+ * and is not last throughout programs. Useful for one-shot set-and-forget
+ * Characteristics such as read-only one. Where there is no need for interactions
+ * later on. This call will prevent the Characteristics to be hooked into
+ * managing chars list of AdafruitBluefruit
+ */
+void BLECharacteristic::setTempMemory(void)
+{
+ _is_temp = true;
+}
+
+void BLECharacteristic::setProperties(uint8_t prop)
+{
+ memcpy(&_properties, &prop, 1);
+}
+
+void BLECharacteristic::setMaxLen(uint16_t max_len)
+{
+ _max_len = max_len;
+}
+
+void BLECharacteristic::setFixedLen(uint16_t fixed_len)
+{
+ if ( fixed_len )
+ {
+ _max_len = fixed_len;
+ _attr_meta.vlen = 0;
+ }else
+ {
+ _attr_meta.vlen = 1;
+ }
+}
+
+void BLECharacteristic::setPermission(BleSecurityMode read_perm, BleSecurityMode write_perm)
+{
+ memcpy(&_attr_meta.read_perm , &read_perm, 1);
+ memcpy(&_attr_meta.write_perm, &write_perm, 1);
+}
+
+void BLECharacteristic::setWriteCallback(write_cb_t fp)
+{
+ _wr_cb = fp;
+}
+
+void BLECharacteristic::setCccdWriteCallback(write_cccd_cb_t fp)
+{
+ _cccd_wr_cb = fp;
+}
+
+void BLECharacteristic::setReadAuthorizeCallback(read_authorize_cb_t fp)
+{
+ _attr_meta.rd_auth = (fp ? 1 : 0);
+ _rd_authorize_cb = fp;
+}
+
+void BLECharacteristic::setWriteAuthorizeCallback(write_authorize_cb_t fp)
+{
+ _attr_meta.wr_auth = (fp ? 1 : 0);
+ _wr_authorize_cb = fp;
+}
+
+void BLECharacteristic::setUserDescriptor(const char* descriptor)
+{
+ _usr_descriptor = descriptor;
+}
+
+void BLECharacteristic::setReportRefDescriptor(uint8_t id, uint8_t type)
+{
+ _report_ref_desc.id = id;
+ _report_ref_desc.type = type;
+}
+
+/**
+ * https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
+ * @param type BLE_GATT_CPF_FORMAT_x value, see ble_gatt.h
+ * @param exponent exponent
+ * @param unit UUID16_UNIT_x, see BLEUuid.h
+ */
+void BLECharacteristic::setPresentationFormatDescriptor(uint8_t type, int8_t exponent, uint16_t unit, uint8_t name_space, uint16_t descritpor)
+{
+ _format_desc.format = type;
+ _format_desc.exponent = exponent;
+ _format_desc.unit = unit;
+ _format_desc.name_space = name_space;
+ _format_desc.desc = descritpor;
+}
+
+ble_gatts_char_handles_t BLECharacteristic::handles(void)
+{
+ return _handles;
+}
+
+err_t BLECharacteristic::begin(void)
+{
+ _service = BLEService::lastService;
+
+ // Add UUID128 if needed
+ (void) uuid.begin();
+
+ // Permission is OPEN if passkey is disabled.
+// if (!nvm_data.core.passkey_enable) BLE_GAP_CONN_SEC_MODE_SET_OPEN(&p_char_def->permission);
+
+ // Correct Read/Write permission according to properties
+ if ( !(_properties.read || _properties.notify || _properties.indicate ) )
+ {
+ _attr_meta.read_perm = BLE_SECMODE_NO_ACCESS;
+ }
+
+ if ( !(_properties.write || _properties.write_wo_resp ) )
+ {
+ _attr_meta.write_perm = BLE_SECMODE_NO_ACCESS;
+ }
+
+ /* CCCD attribute metadata */
+ ble_gatts_attr_md_t cccd_md;
+
+ if ( _properties.notify || _properties.indicate )
+ {
+ /* Notification & Indication require CCCD */
+ memclr( &cccd_md, sizeof(ble_gatts_attr_md_t) );
+ cccd_md.vloc = BLE_GATTS_VLOC_STACK;
+
+ cccd_md.read_perm = BLE_SECMODE_OPEN;
+ cccd_md.write_perm = _attr_meta.read_perm;
+ }
+
+ /* Characteristic metadata */
+ ble_gatts_char_md_t char_md;
+ varclr(&char_md);
+
+ char_md.char_props = _properties;
+ char_md.p_cccd_md = (_properties.notify || _properties.indicate) ? &cccd_md : NULL;
+
+ /* Characteristic extended properties (for user description) */
+ ble_gatts_attr_md_t desc_md =
+ {
+ .read_perm = _attr_meta.read_perm,
+ .write_perm = BLE_SECMODE_NO_ACCESS,
+ .vlen = 0,
+ .vloc = BLE_GATTS_VLOC_STACK,
+ };
+
+ if (_usr_descriptor != NULL && _usr_descriptor[0] != 0)
+ {
+ char_md.p_char_user_desc = (uint8_t*) _usr_descriptor;
+ char_md.char_user_desc_size = char_md.char_user_desc_max_size = strlen(_usr_descriptor);
+ char_md.p_user_desc_md = &desc_md;
+ //char_md.char_ext_props = ext_props,
+ }
+
+ /* Presentation Format Descriptor */
+ if ( _format_desc.format != 0 )
+ {
+ char_md.p_char_pf = &_format_desc;
+ }
+
+ /* GATT attribute declaration */
+ ble_gatts_attr_t attr_char_value =
+ {
+ .p_uuid = &uuid._uuid,
+ .p_attr_md = &_attr_meta,
+ .init_len = (_attr_meta.vlen == 1) ? (uint16_t) 0 : _max_len,
+ .init_offs = 0,
+ .max_len = _max_len,
+ .p_value = NULL
+ };
+
+ VERIFY_STATUS( sd_ble_gatts_characteristic_add(BLE_GATT_HANDLE_INVALID, &char_md, &attr_char_value, &_handles) );
+
+ // Report Reference Descriptor if any (required by HID)
+ if ( _report_ref_desc.type )
+ {
+ // Reference Descriptor
+ ble_gatts_attr_md_t ref_md =
+ {
+ .read_perm = _attr_meta.read_perm,
+ .write_perm = BLE_SECMODE_NO_ACCESS,
+ .vlen = 0,
+ .vloc = BLE_GATTS_VLOC_STACK
+ };
+
+ ble_uuid_t ref_uuid = { .uuid = UUID16_REPORT_REF_DESCR, .type = BLE_UUID_TYPE_BLE };
+ ble_gatts_attr_t ref_desc =
+ {
+ .p_uuid = &ref_uuid,
+ .p_attr_md = &ref_md,
+ .init_len = sizeof(_report_ref_desc),
+ .init_offs = 0,
+ .max_len = sizeof(_report_ref_desc),
+ .p_value = (uint8_t*) &_report_ref_desc
+ };
+
+ uint16_t ref_hdl;
+ VERIFY_STATUS ( sd_ble_gatts_descriptor_add(BLE_GATT_HANDLE_INVALID, &ref_desc, &ref_hdl) );
+
+ (void) ref_hdl; // not used
+ }
+
+ // Currently Only register to Bluefruit if The Characteristic is not temporary memory i.e local variable
+ if ( !_is_temp )
+ {
+ (void) Bluefruit.Gatt._addCharacteristic(this);
+ }
+
+ return ERROR_NONE;
+}
+
+err_t BLECharacteristic::addDescriptor(BLEUuid bleuuid, void const * content, uint16_t len, BleSecurityMode read_perm, BleSecurityMode write_perm)
+{
+ // Meta Data
+ ble_gatts_attr_md_t meta;
+ varclr(&meta);
+
+ memcpy(&meta.read_perm , &read_perm , 1);
+ memcpy(&meta.write_perm, &write_perm, 1);
+ meta.vlen = 0;
+ meta.vloc = BLE_GATTS_VLOC_STACK;
+
+ // Descriptor
+ (void) bleuuid.begin();
+
+ ble_gatts_attr_t desc =
+ {
+ .p_uuid = &bleuuid._uuid,
+ .p_attr_md = &meta,
+ .init_len = len,
+ .init_offs = 0,
+ .max_len = len,
+ .p_value = (uint8_t*) content
+ };
+
+ uint16_t hdl;
+ VERIFY_STATUS ( sd_ble_gatts_descriptor_add(BLE_GATT_HANDLE_INVALID, &desc, &hdl) );
+
+ return ERROR_NONE;
+}
+
+/**
+ * @param event
+ */
+void BLECharacteristic::_eventHandler(ble_evt_t* event)
+{
+ uint16_t const conn_hdl = event->evt.common_evt.conn_handle;
+
+ switch(event->header.evt_id)
+ {
+ case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
+ {
+ ble_gatts_evt_rw_authorize_request_t * request = &event->evt.gatts_evt.params.authorize_request;
+
+ if (request->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
+ {
+ ble_gatts_evt_write_t * wr_req = &request->request.write;
+
+ switch(wr_req->op)
+ {
+ case BLE_GATTS_OP_PREP_WRITE_REQ:
+ {
+ // Prepare Long Write
+ if ( !_long_wr.buffer )
+ {
+ // Allocate long write buffer if not previously
+ _long_wr.bufsize = 1024; // TODO bufsize is 10x MTU
+ _long_wr.buffer = (uint8_t*) rtos_malloc(_long_wr.bufsize);
+ _long_wr.count = 0;
+ }
+
+ ble_gatts_rw_authorize_reply_params_t reply = { .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE };
+
+ if ( wr_req->offset + wr_req->len > _long_wr.bufsize )
+ {
+ reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_PREPARE_QUEUE_FULL;
+ }else
+ {
+ reply.params.write = ((ble_gatts_authorize_params_t) {
+ .gatt_status = BLE_GATT_STATUS_SUCCESS,
+ .update = 1,
+ .offset = wr_req->offset,
+ .len = wr_req->len,
+ .p_data = wr_req->data
+ });
+
+ memcpy(_long_wr.buffer+wr_req->offset, wr_req->data, wr_req->len);
+ _long_wr.count = max16(_long_wr.count, wr_req->offset + wr_req->len);
+ }
+
+ sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
+ }
+ break;
+
+ case BLE_GATTS_OP_EXEC_WRITE_REQ_NOW:
+ // Execute Long Write
+ if ( _long_wr.buffer )
+ {
+ ble_gatts_rw_authorize_reply_params_t reply = { .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE };
+ reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
+
+ sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
+
+ // Long write complete, call write callback if set
+ if (_wr_cb) _wr_cb(*this, _long_wr.buffer, _long_wr.count, 0);
+
+ // free up memory
+ rtos_free(_long_wr.buffer);
+ _long_wr.buffer = NULL;
+ _long_wr.bufsize = _long_wr.count = 0;
+ }
+ break;
+
+ case BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL:
+ // Cancel Long Write
+ break;
+
+ case BLE_GATTS_OP_WRITE_REQ:
+ // Write Request with authorization
+ if (_wr_authorize_cb != NULL) _wr_authorize_cb(*this, &request->request.write);
+ break;
+
+ default: break;
+ }
+ }
+
+ if ( (request->type == BLE_GATTS_AUTHORIZE_TYPE_READ) && (_rd_authorize_cb != NULL))
+ {
+ _rd_authorize_cb(*this, &request->request.read);
+ }
+ }
+ break;
+
+ case BLE_GATTS_EVT_WRITE:
+ {
+ ble_gatts_evt_write_t* request = &event->evt.gatts_evt.params.write;
+
+ LOG_LV2_BUFFER(NULL, request->data, request->len);
+
+ // Value write
+ if (request->handle == _handles.value_handle)
+ {
+ LOG_LV2("GATTS", "attr's value, uuid = 0x%04X", request->uuid.uuid);
+ // TODO Ada callback
+ if (_wr_cb)
+ {
+// uint8_t* data = (uint8_t*) rtos_malloc(request->len);
+//
+// if (data)
+// {
+// ada_callback(data, _wr_cb, *this, data, request->len, request->offset);
+// }else
+ {
+ // invoke directly if cannot allocate memory for data
+ _wr_cb(*this, request->data, request->len, request->offset);
+ }
+ }
+ }
+
+ // CCCD write
+ if ( request->handle == _handles.cccd_handle )
+ {
+ LOG_LV2("GATTS", "attr's cccd");
+
+ // Invoke callback if set
+ if (_cccd_wr_cb)
+ {
+ uint16_t value;
+ memcpy(&value, request->data, 2);
+ _cccd_wr_cb(*this, value);
+ }
+ }
+ }
+ break;
+
+ default: break;
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* WRITE
+ *------------------------------------------------------------------*/
+uint16_t BLECharacteristic::write(const char * str)
+{
+ return write((const uint8_t*) str, strlen(str));
+}
+
+uint16_t BLECharacteristic::write(const void* data, uint16_t len)
+{
+ ble_gatts_value_t value =
+ {
+ .len = min16(len, _max_len), // could not exceed max len
+ .offset = 0 , // TODO gatts long write
+ .p_value = (uint8_t*) data
+ };
+
+ // conn handle only needed for system attribute
+ VERIFY_STATUS( sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID, _handles.value_handle, &value), 0 );
+
+ return value.len;
+}
+
+uint16_t BLECharacteristic::write8(uint8_t num)
+{
+ return write( (uint8_t*) &num, sizeof(num));
+}
+
+uint16_t BLECharacteristic::write16(uint16_t num)
+{
+ return write( (uint8_t*) &num, sizeof(num));
+}
+
+uint16_t BLECharacteristic::write32(uint32_t num)
+{
+ return write( (uint8_t*) &num, sizeof(num));
+}
+
+uint16_t BLECharacteristic::write32(int num)
+{
+ return write32( (uint32_t) num );
+}
+
+/*------------------------------------------------------------------*/
+/* READ
+ *------------------------------------------------------------------*/
+/**
+ * Read Characteristic's value
+ * @param buffer memory to hold value
+ * @param len size of memory
+ * @param offset offset of value (dfeault is 0)
+ * @return number of read bytes
+ */
+uint16_t BLECharacteristic::read(void* buffer, uint16_t bufsize, uint16_t offset)
+{
+ ble_gatts_value_t value =
+ {
+ .len = bufsize,
+ .offset = offset,
+ .p_value = (uint8_t*) buffer
+ };
+
+ // conn handle only needed for system attribute
+ VERIFY_STATUS(sd_ble_gatts_value_get(BLE_CONN_HANDLE_INVALID, _handles.value_handle, &value), 0);
+
+ return value.len;
+}
+
+uint8_t BLECharacteristic::read8(void)
+{
+ uint8_t num;
+ return read(&num, sizeof(num)) ? num : 0;
+}
+
+uint16_t BLECharacteristic::read16(void)
+{
+ uint16_t num;
+ return read(&num, sizeof(num)) ? num : 0;
+}
+
+uint32_t BLECharacteristic::read32(void)
+{
+ uint32_t num;
+ return read(&num, sizeof(num)) ? num : 0;
+}
+
+
+uint16_t BLECharacteristic::getCccd(void)
+{
+ VERIFY( Bluefruit.connected() && (_handles.cccd_handle != BLE_GATT_HANDLE_INVALID), 0 );
+
+ uint16_t cccd;
+ ble_gatts_value_t value =
+ {
+ .len = 2,
+ .offset = 0,
+ .p_value = (uint8_t*) &cccd
+ };
+
+ err_t err = sd_ble_gatts_value_get(Bluefruit.connHandle(), _handles.cccd_handle, &value);
+
+ // CCCD is not set, count as not enabled
+ if ( BLE_ERROR_GATTS_SYS_ATTR_MISSING == err )
+ {
+ cccd = 0;
+ }else
+ {
+ VERIFY_STATUS(err);
+ }
+
+ return cccd;
+}
+
+/*------------------------------------------------------------------*/
+/* NOTIFY
+ *------------------------------------------------------------------*/
+bool BLECharacteristic::notifyEnabled(void)
+{
+ VERIFY( _properties.notify );
+ return (getCccd() & BLE_GATT_HVX_NOTIFICATION);
+}
+
+bool BLECharacteristic::notify(const void* data, uint16_t len)
+{
+ VERIFY( _properties.notify );
+
+ // could not exceed max len
+ uint16_t remaining = min16(len, _max_len);
+
+ if ( notifyEnabled() )
+ {
+ uint16_t const max_payload = Bluefruit.Gap.getMTU( Bluefruit.connHandle() ) - 3;
+ const uint8_t* u8data = (const uint8_t*) data;
+
+ while ( remaining )
+ {
+ // TODO multiple connection support
+ // Failed if there is no free buffer
+ if ( !Bluefruit.Gap.getHvnPacket( Bluefruit.connHandle() ) ) return false;
+
+ uint16_t packet_len = min16(max_payload, remaining);
+
+ ble_gatts_hvx_params_t hvx_params =
+ {
+ .handle = _handles.value_handle,
+ .type = BLE_GATT_HVX_NOTIFICATION,
+ .offset = 0,
+ .p_len = &packet_len,
+ .p_data = (uint8_t*) u8data,
+ };
+
+ LOG_LV2("CHR", "Notify %d bytes", packet_len);
+ VERIFY_STATUS( sd_ble_gatts_hvx(Bluefruit.connHandle(), &hvx_params), false );
+
+ remaining -= packet_len;
+ u8data += packet_len;
+ }
+ }
+ else
+ {
+ write(data, remaining);
+ return false;
+ }
+
+ return true;
+}
+
+bool BLECharacteristic::notify(const char * str)
+{
+ return notify( (const uint8_t*) str, strlen(str) );
+}
+
+bool BLECharacteristic::notify8(uint8_t num)
+{
+ return notify( (uint8_t*) &num, sizeof(num));
+}
+
+bool BLECharacteristic::notify16(uint16_t num)
+{
+ return notify( (uint8_t*) &num, sizeof(num));
+}
+
+bool BLECharacteristic::notify32(uint32_t num)
+{
+ return notify( (uint8_t*) &num, sizeof(num));
+}
+
+bool BLECharacteristic::notify32(int num)
+{
+ return notify32( (uint32_t) num);
+}
+
+/*------------------------------------------------------------------*/
+/* INDICATE
+ *------------------------------------------------------------------*/
+bool BLECharacteristic::indicateEnabled(void)
+{
+ VERIFY( _properties.indicate );
+ return (getCccd() & BLE_GATT_HVX_INDICATION);
+}
+
+bool BLECharacteristic::indicate(const void* data, uint16_t len)
+{
+ VERIFY( _properties.indicate );
+
+ // could not exceed max len
+ uint16_t remaining = min16(len, _max_len);
+
+ if ( indicateEnabled() )
+ {
+ uint16_t conn_hdl = Bluefruit.connHandle();
+
+ uint16_t const max_payload = Bluefruit.Gap.getMTU( conn_hdl ) - 3;
+ const uint8_t* u8data = (const uint8_t*) data;
+
+ while ( remaining )
+ {
+ uint16_t packet_len = min16(max_payload, remaining);
+
+ ble_gatts_hvx_params_t hvx_params =
+ {
+ .handle = _handles.value_handle,
+ .type = BLE_GATT_HVX_INDICATION,
+ .offset = 0,
+ .p_len = &packet_len,
+ .p_data = (uint8_t*) u8data,
+ };
+
+ LOG_LV2("CHR", "Indicate %d bytes", packet_len);
+
+ // Blocking wait until receiving confirmation from peer
+ VERIFY_STATUS( sd_ble_gatts_hvx( conn_hdl, &hvx_params), false );
+ VERIFY ( Bluefruit.Gatt.waitForIndicateConfirm(conn_hdl) );
+
+ remaining -= packet_len;
+ u8data += packet_len;
+ }
+ }
+ else
+ {
+ write(data, remaining);
+ return false;
+ }
+
+ return true;
+}
+
+bool BLECharacteristic::indicate(const char * str)
+{
+ return indicate( (const uint8_t*) str, strlen(str) );
+}
+
+bool BLECharacteristic::indicate8(uint8_t num)
+{
+ return indicate( (uint8_t*) &num, sizeof(num));
+}
+
+bool BLECharacteristic::indicate16(uint16_t num)
+{
+ return indicate( (uint8_t*) &num, sizeof(num));
+}
+
+bool BLECharacteristic::indicate32(uint32_t num)
+{
+ return indicate( (uint8_t*) &num, sizeof(num));
+}
+
+bool BLECharacteristic::indicate32(int num)
+{
+ return indicate32( (uint32_t) num);
+}