Skip to content

Conversation

@srgg
Copy link

@srgg srgg commented Oct 24, 2025

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 srgg force-pushed the feat/aggregate-format-2905 branch from ccf45e6 to ec85ea8 Compare October 24, 2025 13:25
@h2zero
Copy link
Owner

h2zero commented Oct 24, 2025

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.

@srgg
Copy link
Author

srgg commented Oct 24, 2025

@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.

@h2zero
Copy link
Owner

h2zero commented Oct 24, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants