diff --git a/src/NimBLE2905.cpp b/src/NimBLE2905.cpp new file mode 100644 index 00000000..27959b9f --- /dev/null +++ b/src/NimBLE2905.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NimBLE2905.h" +#include "NimBLELog.h" + +#include "NimBLECharacteristic.h" +#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) + +// Define default if not already defined +#ifndef NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS + #define NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS 5 // Default value +#else + // Ensure the value is within a valid range + #if NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS < 1 || NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS > 255 + #error "NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS must be between 1 and 128" + #endif +#endif + +static const char* LOG_TAG = "NimBLE2905"; + +NimBLE2905::NimBLE2905(NimBLECharacteristic* pChr) + : NimBLEDescriptor(NimBLEUUID(static_cast(0x2905)), BLE_GATT_CHR_F_READ, NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS * sizeof(uint16_t), pChr) { +} // NimBLE2905 + +void NimBLE2905::initValue() { + uint16_t aggregatedHandles[NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS]; + size_t validCount = 0; + + for (auto* desc : m_vAggregatedDescriptors) { + uint16_t handle = desc->getHandle(); + + if (handle == 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to initialize value: presentation format descriptor handle is not initialized"); + continue; + } + + aggregatedHandles[validCount++] = handle; + } + + if (validCount > 0) { + setValue(reinterpret_cast(aggregatedHandles), validCount * sizeof(uint16_t)); + } + + // Presentation formats no longer needed, let's free some memory + m_vAggregatedDescriptors.clear(); + m_vAggregatedDescriptors.shrink_to_fit(); +} // initValue + + +/** + * @brief Add presentation format descriptor. + * @param [in] presentationFormat The 2904 descriptor to aggregate. + */ +void NimBLE2905::add2904Descriptor(const NimBLE2904* presentationFormat) { + if (presentationFormat == nullptr) { + NIMBLE_LOGE(LOG_TAG, "Failed to add presentation format descriptor: nullptr"); + return; + } + + if (m_vAggregatedDescriptors.size() < NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS) { + m_vAggregatedDescriptors.push_back(presentationFormat); + } else { + NIMBLE_LOGE(LOG_TAG, "Failed to add presentation format descriptor: maximum capacity reached"); + } +} // add2904Descriptor + + +#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) diff --git a/src/NimBLE2905.h b/src/NimBLE2905.h new file mode 100644 index 00000000..9c1f9076 --- /dev/null +++ b/src/NimBLE2905.h @@ -0,0 +1,56 @@ +/* + * Copyright 2020-2025 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NIMBLE_CPP_2905_H_ +#define NIMBLE_CPP_2905_H_ + +#include "syscfg/syscfg.h" +#if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) + +# include "NimBLEDescriptor.h" + +/** + * @brief Characteristic Aggregate Format descriptor (UUID: 0x2905). + * + * @details Contains an ordered list of handles referencing 0x2904 Presentation Format + * descriptors that define the parent characteristic’s value. + */ +class NimBLE2905 : public NimBLEDescriptor { + public: + NimBLE2905(NimBLECharacteristic* pChr = nullptr); + + void add2904Descriptor(const NimBLE2904* presentationFormat); + private: + + /** + * @brief Descriptor for the Characteristic Aggregate Format (UUID: 0x2905). + * + * @details Contains an ordered list of handles referencing the 0x2904 + * Presentation Format descriptors that define the parent characteristic's value. + * + * @see NimBLEServer::start() + */ + void initValue(); + + friend class NimBLECharacteristic; + friend class NimBLEServer; + + std::vector m_vAggregatedDescriptors; +}; // NimBLE2904 + +#endif // CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) +#endif // NIMBLE_CPP_2904_H_ diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 343d4071..8bde275b 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -26,6 +26,7 @@ # endif # include "NimBLE2904.h" +# include "NimBLE2905.h" # include "NimBLEDevice.h" # include "NimBLELog.h" @@ -86,6 +87,9 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, if (uuid == NimBLEUUID(static_cast(0x2904))) { NIMBLE_LOGW(LOG_TAG, "0x2904 descriptor should be created with create2904()"); pDescriptor = create2904(); + } else if (uuid == NimBLEUUID(static_cast(0x2905))) { + NIMBLE_LOGW(LOG_TAG, "0x2905 descriptor should be created with create2905()"); + pDescriptor = create2905(); } else { pDescriptor = new NimBLEDescriptor(uuid, properties, maxLen, this); } @@ -104,6 +108,16 @@ NimBLE2904* NimBLECharacteristic::create2904() { return pDescriptor; } // create2904 +/** + * @brief Create a Characteristic Aggregate Format Descriptor for this characteristic. + * @return A pointer to a NimBLE2905 descriptor. + */ +NimBLE2905* NimBLECharacteristic::create2905() { + NimBLE2905* pDescriptor = new NimBLE2905(this); + addDescriptor(pDescriptor); + return pDescriptor; +} // create2905 + /** * @brief Add a descriptor to the characteristic. * @param [in] pDescriptor A pointer to the descriptor to add. diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index e3c70b63..b00a589c 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -26,6 +26,7 @@ class NimBLEService; class NimBLECharacteristic; class NimBLEDescriptor; class NimBLE2904; +class NimBLE2905; # include "NimBLELocalValueAttribute.h" @@ -69,6 +70,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN); NimBLE2904* create2904(); + NimBLE2905* create2905(); NimBLEDescriptor* getDescriptorByUUID(const char* uuid, uint16_t index = 0) const; NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid, uint16_t index = 0) const; NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const; diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 1cab58ed..9fd9ccfb 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -18,6 +18,7 @@ #include "NimBLEServer.h" #if CONFIG_BT_NIMBLE_ENABLED && MYNEWT_VAL(BLE_ROLE_PERIPHERAL) +# include "NimBLE2905.h" # include "NimBLEDevice.h" # include "NimBLELog.h" @@ -317,6 +318,19 @@ bool NimBLEServer::start() { } # endif + // Populate any Aggregate Format (0x2905) descriptors now that every attribute handle has been + // assigned during ble_gatts_start() (via the GATT register callback). A 0x2905 value is the + // ordered list of its aggregated 0x2904 presentation-format descriptor handles. + for (const auto& svc : m_svcVec) { + for (const auto& chr : svc->getCharacteristics()) { + for (auto& desc : chr->m_vDescriptors) { + if (desc->getUUID() == NimBLEUUID(static_cast(0x2905))) { + static_cast(desc)->initValue(); + } + } + } + } + // If the services have changed indicate it now if (m_svcChanged) { m_svcChanged = false; diff --git a/src/nimconfig.h b/src/nimconfig.h index 387896a7..a2a64d07 100644 --- a/src/nimconfig.h +++ b/src/nimconfig.h @@ -5,6 +5,9 @@ * Arduino User Options * **********************************************/ +/** @brief Uncomment to change the maximum number of aggregated presentation format descriptors; 5 by default. */ +// #define NIMBLE_MAX_AGGREGATE_FORMAT_DESCRIPTORS 5 + /** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */ // #define MYNEWT_VAL_BLE_MAX_CONNECTIONS 3