Skip to content

BLE Arduino library - Characteristic with multiple descriptors with the same UUID not supported #1038

@timr49

Description

@timr49

Hardware:

Board: Sparkfun ESP32 Thing
Core Installation/update date: No different as of today (28/1/2018) from origin/master of https://github.com/espressif/arduino-esp32.git
IDE name: Arduino IDE 1.8.5
Flash Frequency: 80Mhz
Upload Speed: 921600

Description:

Side Note: In my opinion, the BLE library for ESP32 with the Arduino IDE is an excellent piece of work. The API is well-structured and clearly written from from the ground up, uses meaningful classes, consistent naming, etc. etc. I have found it and presumably therefore the underlying esp-idf code to be robust.

I have encountered what appears to be an incorrect (and hopefully unnecessary) constraint in the implementation of the BLECharacteristic::addDescriptor method. If multiple descriptors with the same UUID are added to a characteristic, only the first one gets a valid handle allocated during the BLEService start() method and, in my tests, is the only one seen by a BLE GATT client using service discovery.

As I understand the BLE spec's, a characteristic's descriptors are not required to have distinct UUIDs and it is intended that in some cases they do not. For example, if Characteristic Presentation Format descriptors (UUID 0x2904) are being used, there are multiple of them for a characteristic with multiple data elements in its payload. (The Characteristic Aggregate Format descriptor (0x2905) can then be used to define the order of multiple Characteristic Presentation Format descriptors within that payload.)

In this library, the association of descriptors with a characteristic is implemented using a std::map and addDescriptor uses setByUUID method to update that map with a UUID as the key. Since std::map requires keys to be unique, that would appear to be at least one cause of this issue. I do not know whether the underlying software or firmware also imposes such a constraint and hence do not know whether it can be fixed in this library alone.

Can anyone with knowledge of the underlying BLE software/firmware comment on whether characteristics with duplicate descriptor UUIDs are supported further down the stack?

Sketch:

//Change the code below by your sketch
/*
    Test sketch to demonstrate isse with multiple descriptors with the same UUID not working with the ESP32 BLE Arduino library.
    Based on examples/BLE_server ...

    Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
    Ported to Arduino ESP32 by Evandro Copercini
*/

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

// Logically, our characteristic value consists of two fields, the first an unsigned 8 bit integer, the second an unsigned 16 bit integer.
// Physically, that is just three octets.
uint8_t characteristicValue1[] = {
  0x11,
  0x22, 0x33,
};

// The logical structure of the above is described by two Characteristic Presentation Format descriptors, one for each field. See:
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
// With more than one Characteristic Presentation Format descriptors, we should also have a Characteristic Aggregate Format descriptor,
// however that would need the handles of the Characteristic Presentation Format descriptors but we don't get a valid handle for the second one.
uint8_t presentationFormat1[] = {
  0x04, // Format = 4 = "unsigned 8-bit integer"
  0x00, // Exponent = 0
  0x00, // Unit = 0x2700 = "unitless" (low byte)
  0x27, // ditto (high byte)
  0x01, // Namespace = 1 = "Bluetooth SIG Assigned Numbers"
  0x00, // Description = 0 = "unknown" (low byte)
  0x00, // ditto (high byte)
};

uint8_t presentationFormat2[] = {
  0x04, // Format = 6 = "unsigned 16-bit integer"
  0x00, // Exponent = 0
  0x00, // Unit = 0x2700 = "unitless" (low byte)
  0x27, // ditto (high byte)
  0x01, // Namespace = 1 = "Bluetooth SIG Assigned Numbers"
  0x00, // Description = 0 = "unknown" (low byte)
  0x00, // ditto (high byte)
};

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("\r\nStarting BLE work!");

  BLEDevice::init("MyESP32");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  // create two descriptors with the same UUID                                     
  BLEDescriptor *pDescriptor1 = new BLEDescriptor((uint16_t)0x2904); // Characteristic Presentation Format
  BLEDescriptor *pDescriptor2 = new BLEDescriptor((uint16_t)0x2904); // ditto
  // and one with a different UUID
  BLEDescriptor *pDescriptor3 = new BLEDescriptor((uint16_t)0x2901); // Characteristic User Description
  // confirm that the second descriptor was created despite its duplicate UUID
  if (pDescriptor2)
    Serial.println("pDescriptor2 created OK");
  else
    Serial.println("!pDescriptor2");
  // attach all three descriptors to our one characteristic
  pCharacteristic->addDescriptor(pDescriptor1);
  pCharacteristic->addDescriptor(pDescriptor2);
  pCharacteristic->addDescriptor(pDescriptor3);

  // give the characteristic a value and start the service
  pCharacteristic->setValue(characteristicValue1, sizeof characteristicValue1);
  pService->start();

  // set the descriptors' values
  pDescriptor1->setValue(presentationFormat1, sizeof presentationFormat1);
  pDescriptor2->setValue(presentationFormat2, sizeof presentationFormat2);
  pDescriptor3->setValue("My Test Characteristic");

  // enable advertising so that this device can be easily found
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->start();

  // have valid handles been allocated for the descriptors?
  Serial.print("pDescriptor1->getHandle()=0x"); Serial.println(pDescriptor1->getHandle(), HEX);
  Serial.print("pDescriptor2->getHandle()=0x"); Serial.println(pDescriptor2->getHandle(), HEX);
  Serial.print("pDescriptor3->getHandle()=0x"); Serial.println(pDescriptor3->getHandle(), HEX);

  Serial.println("Look for MyESP32 and do service discovery with your favourite BLE scanner.");
  Serial.println("I used \"BLE Scanner 4.0\" (Version 2.0.1) from \"bluepixel technologies\" on iOS");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
}

Debug Messages:

pDescriptor1->getHandle()=0x2C
pDescriptor2->getHandle()=0xFFFF
pDescriptor3->getHandle()=0x2B

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: StaleIssue is stale stage (outdated/stuck)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions