The Bluefruit nRF52 BSP codebase is undergoing active development based on customer feedback and testing. As such, the class documentation here is incomplete, and you should consult the Github repo for the latest code and API developments: https://goo.gl/LdEx62

This base class is used when defining custom client for BLE GATT characteristics, and is used throughout the Adafruit Bluefruit nRF52 API and helper classes.

Unless you are implementing a custom client for GATT service and characteristic, you normally won't use this base class directly, and would instantiate and call a higher level helper service or characteristic included in the Bluefruit nRF52 API.

Basic Usage

There are three main steps to using the BLECharacteristic class.

1.) First, you need to declare and instantiate your BLECharacteristic class with a 16-bit or 128-bit UUID:

BLEClientCharacteristic myChar = BLEClientCharacteristic(0xABCD);

2.) Then you need to set the relevant callback for the characteristic if it supports notify or indicate.

myChar.setNotifyCallback(notify_callback);
myChar.begin();
  • .setNotifyCallback This sets the callback that will be fired when we receive a Notify message from peripheral. This is needed to handle notifiable characteristic since callback allow us to response to the message in timely manner
  • .begin() will cause this characteristic to be added to the last BLEClientService that had it's .begin() method called.

3) Discover the characteristic after connected to peripheral by calling .discover() It is a must in order to perform any operation such as .read(), .write(), .enableNotify().

if ( myChar.discover() )
{
  uint32_t value = myChar.read32();
}

API

BLEClientCharacteristic has the following overall class structure:

This documentation may be slightly out of date as bugs are fixed, and the API develops. You should always consult the Github repo for the definitive latest code release and class definitions!
/*--------- Callback Signatures ----------*/
typedef void (*notify_cb_t  ) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
typedef void (*indicate_cb_t) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);

BLEUuid uuid;

// Constructors
BLEClientCharacteristic(void);
BLEClientCharacteristic(BLEUuid bleuuid);

// Destructor
virtual ~BLEClientCharacteristic();

void     begin(BLEClientService* parent_svc = NULL);

bool     discover(void);
bool     discovered(void);

uint16_t connHandle(void);
uint16_t valueHandle(void);
uint8_t  properties(void);

BLEClientService& parentService(void);

/*------------- Read -------------*/
uint16_t read(void* buffer, uint16_t bufsize);
uint8_t  read8 (void);
uint16_t read16(void);
uint32_t read32(void);

/*------------- Write without Response-------------*/
uint16_t write     (const void* data, uint16_t len);
uint16_t write8    (uint8_t value);
uint16_t write16   (uint16_t value);
uint16_t write32   (uint32_t value);

/*------------- Write with Response-------------*/
uint16_t write_resp(const void* data, uint16_t len);
uint16_t write8_resp    (uint8_t value);
uint16_t write16_resp   (uint16_t value);
uint16_t write32_resp   (uint32_t value);

/*------------- Notify -------------*/
bool     writeCCCD       (uint16_t value);

bool     enableNotify    (void);
bool     disableNotify   (void);

bool     enableIndicate  (void);
bool     disableIndicate (void);

/*------------- Callbacks -------------*/
void     setNotifyCallback(notify_cb_t fp, bool useAdaCallback = true);
void     setIndicateCallback(indicate_cb_t fp, bool useAdaCallback = true);

Example

The following example configures an instance of the Heart Rate Monitor (HRM) Service and it's related characteristics:

/*********************************************************************
 This is an example for our nRF52 based Bluefruit LE modules

 Pick one up today in the adafruit shop!

 Adafruit invests time and resources providing this open source code,
 please support Adafruit and open-source hardware by purchasing
 products from Adafruit!

 MIT license, check LICENSE for more information
 All text above, and the splash screen below must be included in
 any redistribution
*********************************************************************/

/* This sketch show how to use BLEClientService and BLEClientCharacteristic
 * to implement a custom client that is used to talk with Gatt server on
 * peripheral.
 *
 * Note: you will need another feather52 running peripheral/custom_HRM sketch
 * to test with.
 */

#include <bluefruit.h>

/* HRM Service Definitions
 * Heart Rate Monitor Service:  0x180D
 * Heart Rate Measurement Char: 0x2A37 (Mandatory)
 * Body Sensor Location Char:   0x2A38 (Optional)
 */

BLEClientService        hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLEClientCharacteristic bslc(UUID16_CHR_BODY_SENSOR_LOCATION);

void setup()
{
  Serial.begin(115200);

  Serial.println("Bluefruit52 Central Custom HRM Example");
  Serial.println("--------------------------------------\n");

  // Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
  // SRAM usage required by SoftDevice will increase dramatically with number of connections
  Bluefruit.begin(0, 1);

  Bluefruit.setName("Bluefruit52 Central");

  // Initialize HRM client
  hrms.begin();

  // Initialize client characteristics of HRM.
  // Note: Client Char will be added to the last service that is begin()ed.
  bslc.begin();

  // set up callback for receiving measurement
  hrmc.setNotifyCallback(hrm_notify_callback);
  hrmc.begin();

  // Increase Blink rate to different from PrPh advertising mode
  Bluefruit.setConnLedInterval(250);

  // Callbacks for Central
  Bluefruit.Central.setDisconnectCallback(disconnect_callback);
  Bluefruit.Central.setConnectCallback(connect_callback);

  /* Start Central Scanning
   * - Enable auto scan if disconnected
   * - Interval = 100 ms, window = 80 ms
   * - Don't use active scan
   * - Filter only accept HRM service
   * - Start(timeout) with timeout = 0 will scan forever (until connected)
   */
  Bluefruit.Scanner.setRxCallback(scan_callback);
  Bluefruit.Scanner.restartOnDisconnect(true);
  Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
  Bluefruit.Scanner.filterUuid(hrms.uuid);
  Bluefruit.Scanner.useActiveScan(false);
  Bluefruit.Scanner.start(0);                   // // 0 = Don't stop scanning after n seconds
}

void loop()
{
  // do nothing
}

/**
 * Callback invoked when scanner pick up an advertising data
 * @param report Structural advertising data
 */
void scan_callback(ble_gap_evt_adv_report_t* report)
{
  // Connect to device with HRM service in advertising
  Bluefruit.Central.connect(report);
}

/**
 * Callback invoked when an connection is established
 * @param conn_handle
 */
void connect_callback(uint16_t conn_handle)
{
  Serial.println("Connected");
  Serial.print("Discovering HRM Service ... ");

  // If HRM is not found, disconnect and return
  if ( !hrms.discover(conn_handle) )
  {
    Serial.println("Found NONE");

    // disconect since we couldn't find HRM service
    Bluefruit.Central.disconnect(conn_handle);

    return;
  }

  // Once HRM service is found, we continue to discover its characteristic
  Serial.println("Found it");

  
  Serial.print("Discovering Measurement characteristic ... ");
  if ( !hrmc.discover() )
  {
    // Measurement chr is mandatory, if it is not found (valid), then disconnect
    Serial.println("not found !!!");  
    Serial.println("Measurement characteristic is mandatory but not found");
    Bluefruit.Central.disconnect(conn_handle);
    return;
  }
  Serial.println("Found it");

  // Measurement is found, continue to look for option Body Sensor Location
  // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
  // Body Sensor Location is optional, print out the location in text if present
  Serial.print("Discovering Body Sensor Location characteristic ... ");
  if ( bslc.discover() )
  {
    Serial.println("Found it");
    
    // Body sensor location value is 8 bit
    const char* body_str[] = { "Other", "Chest", "Wrist", "Finger", "Hand", "Ear Lobe", "Foot" };

    // Read 8-bit BSLC value from peripheral
    uint8_t loc_value = bslc.read8();
    
    Serial.print("Body Location Sensor: ");
    Serial.println(body_str[loc_value]);
  }else
  {
    Serial.println("Found NONE");
  }

  // Reaching here means we are ready to go, let's enable notification on measurement chr
  if ( hrmc.enableNotify() )
  {
    Serial.println("Ready to receive HRM Measurement value");
  }else
  {
    Serial.println("Couldn't enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting");
  }
}

/**
 * Callback invoked when a connection is dropped
 * @param conn_handle
 * @param reason
 */
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  (void) conn_handle;
  (void) reason;

  Serial.println("Disconnected");
}


/**
 * Hooked callback that triggered when a measurement value is sent from peripheral
 * @param chr   Pointer client characteristic that even occurred,
 *              in this example it should be hrmc
 * @param data  Pointer to received data
 * @param len   Length of received data
 */
void hrm_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
  // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
  // Measurement contains of control byte0 and measurement (8 or 16 bit) + optional field
  // if byte0's bit0 is 0 --> measurement is 8 bit, otherwise 16 bit.

  Serial.print("HRM Measurement: ");

  if ( data[0] & bit(0) )
  {
    uint16_t value;
    memcpy(&value, data+1, 2);

    Serial.println(value);
  }
  else
  {
    Serial.println(data[1]);
  }
}

This guide was first published on Mar 11, 2020. It was last updated on Mar 28, 2024.

This page (BLEClientCharacteristic) was last updated on Mar 08, 2024.

Text editor powered by tinymce.