NimBLE-Arduino icon indicating copy to clipboard operation
NimBLE-Arduino copied to clipboard

Characteristic Aggregate Format Descriptor (0x2905) support for Peripheral device

Open srgg opened this issue 2 months ago • 3 comments

Summary

This PR adds full support for the Characteristic Aggregate Format Descriptor (UUID 0x2905) when operating as a BLE peripheral. Correct implementation requires reliable handle mapping for multiple Presentation Format descriptors (0x2904), which previously failed due to duplicate handle assignment.

This PR also introduces PlatformIO build support for the existing examples and includes a usage demonstration of this new capability in NimBLE_Server.ino.

Problem

Descriptor handle resolution used UUID-only lookup (ble_gatts_find_dsc()), which meant:

  • Multiple same-UUID descriptors were assigned the same handle
  • The Aggregate Format descriptor (0x2905) could not assemble a correct referenced handle list
  • Aggregated read/write behavior was broken for peripheral services that include multiple presentation formats

The BLE specification requires each referenced descriptor to have a unique handle for valid aggregation.

Fix

  • Reworked descriptor handle assignment to follow descriptor registration order from ble_gatt_dsc_def
  • Each descriptor instance now retains the unique handle assigned by the NimBLE stack
  • Aggregate Format descriptor correctly lists all Presentation Format descriptors associated with a characteristic

Additional Enhancements

  • PlatformIO support added for all example projects
    They still function as regular Arduino sketches
    and can now also be built and flashed using PlatformIO:
 pio run -e example_server -t upload 
  • Updated the NimBLE_Server.ino example to include a practical usage example of the Aggregate Format descriptor feature:
  /**
     *  2905 “Aggregate Format” descriptor is a special case. When create2905() is called,
     *  it creates an instance of NimBLE2905 with the correct properties and size.
     *  We must then explicitly add the constituent 2904 descriptors that define the
     *  aggregate format (e.g., name, fat percent, weight) using add2904Descriptor().
     *  This ensures the aggregate descriptor correctly references all its component descriptors.
     */
    NimBLE2905* pFormatAggregate = pBurgerIngredientsCharacteristic->create2905();
    pFormatAggregate->add2904Descriptor(pFormatName);
    pFormatAggregate->add2904Descriptor(pFormatFatPercent);
    pFormatAggregate->add2904Descriptor(pFormatWeight);
  • Added a configuration macro for controlling maximum aggregated descriptors:
    /** Uncomment to change the maximum number of aggregated presentation format descriptors; 5 by default. */
    // #define NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS 5
    

Impact

  • Standards-compliant support for 0x2905 on NimBLE peripherals
  • Reliable operation for multiple Presentation Format descriptors (0x2904)
  • Backwards compatible with existing applications and development workflows
  • Easier testing, debugging, and example execution with PlatformIO

srgg avatar Oct 24 '25 13:10 srgg

This looks really good, only issue I have is the changes to the core files. This would make the api incompatible with the esp-idf version of NimBLE, maybe there's another way.

h2zero avatar Oct 24 '25 14:10 h2zero

@h2zero I don’t have much experience with ESP-IDF yet. Could you provide more details about the issue so I can explore alternative ways to address it?

Honestly, I was hesitant to modify ble_gatts directly, since changes are introduced there from time to time. At the moment, the approach I took felt like the most straightforward way to fix the problem.

srgg avatar Oct 24 '25 15:10 srgg

I try to avoid modifying the stack code unless it's to address something that won't break the API with the esp-idf repo. I maintain a separate cpp wrapper for esp-idf: https://github.com/h2zero/esp-nimble-cpp since the NimBLE stack is included already in esp-idf, whereas in this repo it is provided.

So in this case the 2905 class would not work with esp-idf as it would not have those core changes that are needed and the API would no longer be the same for both repos.

h2zero avatar Oct 24 '25 15:10 h2zero