diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53efc6a..80dd3f1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,7 +5,6 @@ cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32)
idf_component_register(SRCS "src/FreeRTOS.cpp"
- "src/NimBLE2902.cpp"
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
diff --git a/docs/BREAKING_API_CHANGES.md b/docs/BREAKING_API_CHANGES.md
index 70bd97b..bec4bf1 100644
--- a/docs/BREAKING_API_CHANGES.md
+++ b/docs/BREAKING_API_CHANGES.md
@@ -33,15 +33,18 @@ When creating a characteristic the properties are now set with `NIMBLE_PROPERTY:
### Descriptors
Descriptors are now created using the `NimBLECharacteristic::createDescriptor()` method.
-The previous method `BLECharacteristic::addDescriptor()` is now a private function in the library.
+The previous method `BLECharacteristic::addDescriptor()` has been removed.
-This was done because the NimBLE host automatically creates a 0x2902 descriptor if a characteristic has NOTIFY or INDICATE properties applied.
-Due to this fact, the library also creates one automatically for your application.
-The only reason to manually create this descriptor now is to assign callback functions.
-If you do not require this functionality you can safely exclude the manual creation of the 0x2902 descriptor.
+0x2902 Descriptor class: formerly known as BLE2902 or NimBLE2902 has been removed.
+It was no longer useful as a new callback `NimBLECharacteristicCallbacks::onSubscribe` was added
+to handle callback functionality and the client subscription status is handled internally.
-For any other descriptor, (except 0x2904, see below) it should now be created just as characteristics are
-by using the `NimBLECharacteristic::createDescriptor` method.
+NimBLE automatically creates the 0x2902 descriptor if a characteristic has a notification or indication property assigned to it.
+
+**Note** Attempting to create a 0x2902 descriptor will trigger an assert to notify the error,
+allowing the creation of it would cause a fault in the NimBLE stack.
+
+All other desctiptors are now created just as characteristics are by using the `NimBLECharacteristic::createDescriptor` methods (except 0x2904, see below).
Which are defined as:
```
NimBLEDescriptor* createDescriptor(const char* uuid,
@@ -67,15 +70,14 @@ pDescriptor = pCharacteristic->createDescriptor("ABCD",
Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes.
-For the 0x2904 and 0x2902 descriptor, there is a special class that is created when you call `createDescriptor("2904")`or `createDescriptor("2902")`.
+For the 0x2904, there is a special class that is created when you call `createDescriptor("2904").
-The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` or `NimBLE2902` so you must cast the returned pointer to
-`NimBLE2904` or `NimBLE2902` to access the specific class methods.
+The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to
+`NimBLE2904` to access the specific class methods.
##### Example
```
p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
-p2902 = (NimBLE2902*)pCharacteristic->createDescriptor("2902");
```
@@ -91,7 +93,6 @@ Security is set on the characteristic or descriptor properties by applying one o
When a peer wants to read or write a characteristic or descriptor with any of these properties applied
it will trigger the pairing process. By default the "just-works" pairing will be performed automatically.
This can be changed to use passkey authentication or numeric comparison. See [Security Differences](#security-differences) for details.
-
# Client API Differences
diff --git a/src/NimBLE2902.cpp b/src/NimBLE2902.cpp
deleted file mode 100644
index 04a07b6..0000000
--- a/src/NimBLE2902.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * NimBLE2902.cpp
- *
- * Created: on March 10, 2020
- * Author H2zero
- *
- * Originally:
- *
- * BLE2902.cpp
- *
- * Created on: Jun 25, 2017
- * Author: kolban
- */
-
-
-/*
- * See also:
- * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
- */
-#include "sdkconfig.h"
-#if defined(CONFIG_BT_ENABLED)
-
-#include "nimconfig.h"
-#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-
-#include "NimBLE2902.h"
-
-NimBLE2902::NimBLE2902(NimBLECharacteristic* pCharacterisitic)
-: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902),
- BLE_GATT_CHR_F_READ |
- BLE_GATT_CHR_F_WRITE,
- 2, pCharacterisitic)
-{
- uint8_t data[2] = { 0, 0 };
- setValue(data, 2);
-} // NimBLE2902
-
-
-/**
- * @brief Get the notifications value.
- * @return The notifications value. True if notifications are enabled and false if not.
- */
-bool NimBLE2902::getNotifications() {
- return (getValue()[0] & (1 << 0)) != 0;
-} // getNotifications
-
-
-/**
- * @brief Get the indications value.
- * @return The indications value. True if indications are enabled and false if not.
- */
-bool NimBLE2902::getIndications() {
- return (getValue()[0] & (1 << 1)) != 0;
-} // getIndications
-
-
-/**
- * @brief Set the indications flag.
- * @param [in] flag The indications flag.
- */
-void NimBLE2902::setIndications(bool flag) {
- uint8_t *pValue = getValue();
- if (flag) pValue[0] |= 1 << 1;
- else pValue[0] &= ~(1 << 1);
-} // setIndications
-
-
-/**
- * @brief Set the notifications flag.
- * @param [in] flag The notifications flag.
- */
-void NimBLE2902::setNotifications(bool flag) {
- uint8_t *pValue = getValue();
- if (flag) pValue[0] |= 1 << 0;
- else pValue[0] &= ~(1 << 0);
-} // setNotifications
-
-#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#endif
diff --git a/src/NimBLE2902.h b/src/NimBLE2902.h
deleted file mode 100644
index 2d84b73..0000000
--- a/src/NimBLE2902.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * NimBLE2902.h
- *
- * Created: on March 10, 2020
- * Author H2zero
- *
- * Originally:
- *
- * BLE2902.h
- *
- * Created on: Jun 25, 2017
- * Author: kolban
- */
-
-#ifndef MAIN_NIMBLE2902_H_
-#define MAIN_NIMBLE2902_H_
-#include "sdkconfig.h"
-#if defined(CONFIG_BT_ENABLED)
-
-#include "nimconfig.h"
-#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-
-#include "NimBLEDescriptor.h"
-
-#include
-
-#define NIMBLE_DESC_FLAG_NOTIFY 0x0001
-#define NIMBLE_DESC_FLAG_INDICATE 0x0002
-
-typedef struct {
- uint16_t conn_id;
- uint16_t sub_val;
-} chr_sub_status_t;
-
-
-/**
- * @brief Descriptor for Client Characteristic Configuration.
- *
- * This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902.
- *
- * See also:
- * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
- */
-class NimBLE2902: public NimBLEDescriptor {
-public:
- bool getNotifications();
- bool getIndications();
- void setNotifications(bool flag);
- void setIndications(bool flag);
-private:
- NimBLE2902(NimBLECharacteristic* pCharacterisitic);
- friend class NimBLECharacteristic;
- std::vector m_subscribedVec;
-
-}; // NimBLE2902
-
-#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#endif /* CONFIG_BT_ENABLED */
-#endif /* MAIN_NIMBLE2902_H_ */
diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp
index 4025d83..5c3ff94 100644
--- a/src/NimBLECharacteristic.cpp
+++ b/src/NimBLECharacteristic.cpp
@@ -16,16 +16,18 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
-#include "NimBLE2902.h"
#include "NimBLE2904.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#define NULL_HANDLE (0xffff)
+#define NIMBLE_SUB_NOTIFY 0x0001
+#define NIMBLE_SUB_INDICATE 0x0002
static NimBLECharacteristicCallbacks defaultCallback;
static const char* LOG_TAG = "NimBLECharacteristic";
+
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic.
@@ -86,20 +88,9 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr;
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
- if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) {
- assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set");
- }
- // We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it.
- pDescriptor = getDescriptorByUUID(uuid);
- if(pDescriptor == nullptr) {
- pDescriptor = new NimBLE2902(this);
- } else {
- return pDescriptor;
- }
-
+ assert(0 && "0x2902 descriptors cannot be manually created");
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this);
-
} else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
}
@@ -269,62 +260,64 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
}
+/**
+ * @brief Get the number of clients subscribed to the characteristic.
+ * @returns Number of clients subscribed to notifications / indications.
+ */
+size_t NimBLECharacteristic::getSubscribedCount() {
+ return m_subscribedVec.size();
+}
+
+
/**
* @brief Set the subscribe status for this characteristic.\n
* This will maintain a vector of subscribed clients and their indicate/notify status.
*/
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
- uint16_t subVal = 0;
- if(event->subscribe.cur_notify) {
- subVal |= NIMBLE_DESC_FLAG_NOTIFY;
- }
- if(event->subscribe.cur_indicate) {
- subVal |= NIMBLE_DESC_FLAG_INDICATE;
- }
-
- if(m_pTaskData != nullptr) {
- m_pTaskData->rc = (subVal & NIMBLE_DESC_FLAG_INDICATE) ? 0 :
- NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED;
- xTaskNotifyGive(m_pTaskData->task);
- }
-
- NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
- event->subscribe.conn_handle, subVal);
-
- NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
- if(p2902 == nullptr){
- ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s",
- std::string(getUUID()).c_str());
+ ble_gap_conn_desc desc;
+ if(ble_gap_conn_find(event->subscribe.conn_handle, &desc) != 0) {
return;
}
- p2902->setNotifications(subVal & NIMBLE_DESC_FLAG_NOTIFY);
- p2902->setIndications(subVal & NIMBLE_DESC_FLAG_INDICATE);
- p2902->m_pCallbacks->onWrite(p2902);
+ uint16_t subVal = 0;
+ if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
+ subVal |= NIMBLE_SUB_NOTIFY;
+ }
+ if(event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
+ subVal |= NIMBLE_SUB_INDICATE;
+ }
+ if(m_pTaskData != nullptr) {
+ m_pTaskData->rc = (subVal & NIMBLE_SUB_INDICATE) ? 0 :
+ NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED;
+ xTaskNotifyGive(m_pTaskData->task);
+ }
+
+ NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
+ event->subscribe.conn_handle, subVal);
- auto it = p2902->m_subscribedVec.begin();
- for(;it != p2902->m_subscribedVec.end(); ++it) {
- if((*it).conn_id == event->subscribe.conn_handle) {
+ m_pCallbacks->onSubscribe(this, &desc, subVal);
+
+ auto it = m_subscribedVec.begin();
+ for(;it != m_subscribedVec.end(); ++it) {
+ if((*it).first == event->subscribe.conn_handle) {
break;
}
}
if(subVal > 0) {
- if(it == p2902->m_subscribedVec.end()) {
- chr_sub_status_t client_sub;
- client_sub.conn_id = event->subscribe.conn_handle;
- client_sub.sub_val = subVal;
- p2902->m_subscribedVec.push_back(client_sub);
+ if(it == m_subscribedVec.end()) {
+ m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
return;
}
- (*it).sub_val = subVal;
+ (*it).second = subVal;
- } else if(it != p2902->m_subscribedVec.end()) {
- p2902->m_subscribedVec.erase(it);
- p2902->m_subscribedVec.shrink_to_fit();
+ } else if(it != m_subscribedVec.end()) {
+ m_subscribedVec.erase(it);
+ m_subscribedVec.shrink_to_fit();
}
+
}
@@ -348,15 +341,16 @@ void NimBLECharacteristic::indicate() {
void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength());
- NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
- if(p2902 == nullptr) {
+ if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
+ !(m_properties & NIMBLE_PROPERTY::INDICATE))
+ {
NIMBLE_LOGE(LOG_TAG,
"<< notify-Error; Notify/indicate not enabled for characterisitc: %s",
std::string(getUUID()).c_str());
}
- if (p2902->m_subscribedVec.size() == 0) {
+ if (m_subscribedVec.size() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
return;
}
@@ -370,18 +364,18 @@ void NimBLECharacteristic::notify(bool is_notification) {
(m_properties & BLE_GATT_CHR_F_READ_ENC);
int rc = 0;
- for (auto &it : p2902->m_subscribedVec) {
- uint16_t _mtu = getService()->getServer()->getPeerMTU(it.conn_id);
+ for (auto &it : m_subscribedVec) {
+ uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first);
// check if connected and subscribed
- if(_mtu == 0 || it.sub_val == 0) {
+ if(_mtu == 0 || it.second == 0) {
continue;
}
// check if security requirements are satisfied
if(reqSec) {
struct ble_gap_conn_desc desc;
- rc = ble_gap_conn_find(it.conn_id, &desc);
+ rc = ble_gap_conn_find(it.first, &desc);
if(rc != 0 || !desc.sec_state.encrypted) {
continue;
}
@@ -391,13 +385,13 @@ void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
}
- if(is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_NOTIFY))) {
+ if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead");
is_notification = false;
}
- if(!is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_INDICATE))) {
+ if(!is_notification && (!(it.second & NIMBLE_SUB_INDICATE))) {
NIMBLE_LOGW(LOG_TAG,
"Sending indication to client subscribed to notification, sending notification instead");
is_notification = true;
@@ -414,7 +408,7 @@ void NimBLECharacteristic::notify(bool is_notification) {
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr};
m_pTaskData = &taskData;
- rc = ble_gattc_indicate_custom(it.conn_id, m_handle, om);
+ rc = ble_gattc_indicate_custom(it.first, m_handle, om);
if(rc != 0){
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
} else {
@@ -433,7 +427,7 @@ void NimBLECharacteristic::notify(bool is_notification) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
}
} else {
- rc = ble_gattc_notify_custom(it.conn_id, m_handle, om);
+ rc = ble_gattc_notify_custom(it.first, m_handle, om);
if(rc == 0) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
@@ -574,5 +568,24 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
} // onStatus
+
+/**
+ * @brief Callback function called when a client changes subscription status.
+ * @param [in] pCharacteristic The characteristic that is the source of the event.
+ * @param [in] desc The connection description struct that is associated with the client.
+ * @param [in] subValue The subscription status:
+ * * 0 = Un-Subscribed
+ * * 1 = Notifications
+ * * 2 = Indications
+ * * 3 = Notifications and Indications
+ */
+void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
+ ble_gap_conn_desc* desc,
+ uint16_t subValue)
+{
+ NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
+}
+
+
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h
index 58f18b0..1c7418a 100644
--- a/src/NimBLECharacteristic.h
+++ b/src/NimBLECharacteristic.h
@@ -110,6 +110,7 @@ public:
std::string toString();
uint16_t getHandle();
+ size_t getSubscribedCount();
private:
@@ -145,6 +146,8 @@ private:
ble_task_data_t *m_pTaskData;
portMUX_TYPE m_valMux;
time_t m_timestamp;
+
+ std::vector> m_subscribedVec;
}; // NimBLECharacteristic
@@ -181,6 +184,7 @@ public:
virtual void onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onNotify(NimBLECharacteristic* pCharacteristic);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code);
+ virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue);
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h
index 9bd52c9..16b6edc 100644
--- a/src/NimBLEDescriptor.h
+++ b/src/NimBLEDescriptor.h
@@ -106,7 +106,6 @@ public:
};
#include "NimBLE2904.h"
-#include "NimBLE2902.h"
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp
index 6ce97ae..decc630 100644
--- a/src/NimBLEServer.cpp
+++ b/src/NimBLEServer.cpp
@@ -182,10 +182,6 @@ void NimBLEServer::start() {
// we do it now.
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
-
- if(nullptr == chr->getDescriptorByUUID(uint16_t(0x2902))) {
- chr->createDescriptor(uint16_t(0x2902));
- }
m_notifyChrVec.push_back(chr);
}
}
@@ -303,9 +299,9 @@ size_t NimBLEServer::getConnectedCount() {
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_SUBSCRIBE: {
- NIMBLE_LOGI(LOG_TAG, "subscribe event; cur_notify=%d\n value handle; "
- "val_handle=%d\n",
- event->subscribe.cur_notify, event->subscribe.attr_handle);
+ NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %s",
+ event->subscribe.attr_handle,
+ (event->subscribe.cur_notify ? "true":"false"));
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->subscribe.attr_handle) {
diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp
index e9c62f9..3420da2 100644
--- a/src/NimBLEService.cpp
+++ b/src/NimBLEService.cpp
@@ -147,31 +147,14 @@ bool NimBLEService::start() {
for(uint8_t i=0; i < numChrs;) {
uint8_t numDscs = pCharacteristic->m_dscVec.size();
- if(numDscs) {
- // skip 2902 as it's automatically created by NimBLE
- // if Indicate or Notify flags are set
- if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) ||
- (pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) &&
- pCharacteristic->getDescriptorByUUID("2902") != nullptr)
- {
- numDscs--;
- }
- }
if(!numDscs){
pChr_a[i].descriptors = NULL;
} else {
// Must have last descriptor uuid = 0 so we have to create 1 extra
- //NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs);
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin();
for(uint8_t d=0; d < numDscs;) {
- // skip 2902
- if(pDescriptor->m_uuid == NimBLEUUID(uint16_t(0x2902))) {
- //NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902");
- pDescriptor = *(pCharacteristic->m_dscVec.begin()+d+1);
- continue;
- }
pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u;
pDsc_a[d].att_flags = pDescriptor->m_properties;
pDsc_a[d].min_key_size = 0;