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 BLE GATT characteristics, and is used throughput the Adafruit Bluefruit nRF52 API and helper classes.

Unless you are implementing a custom 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 two main steps to using the BLECharacteristic class.

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

BLECharacteristic myChar = BLECharacteristic(0xABCD);

Then you need to set the relevant properties for the characteristic, with the following values at minimum:

myChar.setProperties(CHR_PROPS_READ);
myChar.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
myChar.setFixedLen(1); // Alternatively .setMaxLen(uint16_t len)
myChar.begin();
  • .setProperties can be set to one or more of the following macros, which correspond to a single bit in the eight bit 'properties' field for the characteristic definition:
    • CHR_PROPS_BROADCAST = bit(0),
    • CHR_PROPS_READ = bit(1),
    • CHR_PROPS_WRITE_WO_RESP = bit(2),
    • CHR_PROPS_WRITE = bit(3),
    • CHR_PROPS_NOTIFY = bit(4),
    • CHR_PROPS_INDICATE = bit(5)
  • .setPermission sets the security level for the characteristic, where the first value sets the read permissions, and the second value sets the write permissions, where both fields can have one of the following values:
    • SECMODE_NO_ACCESS = 0x00,
    • SECMODE_OPEN = 0x11,
    • SECMODE_ENC_NO_MITM = 0x21,
    • SECMODE_ENC_WITH_MITM = 0x31,
    • SECMODE_SIGNED_NO_MITM = 0x12,
    • SECMODE_SIGNED_WITH_MITM = 0x22
  • .setFixedLen() indicates how many bytes this characteristic has. For characteristics that use 'notify' or 'indicate' this value can be from 1..20, other characteristic types can be set from 1..512 and values >20 bytes will be sent across multiple 20 byte packets. If the characteristic has a variable len, you set the .setMaxLen() value to the maximum value it will hold (up to 20 bytes).
  • .begin() will cause this characteristic to be added to the last BLEService that had it's .begin() method called.

Order of Operations (Important!)

One very important thing to take into consideration when working with BLEService and BLECharacteristic, is that any BLECharacteristic will automatically be added to the last BLEService that had it's `.begin()` function called. As such, you must call yourService.begin() before adding any characteristics!

See the example at the bottom of this page for a concrete example of how this works in practice.

API

BLECharacteristic 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 (*read_authorize_cb_t)  (BLECharacteristic& chr, ble_gatts_evt_read_t * request);
typedef void (*write_authorize_cb_t) (BLECharacteristic& chr, ble_gatts_evt_write_t* request);
typedef void (*write_cb_t)           (BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset);
typedef void (*write_cccd_cb_t)      (BLECharacteristic& chr, uint16_t value);

BLEUuid uuid;

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

// Destructor
virtual ~BLECharacteristic();

BLEService& parentService(void);

void setTempMemory(void);

/*------------- Configure -------------*/
void setUuid(BLEUuid bleuuid);
void setProperties(uint8_t prop);
void setPermission(BleSecurityMode read_perm, BleSecurityMode write_perm);
void setMaxLen(uint16_t max_len);
void setFixedLen(uint16_t fixed_len);

/*------------- Descriptors -------------*/
void setUserDescriptor(const char* descriptor); // aka user descriptor
void setReportRefDescriptor(uint8_t id, uint8_t type); // TODO refactor to use addDescriptor()
void setPresentationFormatDescriptor(uint8_t type, int8_t exponent, uint16_t unit, uint8_t name_space = 1, uint16_t descritpor = 0);

/*------------- Callbacks -------------*/
void setWriteCallback        (write_cb_t fp);
void setCccdWriteCallback    (write_cccd_cb_t fp);
void setReadAuthorizeCallback(read_authorize_cb_t fp);
void setWriteAuthorizeCallbak(write_authorize_cb_t fp);

virtual err_t begin(void);

// Add Descriptor function must be called right after begin()
err_t addDescriptor(BLEUuid bleuuid, void const * content, uint16_t len, BleSecurityMode read_perm = SECMODE_OPEN, BleSecurityMode write_perm = SECMODE_NO_ACCESS);

ble_gatts_char_handles_t handles(void);

/*------------- Write -------------*/
uint16_t write(const void* data, uint16_t len);
uint16_t write(const char* str);

uint16_t write8    (uint8_t  num);
uint16_t write16   (uint16_t num);
uint16_t write32   (uint32_t num);
uint16_t write32   (int      num);


/*------------- Read -------------*/
uint16_t read(void* buffer, uint16_t bufsize);

uint8_t  read8 (void);
uint16_t read16(void);
uint32_t read32(void);

/*------------- Notify -------------*/
uint16_t getCccd(void);

bool notifyEnabled(void);

bool notify(const void* data, uint16_t len);
bool notify(const char* str);

bool notify8    (uint8_t  num);
bool notify16   (uint16_t num);
bool notify32   (uint32_t num);
bool notify32   (int      num);

/*------------- Indicate -------------*/
bool indicateEnabled(void);

bool indicate(const void* data, uint16_t len);
bool indicate(const char* str);

bool indicate8    (uint8_t  num);
bool indicate16   (uint16_t num);
bool indicate32   (uint32_t num);
bool indicate32   (int      num);

Example

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

Note that this example code is incomplete. For the full example open the 'custom_hrm' example that is part of the nRF52 BSP! The code below is for illustration purposes only.
/* HRM Service Definitions
 * Heart Rate Monitor Service:  0x180D
 * Heart Rate Measurement Char: 0x2A37
 * Body Sensor Location Char:   0x2A38
 */
BLEService        hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);

void setupHRM(void)
{
  // Configure the Heart Rate Monitor service
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
  // Supported Characteristics:
  // Name                         UUID    Requirement Properties
  // ---------------------------- ------  ----------- ----------
  // Heart Rate Measurement       0x2A37  Mandatory   Notify
  // Body Sensor Location         0x2A38  Optional    Read
  // Heart Rate Control Point     0x2A39  Conditional Write       <-- Not used here
  hrms.begin();

  // Note: You must call .begin() on the BLEService before calling .begin() on
  // any characteristic(s) within that service definition.. Calling .begin() on
  // a BLECharacteristic will cause it to be added to the last BLEService that
  // was 'begin()'ed!

  // Configure the Heart Rate Measurement characteristic
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
  // Permission = Notify
  // Min Len    = 1
  // Max Len    = 8
  //    B0      = UINT8  - Flag (MANDATORY)
  //      b5:7  = Reserved
  //      b4    = RR-Internal (0 = Not present, 1 = Present)
  //      b3    = Energy expended status (0 = Not present, 1 = Present)
  //      b1:2  = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
  //      b0    = Value format (0 = UINT8, 1 = UINT16)
  //    B1      = UINT8  - 8-bit heart rate measurement value in BPM
  //    B2:3    = UINT16 - 16-bit heart rate measurement value in BPM
  //    B4:5    = UINT16 - Energy expended in joules
  //    B6:7    = UINT16 - RR Internal (1/1024 second resolution)
  hrmc.setProperties(CHR_PROPS_NOTIFY);
  hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  hrmc.setFixedLen(2);
  hrmc.setCccdWriteCallback(cccd_callback);  // Optionally capture CCCD updates
  hrmc.begin();
  uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
  hrmc.notify(hrmdata, 2);                   // Use .notify instead of .write!

  // Configure the Body Sensor Location characteristic
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
  // Permission = Read
  // Min Len    = 1
  // Max Len    = 1
  //    B0      = UINT8 - Body Sensor Location
  //      0     = Other
  //      1     = Chest
  //      2     = Wrist
  //      3     = Finger
  //      4     = Hand
  //      5     = Ear Lobe
  //      6     = Foot
  //      7:255 = Reserved
  bslc.setProperties(CHR_PROPS_READ);
  bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  bslc.setFixedLen(1);
  bslc.begin();
  bslc.write8(2);    // Set the characteristic to 'Wrist' (2)
}

void cccd_callback(BLECharacteristic& chr, uint16_t cccd_value)
{
    // Display the raw request packet
    Serial.print("CCCD Updated: ");
    //Serial.printBuffer(request->data, request->len);
    Serial.print(cccd_value);
    Serial.println("");

    // Check the characteristic this CCCD update is associated with in case
    // this handler is used for multiple CCCD records.
    if (chr.uuid == hrmc.uuid) {
        if (chr.notifyEnabled()) {
            Serial.println("Heart Rate Measurement 'Notify' enabled");
        } else {
            Serial.println("Heart Rate Measurement 'Notify' disabled");
        }
    }
}

This guide was first published on Mar 22, 2017. It was last updated on Mar 19, 2024.

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

Text editor powered by tinymce.