diff --git a/CMakeLists.txt b/CMakeLists.txt
index 769d80a..516e80f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,6 +52,7 @@ idf_component_register(
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
+ "src/NimBLERemoteValueAttribute.cpp"
"src/NimBLEScan.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
diff --git a/docs/Migration_guide.md b/docs/Migration_guide.md
index d1fcee8..accf1bc 100644
--- a/docs/Migration_guide.md
+++ b/docs/Migration_guide.md
@@ -383,13 +383,13 @@ The security callback methods are now incorporated in the `NimBLEServerCallbacks
The callback methods are:
-> `bool onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin)`
+> `bool onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pin)`
Receives the pin when using numeric comparison authentication.
Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject.
-> `void onPassKeyEntry(const NimBLEConnInfo& connInfo)`
+> `void onPassKeyEntry(NimBLEConnInfo& connInfo)`
Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
@@ -399,7 +399,7 @@ Client callback; client should respond with the passkey (pin) by calling `NimBLE
Server callback; should return the passkey (pin) expected from the client.
-> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)`
+> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
diff --git a/examples/Advanced/NimBLE_Client/main/main.cpp b/examples/Advanced/NimBLE_Client/main/main.cpp
index 90e2128..47f8419 100644
--- a/examples/Advanced/NimBLE_Client/main/main.cpp
+++ b/examples/Advanced/NimBLE_Client/main/main.cpp
@@ -39,7 +39,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
- void onPassKeyEntry(const NimBLEConnInfo& connInfo){
+ void onPassKeyEntry(NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
@@ -47,14 +47,14 @@ class ClientCallbacks : public NimBLEClientCallbacks {
NimBLEDevice::injectPassKey(connInfo, 123456);
};
- void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
+ void onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
- void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
+ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
@@ -92,7 +92,7 @@ class scanCallbacks: public NimBLEScanCallbacks {
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
- str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
+ str += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
str += ", Value = " + std::string((char*)pData, length);
diff --git a/examples/Advanced/NimBLE_Server/main/main.cpp b/examples/Advanced/NimBLE_Server/main/main.cpp
index 3effb54..472faa7 100644
--- a/examples/Advanced/NimBLE_Server/main/main.cpp
+++ b/examples/Advanced/NimBLE_Server/main/main.cpp
@@ -52,13 +52,13 @@ class ServerCallbacks: public NimBLEServerCallbacks {
return 123456;
};
- void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
+ void onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
- void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
+ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
@@ -142,7 +142,7 @@ void notifyTask(void * parameter){
if(pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
if(pChr) {
- pChr->notify(true);
+ pChr->notify();
}
}
}
diff --git a/examples/NimBLE_server_get_client_name/main/main.cpp b/examples/NimBLE_server_get_client_name/main/main.cpp
index e255807..6f19db8 100644
--- a/examples/NimBLE_server_get_client_name/main/main.cpp
+++ b/examples/NimBLE_server_get_client_name/main/main.cpp
@@ -25,7 +25,7 @@ class ServerCallbacks : public NimBLEServerCallbacks {
}
// Same as before but now includes the name parameter
- void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name) override {
+ void onAuthenticationComplete(NimBLEConnInfo& connInfo, const std::string& name) override {
if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
diff --git a/examples/basic/BLE_client/main/main.cpp b/examples/basic/BLE_client/main/main.cpp
index cfb80e9..670c4ca 100644
--- a/examples/basic/BLE_client/main/main.cpp
+++ b/examples/basic/BLE_client/main/main.cpp
@@ -51,7 +51,7 @@ class MyClientCallback : public BLEClientCallbacks {
}
/***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/
- void onPassKeyEntry(const NimBLEConnInfo& connInfo){
+ void onPassKeyEntry(NimBLEConnInfo& connInfo){
printf("Server Passkey Entry\n");
/** This should prompt the user to enter the passkey displayed
* on the peer device.
@@ -59,14 +59,14 @@ class MyClientCallback : public BLEClientCallbacks {
NimBLEDevice::injectPassKey(connInfo, 123456);
};
- void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
+ void onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
- void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
+ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
diff --git a/examples/basic/BLE_notify/main/main.cpp b/examples/basic/BLE_notify/main/main.cpp
index b17f49a..807545a 100644
--- a/examples/basic/BLE_notify/main/main.cpp
+++ b/examples/basic/BLE_notify/main/main.cpp
@@ -65,13 +65,13 @@ class MyServerCallbacks: public BLEServerCallbacks {
return 123456;
};
- void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
+ void onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
- void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
+ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
diff --git a/examples/basic/BLE_uart/main/main.cpp b/examples/basic/BLE_uart/main/main.cpp
index 18df6fa..3d0fd5c 100644
--- a/examples/basic/BLE_uart/main/main.cpp
+++ b/examples/basic/BLE_uart/main/main.cpp
@@ -67,13 +67,13 @@ class MyServerCallbacks: public BLEServerCallbacks {
return 123456;
};
- void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
+ void onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPIN(connInfo, true);
};
- void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
+ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
diff --git a/src/NimBLEAttValue.h b/src/NimBLEAttValue.h
index b95e38d..853ae06 100644
--- a/src/NimBLEAttValue.h
+++ b/src/NimBLEAttValue.h
@@ -192,12 +192,7 @@ class NimBLEAttValue {
return setValue(reinterpret_cast(s), len);
}
- /**
- * @brief Get a pointer to the value buffer with timestamp.
- * @param[in] timestamp A pointer to a time_t variable to store the timestamp.
- * @returns A pointer to the internal value buffer.
- */
- const uint8_t* getValue(time_t* timestamp = nullptr) const {
+ const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
if (timestamp != nullptr) {
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
@@ -205,7 +200,7 @@ class NimBLEAttValue {
*timestamp = 0;
# endif
}
- return m_attr_value;
+ return *this;
}
/**
@@ -271,7 +266,15 @@ class NimBLEAttValue {
if (!skipSizeCheck && size() < sizeof(T)) {
return T();
}
- return *(reinterpret_cast(getValue(timestamp)));
+ if (timestamp != nullptr) {
+# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
+ *timestamp = m_timestamp;
+# else
+ *timestamp = 0;
+# endif
+ }
+
+ return *(reinterpret_cast(m_attr_value));
}
/*********************** Operators ************************/
diff --git a/src/NimBLEAttribute.h b/src/NimBLEAttribute.h
new file mode 100644
index 0000000..8fb3b7a
--- /dev/null
+++ b/src/NimBLEAttribute.h
@@ -0,0 +1,50 @@
+/*
+ * NimBLEAttribute.h
+ *
+ * Created: on July 28 2024
+ * Author H2zero
+ */
+
+#ifndef NIMBLE_CPP_ATTRIBUTE_H_
+#define NIMBLE_CPP_ATTRIBUTE_H_
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_ENABLED) && (defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL))
+
+# include "NimBLEUUID.h"
+
+/**
+ * @brief A base class for BLE attributes.
+ */
+class NimBLEAttribute {
+ public:
+ /**
+ * @brief Get the UUID of the attribute.
+ * @return The UUID.
+ */
+ const NimBLEUUID& getUUID() const { return m_uuid; }
+
+ /**
+ * @brief Get the handle of the attribute.
+ */
+ uint16_t getHandle() const { return m_handle; };
+
+ protected:
+ /**
+ * @brief Construct a new NimBLEAttribute object.
+ * @param [in] handle The handle of the attribute.
+ * @param [in] uuid The UUID of the attribute.
+ */
+ NimBLEAttribute(const NimBLEUUID& uuid, uint16_t handle) : m_uuid{uuid}, m_handle{handle} {}
+
+ /**
+ * @brief Destroy the NimBLEAttribute object.
+ */
+ ~NimBLEAttribute() = default;
+
+ const NimBLEUUID m_uuid{};
+ uint16_t m_handle{0};
+};
+
+#endif // CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL)
+#endif // NIMBLE_CPP_ATTRIBUTE_H_
diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp
index ba68a73..6a7b07c 100644
--- a/src/NimBLECharacteristic.cpp
+++ b/src/NimBLECharacteristic.cpp
@@ -13,18 +13,16 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#include "NimBLECharacteristic.h"
-#include "NimBLE2904.h"
-#include "NimBLEDevice.h"
-#include "NimBLELog.h"
+# include "NimBLECharacteristic.h"
+# include "NimBLE2904.h"
+# include "NimBLEDevice.h"
+# include "NimBLELog.h"
-#define NULL_HANDLE (0xffff)
-#define NIMBLE_SUB_NOTIFY 0x0001
-#define NIMBLE_SUB_INDICATE 0x0002
+# define NIMBLE_SUB_NOTIFY 0x0001
+# define NIMBLE_SUB_INDICATE 0x0002
static NimBLECharacteristicCallbacks defaultCallback;
-static const char* LOG_TAG = "NimBLECharacteristic";
-
+static const char* LOG_TAG = "NimBLECharacteristic";
/**
* @brief Construct a characteristic
@@ -33,10 +31,8 @@ static const char* LOG_TAG = "NimBLECharacteristic";
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
-NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties,
- uint16_t max_len, NimBLEService* pService)
-: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {
-}
+NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
+ : NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {}
/**
* @brief Construct a characteristic
@@ -45,27 +41,20 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to.
*/
-NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties,
- uint16_t max_len, NimBLEService* pService)
-: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
- m_uuid = uuid;
- m_handle = NULL_HANDLE;
- m_properties = properties;
- m_pCallbacks = &defaultCallback;
- m_pService = pService;
- m_removed = 0;
+NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
+ : NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallback}, m_pService{pService} {
+ setProperties(properties);
} // NimBLECharacteristic
/**
* @brief Destructor.
*/
NimBLECharacteristic::~NimBLECharacteristic() {
- for(auto &it : m_dscVec) {
- delete it;
+ for (const auto& dsc : m_vDescriptors) {
+ delete dsc;
}
} // ~NimBLECharacteristic
-
/**
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
@@ -77,7 +66,6 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
return createDescriptor(NimBLEUUID(uuid), properties, max_len);
}
-
/**
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
@@ -85,7 +73,7 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
* @param [in] max_len - The max length in bytes of the descriptor value.
* @return The new BLE descriptor.
*/
-NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
+NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr;
if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this);
@@ -97,47 +85,52 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
return pDescriptor;
} // createDescriptor
-
/**
* @brief Add a descriptor to the characteristic.
* @param [in] pDescriptor A pointer to the descriptor to add.
*/
-void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) {
+void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) {
bool foundRemoved = false;
-
- if(pDescriptor->m_removed > 0) {
- for(auto& it : m_dscVec) {
- if(it == pDescriptor) {
+ if (pDescriptor->getRemoved() > 0) {
+ for (const auto& dsc : m_vDescriptors) {
+ if (dsc == pDescriptor) {
foundRemoved = true;
- pDescriptor->m_removed = 0;
+ pDescriptor->setRemoved(0);
}
}
}
- if(!foundRemoved) {
- m_dscVec.push_back(pDescriptor);
+ // Check if the descriptor is already in the vector and if so, return.
+ for (const auto& dsc : m_vDescriptors) {
+ if (dsc == pDescriptor) {
+ pDescriptor->setCharacteristic(this); // Update the characteristic pointer in the descriptor.
+ return;
+ }
+ }
+
+ if (!foundRemoved) {
+ m_vDescriptors.push_back(pDescriptor);
}
pDescriptor->setCharacteristic(this);
NimBLEDevice::getServer()->serviceChanged();
}
-
/**
* @brief Remove a descriptor from the characteristic.
* @param[in] pDescriptor A pointer to the descriptor instance to remove from the characteristic.
* @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources.
*/
-void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc) {
+void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc) {
// Check if the descriptor was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
- if(pDescriptor->m_removed > 0) {
- if(deleteDsc) {
- for(auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) {
+ if (pDescriptor->getRemoved() > 0) {
+ if (deleteDsc) {
+ for (auto it = m_vDescriptors.begin(); it != m_vDescriptors.end(); ++it) {
if ((*it) == pDescriptor) {
- delete *it;
- m_dscVec.erase(it);
+ delete (*it);
+ m_vDescriptors.erase(it);
break;
}
}
@@ -146,30 +139,28 @@ void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool
return;
}
- pDescriptor->m_removed = deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
+ pDescriptor->setRemoved(deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
NimBLEDevice::getServer()->serviceChanged();
} // removeDescriptor
-
/**
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
-NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
+NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) const {
return getDescriptorByUUID(NimBLEUUID(uuid));
} // getDescriptorByUUID
-
/**
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
-NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
- for (auto &it : m_dscVec) {
- if (it->getUUID() == uuid) {
- return it;
+NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID& uuid) const {
+ for (const auto& dsc : m_vDescriptors) {
+ if (dsc->getUUID() == uuid) {
+ return dsc;
}
}
return nullptr;
@@ -180,351 +171,230 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
* @param [in] handle The handle of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
-NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) {
- for (auto &it : m_dscVec) {
- if (it->getHandle() == handle) {
- return it;
+NimBLEDescriptor* NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) const {
+ for (const auto& dsc : m_vDescriptors) {
+ if (dsc->getHandle() == handle) {
+ return dsc;
}
}
return nullptr;
-}
-
-
-/**
- * @brief Get the handle of the characteristic.
- * @return The handle of the characteristic.
- */
-uint16_t NimBLECharacteristic::getHandle() {
- return m_handle;
-} // getHandle
-
+} // getDescriptorByHandle
/**
* @brief Get the properties of the characteristic.
* @return The properties of the characteristic.
*/
-uint16_t NimBLECharacteristic::getProperties() {
+uint16_t NimBLECharacteristic::getProperties() const {
return m_properties;
} // getProperties
-
/**
- * @brief Get the service associated with this characteristic.
+ * @brief Get the service that owns this characteristic.
*/
-NimBLEService* NimBLECharacteristic::getService() {
+NimBLEService* NimBLECharacteristic::getService() const {
return m_pService;
} // getService
-
-void NimBLECharacteristic::setService(NimBLEService *pService) {
+void NimBLECharacteristic::setService(NimBLEService* pService) {
m_pService = pService;
-}
-
-
-/**
- * @brief Get the UUID of the characteristic.
- * @return The UUID of the characteristic.
- */
-NimBLEUUID NimBLECharacteristic::getUUID() {
- return m_uuid;
-} // getUUID
-
-
-/**
- * @brief Retrieve the current value of the characteristic.
- * @return The NimBLEAttValue containing the current characteristic value.
- */
-NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) {
- if(timestamp != nullptr) {
- m_value.getValue(timestamp);
- }
-
- return m_value;
-} // getValue
-
-
-/**
- * @brief Retrieve the the current data length of the characteristic.
- * @return The length of the current characteristic data.
- */
-size_t NimBLECharacteristic::getDataLength() {
- return m_value.size();
-}
-
-
-/**
- * @brief STATIC callback to handle events from the NimBLE stack.
- */
-int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
- struct ble_gatt_access_ctxt *ctxt,
- void *arg)
-{
- if (conn_handle > BLE_HCI_LE_CONN_HANDLE_MAX)
- {
- NIMBLE_LOGW(LOG_TAG, "Conn_handle (%d) is above the maximum value (%d)", conn_handle, BLE_HCI_LE_CONN_HANDLE_MAX);
- return BLE_ATT_ERR_INVALID_HANDLE;
- }
-
- const ble_uuid_t *uuid;
- int rc;
- NimBLEConnInfo peerInfo;
- NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;
-
- NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
- ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write");
-
- uuid = ctxt->chr->uuid;
- if(ble_uuid_cmp(uuid, pCharacteristic->getUUID().getBase()) == 0){
- switch(ctxt->op) {
- case BLE_GATT_ACCESS_OP_READ_CHR: {
- ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
-
- // If the packet header is only 8 bytes this is a follow up of a long read
- // so we don't want to call the onRead() callback again.
- if(ctxt->om->om_pkthdr_len > 8 ||
- conn_handle == BLE_HS_CONN_HANDLE_NONE ||
- pCharacteristic->m_value.size() <= (ble_att_mtu(peerInfo.m_desc.conn_handle) - 3)) {
- pCharacteristic->m_pCallbacks->onRead(pCharacteristic, peerInfo);
- }
-
- ble_npl_hw_enter_critical();
- rc = os_mbuf_append(ctxt->om, pCharacteristic->m_value.data(), pCharacteristic->m_value.size());
- ble_npl_hw_exit_critical(0);
- return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
- }
-
- case BLE_GATT_ACCESS_OP_WRITE_CHR: {
- uint16_t att_max_len = pCharacteristic->m_value.max_size();
-
- if (ctxt->om->om_len > att_max_len) {
- return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
- }
-
- uint8_t buf[att_max_len];
- size_t len = ctxt->om->om_len;
- memcpy(buf, ctxt->om->om_data,len);
-
- os_mbuf *next;
- next = SLIST_NEXT(ctxt->om, om_next);
- while(next != NULL){
- if((len + next->om_len) > att_max_len) {
- return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
- }
- memcpy(&buf[len], next->om_data, next->om_len);
- len += next->om_len;
- next = SLIST_NEXT(next, om_next);
- }
-
- ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
- pCharacteristic->setValue(buf, len);
- pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, peerInfo);
- return 0;
- }
- default:
- break;
- }
- }
-
- return BLE_ATT_ERR_UNLIKELY;
-}
-
+} // setService
/**
* @brief Get the number of clients subscribed to the characteristic.
* @returns Number of clients subscribed to notifications / indications.
*/
-size_t NimBLECharacteristic::getSubscribedCount() {
+size_t NimBLECharacteristic::getSubscribedCount() const {
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) {
- NimBLEConnInfo peerInfo;
- if(ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc) != 0) {
- return;
- }
-
+void NimBLECharacteristic::setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo) {
uint16_t subVal = 0;
- if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
+ 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)) {
+ if (event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
subVal |= NIMBLE_SUB_INDICATE;
}
- NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
- event->subscribe.conn_handle, subVal);
+ NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", connInfo.getConnHandle(), subVal);
- if(!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
- NimBLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle);
+ if (!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
+ NimBLEDevice::getServer()->clearIndicateWait(connInfo.getConnHandle());
}
-
auto it = m_subscribedVec.begin();
- for(;it != m_subscribedVec.end(); ++it) {
- if((*it).first == event->subscribe.conn_handle) {
+ for (; it != m_subscribedVec.end(); ++it) {
+ if ((*it).first == connInfo.getConnHandle()) {
break;
}
}
- if(subVal > 0) {
- if(it == m_subscribedVec.end()) {
- m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
+ if (subVal > 0) {
+ if (it == m_subscribedVec.end()) {
+ m_subscribedVec.push_back({connInfo.getConnHandle(), subVal});
} else {
(*it).second = subVal;
}
- } else if(it != m_subscribedVec.end()) {
+ } else if (it != m_subscribedVec.end()) {
m_subscribedVec.erase(it);
}
- m_pCallbacks->onSubscribe(this, peerInfo, subVal);
+ m_pCallbacks->onSubscribe(this, connInfo, subVal);
}
-
/**
* @brief Send an indication.
+ * @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
+ * the indication to all subscribed clients.
*/
-void NimBLECharacteristic::indicate() {
- notify(false);
+void NimBLECharacteristic::indicate(uint16_t conn_handle) const {
+ sendValue(m_value.data(), m_value.size(), false, conn_handle);
} // indicate
-
/**
* @brief Send an indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
+ * @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
+ * the indication to all subscribed clients.
*/
-void NimBLECharacteristic::indicate(const uint8_t* value, size_t length) {
- notify(value, length, false);
+void NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_t conn_handle) const {
+ sendValue(value, length, false, conn_handle);
} // indicate
-
/**
* @brief Send an indication.
* @param[in] value A std::vector containing the value to send as the notification value.
+ * @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
+ * the indication to all subscribed clients.
*/
-void NimBLECharacteristic::indicate(const std::vector& value) {
- notify(value.data(), value.size(), false);
+void NimBLECharacteristic::indicate(const std::vector& value, uint16_t conn_handle) const {
+ sendValue(value.data(), value.size(), false, conn_handle);
} // indicate
-
/**
- * @brief Send a notification or indication.
- * @param[in] is_notification if true sends a notification, false sends an indication.
- * @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
+ * @brief Send a notification.
+ * @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
+ * the notification to all subscribed clients.
*/
-void NimBLECharacteristic::notify(bool is_notification, uint16_t conn_handle) {
- notify(m_value.data(), m_value.length(), is_notification, conn_handle);
+void NimBLECharacteristic::notify(uint16_t conn_handle) const {
+ sendValue(m_value.data(), m_value.size(), true, conn_handle);
} // notify
+/**
+ * @brief Send a notification.
+ * @param[in] value A pointer to the data to send.
+ * @param[in] length The length of the data to send.
+ * @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
+ * the notification to all subscribed clients.
+ */
+void NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t conn_handle) const {
+ sendValue(value, length, true, conn_handle);
+} // indicate
/**
- * @brief Send a notification or indication.
+ * @brief Send a notification.
* @param[in] value A std::vector containing the value to send as the notification value.
- * @param[in] is_notification if true sends a notification, false sends an indication.
- * @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
+ * @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
+ * the notification to all subscribed clients.
*/
-void NimBLECharacteristic::notify(const std::vector& value, bool is_notification, uint16_t conn_handle) {
- notify(value.data(), value.size(), is_notification, conn_handle);
+void NimBLECharacteristic::notify(const std::vector& value, uint16_t conn_handle) const {
+ sendValue(value.data(), value.size(), true, conn_handle);
} // notify
-
/**
- * @brief Send a notification or indication.
+ * @brief Sends a notification or indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
* @param[in] is_notification if true sends a notification, false sends an indication.
- * @param[in] conn_handle Connection handle to send individual notification, or BLE_HCI_LE_CONN_HANDLE_MAX + 1 to send notification to all subscribed clients.
+ * @param[in] conn_handle Connection handle to send to a specific peer, or BLE_HS_CONN_HANDLE_NONE to send
+ * to all subscribed clients.
*/
-void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) {
- NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length);
+void NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) const {
+ NIMBLE_LOGD(LOG_TAG, ">> sendValue");
- if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
- !(m_properties & NIMBLE_PROPERTY::INDICATE))
- {
- NIMBLE_LOGE(LOG_TAG,
- "<< notify-Error; Notify/indicate not enabled for characteristic: %s",
- std::string(getUUID()).c_str());
- }
-
- if (m_subscribedVec.size() == 0) {
- NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
+ if (is_notification && !(getProperties() & NIMBLE_PROPERTY::NOTIFY)) {
+ NIMBLE_LOGE(LOG_TAG, "<< sendValue: notification not enabled for characteristic");
return;
}
- m_pCallbacks->onNotify(this);
+ if (!is_notification && !(getProperties() & NIMBLE_PROPERTY::INDICATE)) {
+ NIMBLE_LOGE(LOG_TAG, "<< sendValue: indication not enabled for characteristic");
+ return;
+ }
- bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) ||
- (m_properties & BLE_GATT_CHR_F_READ_AUTHOR) ||
- (m_properties & BLE_GATT_CHR_F_READ_ENC);
- int rc = 0;
+ if (!m_subscribedVec.size()) {
+ NIMBLE_LOGD(LOG_TAG, "<< sendValue: No clients subscribed.");
+ return;
+ }
- for (auto &it : m_subscribedVec) {
- // check if need a specific client
+ for (const auto& it : m_subscribedVec) {
+ // check if connected and subscribed
+ if (!it.second) {
+ continue;
+ }
+
+ // sending to a specific client?
if ((conn_handle <= BLE_HCI_LE_CONN_HANDLE_MAX) && (it.first != conn_handle)) {
continue;
}
- uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first) - 3;
+ if (is_notification && !(it.second & NIMBLE_SUB_NOTIFY)) {
+ continue;
+ }
- // check if connected and subscribed
- if(_mtu == 0 || it.second == 0) {
+ if (!is_notification && !(it.second & NIMBLE_SUB_INDICATE)) {
continue;
}
// check if security requirements are satisfied
- if(reqSec) {
- struct ble_gap_conn_desc desc;
- rc = ble_gap_conn_find(it.first, &desc);
- if(rc != 0 || !desc.sec_state.encrypted) {
+ if ((getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || (getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) ||
+ (getProperties() & BLE_GATT_CHR_F_READ_ENC)) {
+ ble_gap_conn_desc desc;
+ if (ble_gap_conn_find(it.first, &desc) != 0 || !desc.sec_state.encrypted) {
continue;
}
}
- if (length > _mtu) {
- NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu);
- }
-
- 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.second & NIMBLE_SUB_INDICATE))) {
- NIMBLE_LOGW(LOG_TAG,
- "Sending indication to client subscribed to notification, sending notification instead");
- is_notification = true;
- }
-
// don't create the m_buf until we are sure to send the data or else
// we could be allocating a buffer that doesn't get released.
// We also must create it in each loop iteration because it is consumed with each host call.
- os_mbuf *om = ble_hs_mbuf_from_flat(value, length);
+ os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
+ if (!om) {
+ NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
+ return;
+ }
- if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
- if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
- NIMBLE_LOGE(LOG_TAG, "prior Indication in progress");
- os_mbuf_free_chain(om);
- return;
+ if (is_notification) {
+ ble_gattc_notify_custom(it.first, getHandle(), om);
+ } else {
+ if (!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
+ NIMBLE_LOGE(LOG_TAG, "<< sendValue: waiting for previous indicate");
+ os_mbuf_free_chain(om);
+ return;
}
- rc = ble_gattc_indicate_custom(it.first, m_handle, om);
- if(rc != 0){
+ if (ble_gattc_indicate_custom(it.first, getHandle(), om) != 0) {
NimBLEDevice::getServer()->clearIndicateWait(it.first);
}
- } else {
- ble_gattc_notify_custom(it.first, m_handle, om);
}
}
- NIMBLE_LOGD(LOG_TAG, "<< notify");
-} // Notify
+ NIMBLE_LOGD(LOG_TAG, "<< sendValue");
+} // sendValue
+void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) {
+ m_pCallbacks->onRead(this, connInfo);
+}
+
+void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
+ setValue(val, len);
+ m_pCallbacks->onWrite(this, connInfo);
+}
/**
* @brief Set the callback handlers for this characteristic.
@@ -532,60 +402,31 @@ void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_n
* used to define any callbacks for the characteristic.
*/
void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) {
- if (pCallbacks != nullptr){
+ if (pCallbacks != nullptr) {
m_pCallbacks = pCallbacks;
} else {
m_pCallbacks = &defaultCallback;
}
} // setCallbacks
-
/**
* @brief Get the callback handlers for this characteristic.
*/
-NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
+NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() const {
return m_pCallbacks;
-} //getCallbacks
-
-
-/**
- * @brief Set the value of the characteristic from a data buffer .
- * @param [in] data The data buffer to set for the characteristic.
- * @param [in] length The number of bytes in the data buffer.
- */
-void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
-#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
- char* pHex = NimBLEUtils::buildHexData(nullptr, data, length);
- NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s",
- length, pHex, getUUID().toString().c_str());
- free(pHex);
-#endif
-
- m_value.setValue(data, length);
- NIMBLE_LOGD(LOG_TAG, "<< setValue");
-} // setValue
-
-
-/**
- * @brief Set the value of the characteristic from a `std::vector`.\n
- * @param [in] vec The std::vector reference to set the characteristic value from.
- */
-void NimBLECharacteristic::setValue(const std::vector& vec) {
- return setValue((uint8_t*)&vec[0], vec.size());
-}// setValue
-
+} // getCallbacks
/**
* @brief Return a string representation of the characteristic.
* @return A string representation of the characteristic.
*/
-std::string NimBLECharacteristic::toString() {
+std::string NimBLECharacteristic::toString() const {
std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x";
- char hex[5];
- snprintf(hex, sizeof(hex), "%04x", m_handle);
+ char hex[5];
+ snprintf(hex, sizeof(hex), "%04x", getHandle());
res += hex;
res += " ";
- if (m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read ";
+ if (m_properties & BLE_GATT_CHR_PROP_READ) res += "Read ";
if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write ";
if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse ";
if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast ";
@@ -594,7 +435,6 @@ std::string NimBLECharacteristic::toString() {
return res;
} // toString
-
/**
* @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
@@ -604,7 +444,6 @@ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
} // onRead
-
/**
* @brief Callback function to support a write request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
@@ -614,16 +453,6 @@ void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristi
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
} // onWrite
-
-/**
- * @brief Callback function to support a Notify request.
- * @param [in] pCharacteristic The characteristic that is the source of the event.
- */
-void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) {
- NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default");
-} // onNotify
-
-
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
@@ -635,7 +464,6 @@ 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.
@@ -648,8 +476,7 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
*/
void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
NimBLEConnInfo& connInfo,
- uint16_t subValue)
-{
+ uint16_t subValue) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
}
diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h
index 494242c..c43054c 100644
--- a/src/NimBLECharacteristic.h
+++ b/src/NimBLECharacteristic.h
@@ -11,131 +11,71 @@
* Author: kolban
*/
-#ifndef MAIN_NIMBLECHARACTERISTIC_H_
-#define MAIN_NIMBLECHARACTERISTIC_H_
+#ifndef NIMBLE_CPP_CHARACTERISTIC_H_
+#define NIMBLE_CPP_CHARACTERISTIC_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#if defined(CONFIG_NIMBLE_CPP_IDF)
-#include "host/ble_hs.h"
-#else
-#include "nimble/nimble/host/include/host/ble_hs.h"
-#endif
-
-/**** FIX COMPILATION ****/
-#undef min
-#undef max
-/**************************/
-
-typedef enum {
- READ = BLE_GATT_CHR_F_READ,
- READ_ENC = BLE_GATT_CHR_F_READ_ENC,
- READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN,
- READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR,
- WRITE = BLE_GATT_CHR_F_WRITE,
- WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP,
- WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC,
- WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN,
- WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR,
- BROADCAST = BLE_GATT_CHR_F_BROADCAST,
- NOTIFY = BLE_GATT_CHR_F_NOTIFY,
- INDICATE = BLE_GATT_CHR_F_INDICATE
-} NIMBLE_PROPERTY;
-
-#include "NimBLEService.h"
-#include "NimBLEDescriptor.h"
-#include "NimBLEAttValue.h"
-#include "NimBLEConnInfo.h"
-
-#include
-#include
-
-class NimBLEService;
-class NimBLEDescriptor;
class NimBLECharacteristicCallbacks;
+class NimBLECharacteristic;
+# include "NimBLELocalValueAttribute.h"
+# include "NimBLEServer.h"
+# include "NimBLEService.h"
+# include "NimBLEDescriptor.h"
+# include "NimBLEAttValue.h"
+# include "NimBLEConnInfo.h"
+
+# include
+# include
/**
- * @brief The model of a %BLE Characteristic.
+ * @brief The model of a BLE Characteristic.
*
- * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and
- * can be read and written to by a %BLE client.
+ * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE service and
+ * can be read and written to by a BLE client.
*/
-class NimBLECharacteristic {
-public:
- NimBLECharacteristic(const char* uuid,
- uint16_t properties =
- NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
- NimBLEService* pService = nullptr);
- NimBLECharacteristic(const NimBLEUUID &uuid,
- uint16_t properties =
- NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
- NimBLEService* pService = nullptr);
+class NimBLECharacteristic : public NimBLELocalValueAttribute {
+ public:
+ NimBLECharacteristic(const char* uuid,
+ uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
+ NimBLEService* pService = nullptr);
+ NimBLECharacteristic(const NimBLEUUID& uuid,
+ uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
+ NimBLEService* pService = nullptr);
~NimBLECharacteristic();
- uint16_t getHandle();
- NimBLEUUID getUUID();
- std::string toString();
- void indicate();
- void indicate(const uint8_t* value, size_t length);
- void indicate(const std::vector& value);
- void notify(bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1);
- void notify(const uint8_t* value, size_t length, bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1);
- void notify(const std::vector& value, bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1);
- size_t getSubscribedCount();
- void addDescriptor(NimBLEDescriptor *pDescriptor);
- NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
- NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
- NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
- void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false);
- NimBLEService* getService();
- uint16_t getProperties();
- NimBLEAttValue getValue(time_t *timestamp = nullptr);
- size_t getDataLength();
- void setValue(const uint8_t* data, size_t size);
- void setValue(const std::vector& vec);
- void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
+ std::string toString() const;
+ size_t getSubscribedCount() const;
+ void addDescriptor(NimBLEDescriptor* pDescriptor);
+ void removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc = false);
+ uint16_t getProperties() const;
+ void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
+ void indicate(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
+ void indicate(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
+ void indicate(const std::vector& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
+ void notify(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
+ void notify(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
+ void notify(const std::vector& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
+
NimBLEDescriptor* createDescriptor(const char* uuid,
- uint32_t properties =
- NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);;
- NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
- uint32_t properties =
- NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
-
- NimBLECharacteristicCallbacks* getCallbacks();
+ uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
+ NimBLEDescriptor* createDescriptor(const NimBLEUUID& uuid,
+ uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
+ NimBLEDescriptor* getDescriptorByUUID(const char* uuid) const;
+ NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid) const;
+ NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const;
+ NimBLEService* getService() const;
+ NimBLECharacteristicCallbacks* getCallbacks() const;
/*********************** Template Functions ************************/
- /**
- * @brief Template to set the characteristic value to val.
- * @param [in] s The value to set.
- */
- template
- void setValue(const T &s) { m_value.setValue(s); }
-
- /**
- * @brief Template to convert the characteristic data to .
- * @tparam T The type to convert the data to.
- * @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
- * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof().
- * @details Use: getValue(×tamp, skipSizeCheck);
- */
- template
- T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
- return m_value.getValue(timestamp, skipSizeCheck);
- }
-
/**
* @brief Template to send a notification from a class type that has a c_str() and length() method.
* @tparam T The a reference to a class containing the data to send.
@@ -143,14 +83,14 @@ public:
* @param[in] is_notification if true sends a notification, false sends an indication.
* @details Only used if the has a `c_str()` method.
*/
- template
-#ifdef _DOXYGEN_
+ template
+# ifdef _DOXYGEN_
void
-#else
+# else
typename std::enable_if::value, void>::type
-#endif
- notify(const T& value, bool is_notification = true) {
- notify((uint8_t*)value.c_str(), value.length(), is_notification);
+# endif
+ notify(const T& value, bool is_notification = true) const {
+ notify(reinterpret_cast(value.c_str()), value.length(), is_notification);
}
/**
@@ -159,39 +99,35 @@ public:
* @param[in] value The value to set.
* @details Only used if the has a `c_str()` method.
*/
- template
-#ifdef _DOXYGEN_
+ template
+# ifdef _DOXYGEN_
void
-#else
+# else
typename std::enable_if::value, void>::type
-#endif
- indicate(const T& value) {
- indicate((uint8_t*)value.c_str(), value.length());
+# endif
+ indicate(const T& value) const {
+ indicate(reinterpret_cast(value.c_str()), value.length());
}
-private:
+ private:
+ friend class NimBLEServer;
+ friend class NimBLEService;
- friend class NimBLEServer;
- friend class NimBLEService;
+ void setService(NimBLEService* pService);
+ void setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo);
+ void readEvent(NimBLEConnInfo& connInfo) override;
+ void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override;
+ void sendValue(const uint8_t* value,
+ size_t length,
+ bool is_notification = true,
+ uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
- void setService(NimBLEService *pService);
- void setSubscribe(struct ble_gap_event *event);
- static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
- struct ble_gatt_access_ctxt *ctxt, void *arg);
-
- NimBLEUUID m_uuid;
- uint16_t m_handle;
- uint16_t m_properties;
- NimBLECharacteristicCallbacks* m_pCallbacks;
- NimBLEService* m_pService;
- NimBLEAttValue m_value;
- std::vector m_dscVec;
- uint8_t m_removed;
-
- std::vector> m_subscribedVec;
+ NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
+ NimBLEService* m_pService{nullptr};
+ std::vector m_vDescriptors{};
+ std::vector> m_subscribedVec{};
}; // NimBLECharacteristic
-
/**
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
*
@@ -200,14 +136,13 @@ private:
* sub-classed instance of this class and will be notified when such an event happens.
*/
class NimBLECharacteristicCallbacks {
-public:
- virtual ~NimBLECharacteristicCallbacks(){}
+ public:
+ virtual ~NimBLECharacteristicCallbacks() {}
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
- virtual void onNotify(NimBLECharacteristic* pCharacteristic);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code);
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
-#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/
+#endif /*NIMBLE_CPP_CHARACTERISTIC_H_*/
diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp
index 0fbcbb4..9d96fbd 100644
--- a/src/NimBLEClient.cpp
+++ b/src/NimBLEClient.cpp
@@ -15,6 +15,8 @@
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
+#include "NimBLERemoteService.h"
+#include "NimBLERemoteCharacteristic.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
@@ -333,10 +335,10 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
* Called automatically when a characteristic or descriptor requires encryption or authentication to access it.
* @return True on success.
*/
-bool NimBLEClient::secureConnection() {
+bool NimBLEClient::secureConnection() const {
NIMBLE_LOGD(LOG_TAG, ">> secureConnection()");
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
- ble_task_data_t taskData = {this, cur_task, 0, nullptr};
+ ble_task_data_t taskData = {const_cast(this), cur_task, 0, nullptr};
int retryCount = 1;
@@ -542,7 +544,7 @@ void NimBLEClient::setConnectTimeout(uint32_t time) {
* @brief Get the connection id for this client.
* @return The connection id.
*/
-uint16_t NimBLEClient::getConnId() {
+uint16_t NimBLEClient::getConnId() const {
return m_conn_id;
} // getConnId
@@ -610,7 +612,7 @@ bool NimBLEClient::setConnection(uint16_t conn_id) {
/**
* @brief Retrieve the address of the peer.
*/
-NimBLEAddress NimBLEClient::getPeerAddress() {
+NimBLEAddress NimBLEClient::getPeerAddress() const {
return m_peerAddress;
} // getPeerAddress
@@ -776,7 +778,7 @@ bool NimBLEClient::discoverAttributes() {
return false;
}
- for(auto chr: svc->m_characteristicVector) {
+ for(auto chr: svc->m_vChars) {
if (!chr->retrieveDescriptors()) {
return false;
}
@@ -792,7 +794,7 @@ bool NimBLEClient::discoverAttributes() {
* Here we ask the server for its set of services and wait until we have received them all.
* @return true on success otherwise false if an error occurred
*/
-bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
+bool NimBLEClient::retrieveServices(const NimBLEUUID *uuidFilter) {
/**
* Design
* ------
@@ -811,10 +813,10 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
- if(uuid_filter == nullptr) {
+ if(uuidFilter == nullptr) {
rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData);
} else {
- rc = ble_gattc_disc_svc_by_uuid(m_conn_id, uuid_filter->getBase(),
+ rc = ble_gattc_disc_svc_by_uuid(m_conn_id, uuidFilter->getBase(),
NimBLEClient::serviceDiscoveredCB, &taskData);
}
@@ -972,7 +974,7 @@ NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handl
* @brief Get the current mtu of this connection.
* @returns The MTU value.
*/
-uint16_t NimBLEClient::getMTU() {
+uint16_t NimBLEClient::getMTU() const {
return ble_att_mtu(m_conn_id);
} // getMTU
@@ -1086,7 +1088,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
continue;
}
- auto cVector = &it->m_characteristicVector;
+ auto cVector = &it->m_vChars;
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
it->getUUID().toString().c_str(),
event->notify_rx.attr_handle);
@@ -1325,20 +1327,20 @@ bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, con
return true;
}
-void NimBLEClientCallbacks::onPassKeyEntry(const NimBLEConnInfo& connInfo){
+void NimBLEClientCallbacks::onPassKeyEntry(NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyEntry: default: 123456");
NimBLEDevice::injectPassKey(connInfo, 123456);
} //onPassKeyEntry
-void NimBLEClientCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
+void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
}
-void NimBLEClientCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
+void NimBLEClientCallbacks::onIdentity(NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEClientCallbacks", "onIdentity: default");
} // onIdentity
-void NimBLEClientCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
+void NimBLEClientCallbacks::onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pin){
NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true");
NimBLEDevice::injectConfirmPIN(connInfo, true);
}
diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h
index acef487..b571e90 100644
--- a/src/NimBLEClient.h
+++ b/src/NimBLEClient.h
@@ -23,15 +23,14 @@
#include "NimBLEConnInfo.h"
#include "NimBLEAttValue.h"
#include "NimBLEAdvertisedDevice.h"
-#include "NimBLERemoteService.h"
#include
#include
class NimBLERemoteService;
class NimBLERemoteCharacteristic;
-class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice;
+class NimBLEClientCallbacks;
/**
* @brief A model of a %BLE client.
@@ -42,7 +41,7 @@ public:
bool connect(const NimBLEAddress &address, bool deleteAttributes = true);
bool connect(bool deleteAttributes = true);
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
- NimBLEAddress getPeerAddress();
+ NimBLEAddress getPeerAddress() const;
void setPeerAddress(const NimBLEAddress &address);
int getRssi();
std::vector* getServices(bool refresh = false);
@@ -60,12 +59,12 @@ public:
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true);
std::string toString();
- uint16_t getConnId();
+ uint16_t getConnId() const;
void clearConnection();
bool setConnection(NimBLEConnInfo &conn_info);
bool setConnection(uint16_t conn_id);
- uint16_t getMTU();
- bool secureConnection();
+ uint16_t getMTU() const;
+ bool secureConnection() const;
void setConnectTimeout(uint32_t timeout);
void setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
@@ -93,17 +92,17 @@ private:
const struct ble_gatt_svc *service,
void *arg);
static void dcTimerCb(ble_npl_event *event);
- bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
+ bool retrieveServices(const NimBLEUUID *uuidFilter = nullptr);
- NimBLEAddress m_peerAddress;
- int m_lastErr;
- uint16_t m_conn_id;
- bool m_connEstablished;
- bool m_deleteCallbacks;
- int32_t m_connectTimeout;
- NimBLEClientCallbacks* m_pClientCallbacks;
- ble_task_data_t* m_pTaskData;
- ble_npl_callout m_dcTimer;
+ NimBLEAddress m_peerAddress;
+ mutable int m_lastErr;
+ uint16_t m_conn_id;
+ bool m_connEstablished;
+ bool m_deleteCallbacks;
+ int32_t m_connectTimeout;
+ NimBLEClientCallbacks* m_pClientCallbacks;
+ mutable ble_task_data_t* m_pTaskData;
+ ble_npl_callout m_dcTimer;
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t m_phyMask;
#endif
@@ -149,27 +148,27 @@ public:
* @brief Called when server requests a passkey for pairing.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
- virtual void onPassKeyEntry(const NimBLEConnInfo& connInfo);
+ virtual void onPassKeyEntry(NimBLEConnInfo& connInfo);
/**
* @brief Called when the pairing procedure is complete.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n
* This can be used to check the status of the connection encryption/pairing.
*/
- virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
+ virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
/**
* @brief Called when using numeric comparision for pairing.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @param [in] pin The pin to compare with the server.
*/
- virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin);
+ virtual void onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pin);
/**
* @brief Called when the peer identity address is resolved.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
*/
- virtual void onIdentity(const NimBLEConnInfo& connInfo);
+ virtual void onIdentity(NimBLEConnInfo& connInfo);
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
diff --git a/src/NimBLEConnInfo.h b/src/NimBLEConnInfo.h
index 274e6d3..752617a 100644
--- a/src/NimBLEConnInfo.h
+++ b/src/NimBLEConnInfo.h
@@ -1,58 +1,66 @@
#ifndef NIMBLECONNINFO_H_
#define NIMBLECONNINFO_H_
+#if defined(CONFIG_NIMBLE_CPP_IDF)
+# include "host/ble_gap.h"
+#else
+# include "nimble/nimble/host/include/host/ble_gap.h"
+#endif
+
#include "NimBLEAddress.h"
/**
* @brief Connection information.
*/
class NimBLEConnInfo {
-friend class NimBLEServer;
-friend class NimBLEClient;
-friend class NimBLECharacteristic;
-friend class NimBLEDescriptor;
+ public:
+ /** @brief Gets the over-the-air address of the connected peer */
+ NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); }
+
+ /** @brief Gets the ID address of the connected peer */
+ NimBLEAddress getIdAddress() const { return NimBLEAddress(m_desc.peer_id_addr); }
+
+ /** @brief Gets the connection handle (also known as the connection id) of the connected peer */
+ uint16_t getConnHandle() const { return m_desc.conn_handle; }
+
+ /** @brief Gets the connection interval for this connection (in 1.25ms units) */
+ uint16_t getConnInterval() const { return m_desc.conn_itvl; }
+
+ /** @brief Gets the supervision timeout for this connection (in 10ms units) */
+ uint16_t getConnTimeout() const { return m_desc.supervision_timeout; }
+
+ /** @brief Gets the allowable latency for this connection (unit = number of intervals) */
+ uint16_t getConnLatency() const { return m_desc.conn_latency; }
+
+ /** @brief Gets the maximum transmission unit size for this connection (in bytes) */
+ uint16_t getMTU() const { return ble_att_mtu(m_desc.conn_handle); }
+
+ /** @brief Check if we are in the master role in this connection */
+ bool isMaster() const { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
+
+ /** @brief Check if we are in the slave role in this connection */
+ bool isSlave() const { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
+
+ /** @brief Check if we are connected to a bonded peer */
+ bool isBonded() const { return (m_desc.sec_state.bonded == 1); }
+
+ /** @brief Check if the connection in encrypted */
+ bool isEncrypted() const { return (m_desc.sec_state.encrypted == 1); }
+
+ /** @brief Check if the the connection has been authenticated */
+ bool isAuthenticated() const { return (m_desc.sec_state.authenticated == 1); }
+
+ /** @brief Gets the key size used to encrypt the connection */
+ uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; }
+
+ private:
+ friend class NimBLEServer;
+ friend class NimBLEClient;
+ friend class NimBLECharacteristic;
+ friend class NimBLEDescriptor;
ble_gap_conn_desc m_desc{};
NimBLEConnInfo(){};
- NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
-public:
- /** @brief Gets the over-the-air address of the connected peer */
- NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); }
-
- /** @brief Gets the ID address of the connected peer */
- NimBLEAddress getIdAddress() const { return NimBLEAddress(m_desc.peer_id_addr); }
-
- /** @brief Gets the connection handle (also known as the connection id) of the connected peer */
- uint16_t getConnHandle() const { return m_desc.conn_handle; }
-
- /** @brief Gets the connection interval for this connection (in 1.25ms units) */
- uint16_t getConnInterval() const { return m_desc.conn_itvl; }
-
- /** @brief Gets the supervision timeout for this connection (in 10ms units) */
- uint16_t getConnTimeout() const { return m_desc.supervision_timeout; }
-
- /** @brief Gets the allowable latency for this connection (unit = number of intervals) */
- uint16_t getConnLatency() const { return m_desc.conn_latency; }
-
- /** @brief Gets the maximum transmission unit size for this connection (in bytes) */
- uint16_t getMTU() const { return ble_att_mtu(m_desc.conn_handle); }
-
- /** @brief Check if we are in the master role in this connection */
- bool isMaster() const { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
-
- /** @brief Check if we are in the slave role in this connection */
- bool isSlave() const { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
-
- /** @brief Check if we are connected to a bonded peer */
- bool isBonded() const { return (m_desc.sec_state.bonded == 1); }
-
- /** @brief Check if the connection in encrypted */
- bool isEncrypted() const { return (m_desc.sec_state.encrypted == 1); }
-
- /** @brief Check if the the connection has been authenticated */
- bool isAuthenticated() const { return (m_desc.sec_state.authenticated == 1); }
-
- /** @brief Gets the key size used to encrypt the connection */
- uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; }
+ NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
};
#endif
diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp
index c919126..1f0bcad 100644
--- a/src/NimBLEDescriptor.cpp
+++ b/src/NimBLEDescriptor.cpp
@@ -15,17 +15,24 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#include "NimBLEService.h"
-#include "NimBLEDescriptor.h"
-#include "NimBLELog.h"
+# include "NimBLEService.h"
+# include "NimBLEDescriptor.h"
+# include "NimBLELog.h"
-#include
+# include
-#define NULL_HANDLE (0xffff)
-
-static const char* LOG_TAG = "NimBLEDescriptor";
+static const char* LOG_TAG = "NimBLEDescriptor";
static NimBLEDescriptorCallbacks defaultCallbacks;
+/**
+ * @brief Construct a descriptor
+ * @param [in] uuid - UUID (const char*) for the descriptor.
+ * @param [in] properties - Properties for the descriptor.
+ * @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
+ * @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
+ */
+NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic)
+ : NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {}
/**
* @brief Construct a descriptor
@@ -34,239 +41,64 @@ static NimBLEDescriptorCallbacks defaultCallbacks;
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/
-NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len,
- NimBLECharacteristic* pCharacteristic)
-: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {
-}
-
-
-/**
- * @brief Construct a descriptor
- * @param [in] uuid - UUID (const char*) for the descriptor.
- * @param [in] properties - Properties for the descriptor.
- * @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
- * @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
- */
-NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len,
- NimBLECharacteristic* pCharacteristic)
-: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
- m_uuid = uuid;
- m_handle = NULL_HANDLE; // Handle is initially unknown.
- m_pCharacteristic = pCharacteristic;
- m_pCallbacks = &defaultCallbacks; // No initial callback.
- m_properties = 0;
-
+NimBLEDescriptor::NimBLEDescriptor(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic)
+ : NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallbacks}, m_pCharacteristic{pCharacteristic} {
// Check if this is the client configuration descriptor and set to removed if true.
if (uuid == NimBLEUUID((uint16_t)0x2902)) {
NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove.");
- m_removed = 1;
- } else {
- m_removed = 0;
+ setRemoved(NIMBLE_ATT_REMOVE_HIDE);
}
- if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
- m_properties |= BLE_ATT_F_READ;
+ // convert uint16_t properties to uint8_t for descriptor properties
+ uint8_t descProperties = 0;
+ if (properties & NIMBLE_PROPERTY::READ) {
+ descProperties |= BLE_ATT_F_READ;
}
- if (properties & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) {
- m_properties |= BLE_ATT_F_WRITE;
+ if (properties & (NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::WRITE)) {
+ descProperties |= BLE_ATT_F_WRITE;
}
- if (properties & BLE_GATT_CHR_F_READ_ENC) {
- m_properties |= BLE_ATT_F_READ_ENC;
+ if (properties & NIMBLE_PROPERTY::READ_ENC) {
+ descProperties |= BLE_ATT_F_READ_ENC;
}
- if (properties & BLE_GATT_CHR_F_READ_AUTHEN) {
- m_properties |= BLE_ATT_F_READ_AUTHEN;
+ if (properties & NIMBLE_PROPERTY::READ_AUTHEN) {
+ descProperties |= BLE_ATT_F_READ_AUTHEN;
}
- if (properties & BLE_GATT_CHR_F_READ_AUTHOR) {
- m_properties |= BLE_ATT_F_READ_AUTHOR;
+ if (properties & NIMBLE_PROPERTY::READ_AUTHOR) {
+ descProperties |= BLE_ATT_F_READ_AUTHOR;
}
- if (properties & BLE_GATT_CHR_F_WRITE_ENC) {
- m_properties |= BLE_ATT_F_WRITE_ENC;
+ if (properties & NIMBLE_PROPERTY::WRITE_ENC) {
+ descProperties |= BLE_ATT_F_WRITE_ENC;
}
- if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) {
- m_properties |= BLE_ATT_F_WRITE_AUTHEN;
+ if (properties & NIMBLE_PROPERTY::WRITE_AUTHEN) {
+ descProperties |= BLE_ATT_F_WRITE_AUTHEN;
}
- if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) {
- m_properties |= BLE_ATT_F_WRITE_AUTHOR;
+ if (properties & NIMBLE_PROPERTY::WRITE_AUTHOR) {
+ descProperties |= BLE_ATT_F_WRITE_AUTHOR;
}
+ setProperties(descProperties);
} // NimBLEDescriptor
-
-/**
- * @brief NimBLEDescriptor destructor.
- */
-NimBLEDescriptor::~NimBLEDescriptor() {
-} // ~NimBLEDescriptor
-
-/**
- * @brief Get the BLE handle for this descriptor.
- * @return The handle for this descriptor.
- */
-uint16_t NimBLEDescriptor::getHandle() {
- return m_handle;
-} // getHandle
-
-
-/**
- * @brief Get the length of the value of this descriptor.
- * @return The length (in bytes) of the value of this descriptor.
- */
-size_t NimBLEDescriptor::getLength() {
- return m_value.size();
-} // getLength
-
-
-/**
- * @brief Get the UUID of the descriptor.
- */
-NimBLEUUID NimBLEDescriptor::getUUID() {
- return m_uuid;
-} // getUUID
-
-
-/**
- * @brief Get the value of this descriptor.
- * @return The NimBLEAttValue of this descriptor.
- */
-NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) {
- if (timestamp != nullptr) {
- m_value.getValue(timestamp);
- }
-
- return m_value;
-} // getValue
-
-
-/**
- * @brief Get the value of this descriptor as a string.
- * @return A std::string instance containing a copy of the descriptor's value.
- */
-std::string NimBLEDescriptor::getStringValue() {
- return std::string(m_value);
-}
-
-
/**
* @brief Get the characteristic this descriptor belongs to.
* @return A pointer to the characteristic this descriptor belongs to.
*/
-NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() {
+NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() const {
return m_pCharacteristic;
} // getCharacteristic
-
-int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
- struct ble_gatt_access_ctxt *ctxt, void *arg) {
- (void)conn_handle;
- (void)attr_handle;
-
- const ble_uuid_t *uuid;
- int rc;
- NimBLEConnInfo peerInfo{};
- NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
-
- NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(),
- ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write");
-
- uuid = ctxt->chr->uuid;
- if(ble_uuid_cmp(uuid, pDescriptor->getUUID().getBase()) == 0){
- switch(ctxt->op) {
- case BLE_GATT_ACCESS_OP_READ_DSC: {
- ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
-
- // If the packet header is only 8 bytes this is a follow up of a long read
- // so we don't want to call the onRead() callback again.
- if(ctxt->om->om_pkthdr_len > 8 ||
- conn_handle == BLE_HS_CONN_HANDLE_NONE ||
- pDescriptor->m_value.size() <= (ble_att_mtu(peerInfo.getConnHandle()) - 3)) {
- pDescriptor->m_pCallbacks->onRead(pDescriptor, peerInfo);
- }
-
- ble_npl_hw_enter_critical();
- rc = os_mbuf_append(ctxt->om, pDescriptor->m_value.data(), pDescriptor->m_value.size());
- ble_npl_hw_exit_critical(0);
- return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
- }
-
- case BLE_GATT_ACCESS_OP_WRITE_DSC: {
- ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
-
- uint16_t att_max_len = pDescriptor->m_value.max_size();
- if (ctxt->om->om_len > att_max_len) {
- return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
- }
-
- uint8_t buf[att_max_len];
- size_t len = ctxt->om->om_len;
- memcpy(buf, ctxt->om->om_data,len);
- os_mbuf *next;
- next = SLIST_NEXT(ctxt->om, om_next);
- while(next != NULL){
- if((len + next->om_len) > att_max_len) {
- return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
- }
- memcpy(&buf[len], next->om_data, next->om_len);
- len += next->om_len;
- next = SLIST_NEXT(next, om_next);
- }
-
- pDescriptor->setValue(buf, len);
- pDescriptor->m_pCallbacks->onWrite(pDescriptor, peerInfo);
- return 0;
- }
- default:
- break;
- }
- }
-
- return BLE_ATT_ERR_UNLIKELY;
-}
-
/**
* @brief Set the callback handlers for this descriptor.
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
*/
void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) {
- if (pCallbacks != nullptr){
+ if (pCallbacks != nullptr) {
m_pCallbacks = pCallbacks;
} else {
m_pCallbacks = &defaultCallbacks;
}
} // setCallbacks
-
-/**
- * @brief Set the handle of this descriptor.
- * Set the handle of this descriptor to be the supplied value.
- * @param [in] handle The handle to be associated with this descriptor.
- * @return N/A.
- */
-void NimBLEDescriptor::setHandle(uint16_t handle) {
- NIMBLE_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
- m_handle = handle;
- NIMBLE_LOGD(LOG_TAG, "<< setHandle()");
-} // setHandle
-
-
-/**
- * @brief Set the value of the descriptor.
- * @param [in] data The data to set for the descriptor.
- * @param [in] length The length of the data in bytes.
- */
-void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
- m_value.setValue(data, length);
-} // setValue
-
-
-/**
- * @brief Set the value of the descriptor from a `std::vector`.\n
- * @param [in] vec The std::vector reference to set the descriptor value from.
- */
-void NimBLEDescriptor::setValue(const std::vector& vec) {
- return setValue((uint8_t*)&vec[0], vec.size());
-} // setValue
-
-
/**
* @brief Set the characteristic this descriptor belongs to.
* @param [in] pChar A pointer to the characteristic this descriptor belongs to.
@@ -275,18 +107,25 @@ void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) {
m_pCharacteristic = pChar;
} // setCharacteristic
-
/**
* @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor.
*/
-std::string NimBLEDescriptor::toString() {
+std::string NimBLEDescriptor::toString() const {
char hex[5];
- snprintf(hex, sizeof(hex), "%04x", m_handle);
+ snprintf(hex, sizeof(hex), "%04x", getHandle());
std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex;
return res;
} // toString
+void NimBLEDescriptor::readEvent(NimBLEConnInfo& connInfo) {
+ m_pCallbacks->onRead(this, connInfo);
+} // readEvent
+
+void NimBLEDescriptor::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
+ setValue(val, len);
+ m_pCallbacks->onWrite(this, connInfo);
+} // writeEvent
/**
* @brief Callback function to support a read request.
@@ -294,18 +133,15 @@ std::string NimBLEDescriptor::toString() {
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
- (void)pDescriptor;
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default");
} // onRead
-
/**
* @brief Callback function to support a write request.
* @param [in] pDescriptor The descriptor that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
- (void)pDescriptor;
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
} // onWrite
diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h
index e33334c..435e103 100644
--- a/src/NimBLEDescriptor.h
+++ b/src/NimBLEDescriptor.h
@@ -12,94 +12,52 @@
* Author: kolban
*/
-#ifndef MAIN_NIMBLEDESCRIPTOR_H_
-#define MAIN_NIMBLEDESCRIPTOR_H_
+#ifndef NIMBLE_CPP_DESCRIPTOR_H_
+#define NIMBLE_CPP_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#include "NimBLECharacteristic.h"
-#include "NimBLEUUID.h"
-#include "NimBLEAttValue.h"
-#include "NimBLEConnInfo.h"
-
-#include
-
-class NimBLEService;
-class NimBLECharacteristic;
+class NimBLEDescriptor;
class NimBLEDescriptorCallbacks;
+# include "NimBLELocalValueAttribute.h"
+# include "NimBLECharacteristic.h"
+# include "NimBLEUUID.h"
+# include "NimBLEAttValue.h"
+# include "NimBLEConnInfo.h"
+
+# include
/**
- * @brief A model of a %BLE descriptor.
+ * @brief A model of a BLE descriptor.
*/
-class NimBLEDescriptor {
-public:
- NimBLEDescriptor(const char* uuid, uint16_t properties,
- uint16_t max_len,
+class NimBLEDescriptor : public NimBLELocalValueAttribute {
+ public:
+ NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic = nullptr);
+
+ NimBLEDescriptor(const NimBLEUUID& uuid,
+ uint16_t properties,
+ uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
+ ~NimBLEDescriptor() = default;
- NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
- uint16_t max_len,
- NimBLECharacteristic* pCharacteristic = nullptr);
-
- ~NimBLEDescriptor();
-
- uint16_t getHandle();
- NimBLEUUID getUUID();
- std::string toString();
+ std::string toString() const;
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
- NimBLECharacteristic* getCharacteristic();
+ NimBLECharacteristic* getCharacteristic() const;
- size_t getLength();
- NimBLEAttValue getValue(time_t *timestamp = nullptr);
- std::string getStringValue();
-
- void setValue(const uint8_t* data, size_t size);
- void setValue(const std::vector& vec);
-
- /*********************** Template Functions ************************/
-
- /**
- * @brief Template to set the characteristic value to val.
- * @param [in] s The value to set.
- */
- template
- void setValue(const T &s) { m_value.setValue(s); }
-
- /**
- * @brief Template to convert the descriptor data to .
- * @tparam T The type to convert the data to.
- * @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
- * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof().
- * @details Use: getValue(×tamp, skipSizeCheck);
- */
- template
- T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
- return m_value.getValue(timestamp, skipSizeCheck);
- }
-
-private:
+ private:
friend class NimBLECharacteristic;
friend class NimBLEService;
- friend class NimBLE2904;
- static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
- struct ble_gatt_access_ctxt *ctxt, void *arg);
- void setHandle(uint16_t handle);
- void setCharacteristic(NimBLECharacteristic* pChar);
+ void setCharacteristic(NimBLECharacteristic* pChar);
+ void readEvent(NimBLEConnInfo& connInfo) override;
+ void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override;
- NimBLEUUID m_uuid;
- uint16_t m_handle;
- NimBLEDescriptorCallbacks* m_pCallbacks;
- NimBLECharacteristic* m_pCharacteristic;
- uint8_t m_properties;
- NimBLEAttValue m_value;
- uint8_t m_removed;
+ NimBLEDescriptorCallbacks* m_pCallbacks{nullptr};
+ NimBLECharacteristic* m_pCharacteristic{nullptr};
}; // NimBLEDescriptor
-
/**
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
*
@@ -108,13 +66,13 @@ private:
* sub-classed instance of this class and will be notified when such an event happens.
*/
class NimBLEDescriptorCallbacks {
-public:
- virtual ~NimBLEDescriptorCallbacks(){}
+ public:
+ virtual ~NimBLEDescriptorCallbacks() = default;
virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
};
-#include "NimBLE2904.h"
+# include "NimBLE2904.h"
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
-#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */
+#endif /* NIMBLE_CPP_DESCRIPTOR_H_ */
diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp
index 0d8286a..74e3fd1 100644
--- a/src/NimBLEDevice.cpp
+++ b/src/NimBLEDevice.cpp
@@ -54,8 +54,11 @@
# include "esp32-hal-bt.h"
#endif
-#include "NimBLELog.h"
+#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
+#include "NimBLEClient.h"
+#endif
+#include "NimBLELog.h"
#include
static const char* LOG_TAG = "NimBLEDevice";
diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h
index 3f1b625..121df58 100644
--- a/src/NimBLEDevice.h
+++ b/src/NimBLEDevice.h
@@ -31,7 +31,7 @@
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#include "NimBLEClient.h"
+class NimBLEClient;
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@@ -46,6 +46,7 @@
#endif
#include
+#include
#define BLEDevice NimBLEDevice
#define BLEClient NimBLEClient
@@ -236,6 +237,12 @@ private:
#endif
};
+#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
+#include "NimBLEClient.h"
+#include "NimBLERemoteService.h"
+#include "NimBLERemoteCharacteristic.h"
+#include "NimBLERemoteDescriptor.h"
+#endif
#endif // CONFIG_BT_ENABLED
#endif // MAIN_NIMBLEDEVICE_H_
diff --git a/src/NimBLELocalAttribute.h b/src/NimBLELocalAttribute.h
new file mode 100644
index 0000000..8de1328
--- /dev/null
+++ b/src/NimBLELocalAttribute.h
@@ -0,0 +1,48 @@
+/*
+ * NimBLELocalAttribute.cpp
+ *
+ * Created: on July 28 2024
+ * Author H2zero
+ */
+
+#ifndef NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
+#define NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+
+# include "NimBLEAttribute.h"
+
+/**
+ * @brief A base class for local BLE attributes.
+ */
+class NimBLELocalAttribute : public NimBLEAttribute {
+ public:
+ /**
+ * @brief Get the removed flag.
+ * @return The removed flag.
+ */
+ uint8_t getRemoved() const { return m_removed; }
+
+ protected:
+ /**
+ * @brief Construct a local attribute.
+ */
+ NimBLELocalAttribute(const NimBLEUUID& uuid, uint16_t handle) : NimBLEAttribute{uuid, handle}, m_removed{0} {}
+
+ /**
+ * @brief Destroy the local attribute.
+ */
+ ~NimBLELocalAttribute() = default;
+
+ /**
+ * @brief Set the removed flag.
+ * @param [in] removed The removed flag.
+ */
+ void setRemoved(uint8_t removed) { m_removed = removed; }
+
+ uint8_t m_removed{0};
+};
+
+#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#endif // NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
diff --git a/src/NimBLELocalValueAttribute.h b/src/NimBLELocalValueAttribute.h
new file mode 100644
index 0000000..756622f
--- /dev/null
+++ b/src/NimBLELocalValueAttribute.h
@@ -0,0 +1,156 @@
+/*
+ * NimBLELocalValueAttribute.cpp
+ *
+ * Created: on July 28 2024
+ * Author H2zero
+ */
+
+#ifndef NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
+#define NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+
+# if defined(CONFIG_NIMBLE_CPP_IDF)
+# include "host/ble_hs.h"
+# else
+# include "nimble/nimble/host/include/host/ble_hs.h"
+# endif
+
+typedef enum {
+ READ = BLE_GATT_CHR_F_READ,
+ READ_ENC = BLE_GATT_CHR_F_READ_ENC,
+ READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN,
+ READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR,
+ WRITE = BLE_GATT_CHR_F_WRITE,
+ WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP,
+ WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC,
+ WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN,
+ WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR,
+ BROADCAST = BLE_GATT_CHR_F_BROADCAST,
+ NOTIFY = BLE_GATT_CHR_F_NOTIFY,
+ INDICATE = BLE_GATT_CHR_F_INDICATE
+} NIMBLE_PROPERTY;
+
+# include "NimBLELocalAttribute.h"
+# include "NimBLEAttValue.h"
+# include
+class NimBLEConnInfo;
+
+class NimBLELocalValueAttribute : public NimBLELocalAttribute {
+ public:
+ /**
+ * @brief Get the properties of the attribute.
+ */
+ uint16_t getProperties() const { return m_properties; }
+
+ /**
+ * @brief Get the length of the attribute value.
+ * @return The length of the attribute value.
+ */
+ size_t getLength() const { return m_value.size(); }
+
+ /**
+ * @brief Get a copy of the value of the attribute value.
+ * @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
+ * @return A copy of the attribute value.
+ */
+ NimBLEAttValue getValue(time_t* timestamp = nullptr) const { return m_value; }
+
+ /**
+ * @brief Set the value of the attribute value.
+ * @param [in] data The data to set the value to.
+ * @param [in] size The size of the data.
+ */
+ void setValue(const uint8_t* data, size_t size) { m_value.setValue(data, size); }
+
+ /**
+ * @brief Set the value of the attribute value.
+ * @param [in] str The string to set the value to.
+ */
+ void setValue(const char* str) { m_value.setValue(str); }
+
+ /**
+ * @brief Set the value of the attribute value.
+ * @param [in] vec The vector to set the value to.
+ */
+ void setValue(const std::vector& vec) { m_value.setValue(vec); }
+
+ /**
+ * @brief Template to set the value to val.
+ * @param [in] val The value to set.
+ */
+ template
+ void setValue(const T& val) {
+ m_value.setValue(val);
+ }
+
+ /**
+ * @brief Template to convert the data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
+ * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
+ template
+ T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
+ return m_value.getValue(timestamp, skipSizeCheck);
+ }
+
+ protected:
+ friend class NimBLEServer;
+
+ /**
+ * @brief Construct a new NimBLELocalValueAttribute object.
+ * @param [in] uuid The UUID of the attribute.
+ * @param [in] handle The handle of the attribute.
+ * @param [in] maxLen The maximum length of the attribute value.
+ * @param [in] initLen The initial length of the attribute value.
+ */
+ NimBLELocalValueAttribute(const NimBLEUUID& uuid,
+ uint16_t handle,
+ uint16_t maxLen,
+ uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
+ : NimBLELocalAttribute(uuid, handle), m_value(initLen, maxLen) {}
+
+ /**
+ * @brief Destroy the NimBLELocalValueAttribute object.
+ */
+ virtual ~NimBLELocalValueAttribute() = default;
+
+ /**
+ * @brief Callback function to support a read request.
+ * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
+ * @details This function is called by NimBLEServer when a read request is received.
+ */
+ virtual void readEvent(NimBLEConnInfo& connInfo) = 0;
+
+ /**
+ * @brief Callback function to support a write request.
+ * @param [in] val The value to write.
+ * @param [in] len The length of the value.
+ * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
+ * @details This function is called by NimBLEServer when a write request is received.
+ */
+ virtual void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) = 0;
+
+ /**
+ * @brief Get a pointer to value of the attribute.
+ * @return A pointer to the value of the attribute.
+ * @details This function is used by NimBLEServer when handling read/write requests.
+ */
+ const NimBLEAttValue& getAttVal() const { return m_value; }
+
+ /**
+ * @brief Set the properties of the attribute.
+ * @param [in] properties The properties of the attribute.
+ */
+ void setProperties(uint16_t properties) { m_properties = properties; }
+
+ NimBLEAttValue m_value{};
+ uint16_t m_properties{0};
+};
+
+#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#endif // NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp
index 1678858..f6d1207 100644
--- a/src/NimBLERemoteCharacteristic.cpp
+++ b/src/NimBLERemoteCharacteristic.cpp
@@ -15,17 +15,25 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#include "NimBLERemoteCharacteristic.h"
-#include "NimBLEUtils.h"
-#include "NimBLELog.h"
+# include "NimBLERemoteCharacteristic.h"
+# include "NimBLERemoteDescriptor.h"
+# include "NimBLERemoteService.h"
+# include "NimBLEClient.h"
+# include "NimBLEUtils.h"
+# include "NimBLELog.h"
-#include
+# include
+
+typedef struct {
+ const NimBLEUUID* uuid;
+ void* task_data;
+} desc_filter_t;
static const char* LOG_TAG = "NimBLERemoteCharacteristic";
/**
* @brief Constructor.
- * @param [in] reference to the service this characteristic belongs to.
+ * @param [in] svc A pointer to the service this characteristic belongs to.
* @param [in] ble_gatt_chr struct defined as:
* struct ble_gatt_chr {
* uint16_t def_handle;
@@ -34,34 +42,12 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
* ble_uuid_any_t uuid;
* };
*/
- NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
- const struct ble_gatt_chr *chr)
-{
- NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
- switch (chr->uuid.u.type) {
- case BLE_UUID_TYPE_16:
- m_uuid = NimBLEUUID(chr->uuid.u16.value);
- break;
- case BLE_UUID_TYPE_32:
- m_uuid = NimBLEUUID(chr->uuid.u32.value);
- break;
- case BLE_UUID_TYPE_128:
- m_uuid = NimBLEUUID(const_cast(&chr->uuid.u128));
- break;
- default:
- break;
- }
-
- m_handle = chr->val_handle;
- m_defHandle = chr->def_handle;
- m_endHandle = 0;
- m_charProp = chr->properties;
- m_pRemoteService = pRemoteService;
- m_notifyCallback = nullptr;
-
- NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
- } // NimBLERemoteCharacteristic
-
+NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(const NimBLERemoteService* svc, const ble_gatt_chr* chr)
+ : NimBLERemoteValueAttribute{chr->uuid, chr->val_handle},
+ m_pRemoteService{svc},
+ m_properties{chr->properties},
+ m_notifyCallback{},
+ m_vDescriptors{} {} // NimBLERemoteCharacteristic
/**
*@brief Destructor.
@@ -70,266 +56,114 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
deleteDescriptors();
} // ~NimBLERemoteCharacteristic
-/*
-#define BLE_GATT_CHR_PROP_BROADCAST 0x01
-#define BLE_GATT_CHR_PROP_READ 0x02
-#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04
-#define BLE_GATT_CHR_PROP_WRITE 0x08
-#define BLE_GATT_CHR_PROP_NOTIFY 0x10
-#define BLE_GATT_CHR_PROP_INDICATE 0x20
-#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40
-#define BLE_GATT_CHR_PROP_EXTENDED 0x80
-*/
-
-/**
- * @brief Does the characteristic support broadcasting?
- * @return True if the characteristic supports broadcasting.
- */
-bool NimBLERemoteCharacteristic::canBroadcast() {
- return (m_charProp & BLE_GATT_CHR_PROP_BROADCAST) != 0;
-} // canBroadcast
-
-
-/**
- * @brief Does the characteristic support indications?
- * @return True if the characteristic supports indications.
- */
-bool NimBLERemoteCharacteristic::canIndicate() {
- return (m_charProp & BLE_GATT_CHR_PROP_INDICATE) != 0;
-} // canIndicate
-
-
-/**
- * @brief Does the characteristic support notifications?
- * @return True if the characteristic supports notifications.
- */
-bool NimBLERemoteCharacteristic::canNotify() {
- return (m_charProp & BLE_GATT_CHR_PROP_NOTIFY) != 0;
-} // canNotify
-
-
-/**
- * @brief Does the characteristic support reading?
- * @return True if the characteristic supports reading.
- */
-bool NimBLERemoteCharacteristic::canRead() {
- return (m_charProp & BLE_GATT_CHR_PROP_READ) != 0;
-} // canRead
-
-
-/**
- * @brief Does the characteristic support writing?
- * @return True if the characteristic supports writing.
- */
-bool NimBLERemoteCharacteristic::canWrite() {
- return (m_charProp & BLE_GATT_CHR_PROP_WRITE) != 0;
-} // canWrite
-
-
-/**
- * @brief Does the characteristic support writing with no response?
- * @return True if the characteristic supports writing with no response.
- */
-bool NimBLERemoteCharacteristic::canWriteNoResponse() {
- return (m_charProp & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0;
-} // canWriteNoResponse
-
-
/**
* @brief Callback used by the API when a descriptor is discovered or search complete.
*/
-int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- uint16_t chr_val_handle,
- const struct ble_gatt_dsc *dsc,
- void *arg)
-{
+int NimBLERemoteCharacteristic::descriptorDiscCB(
+ uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg) {
int rc = error->status;
- NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d",
- rc, (rc == 0) ? dsc->handle : -1);
+ NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1);
- desc_filter_t *filter = (desc_filter_t*)arg;
- const NimBLEUUID *uuid_filter = filter->uuid;
- ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data;
- NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
+ auto filter = (desc_filter_t*)arg;
+ auto pTaskData = (ble_task_data_t*)filter->task_data;
+ const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->pATT;
+ const NimBLEUUID* uuidFilter = filter->uuid;
- if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
- return 0;
- }
-
- switch (rc) {
- case 0: {
- if (uuid_filter != nullptr) {
- if (ble_uuid_cmp(uuid_filter->getBase(), &dsc->uuid.u) != 0) {
- return 0;
- } else {
- rc = BLE_HS_EDONE;
- }
- }
-
- NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc);
- characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor);
- break;
- }
- default:
- break;
- }
-
- /* If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process.
- * Else if rc == 0, just return 0 to continue the discovery until we get BLE_HS_EDONE.
- * If we get any other error code tell the application to abort by returning non-zero in the rc.
- */
- if (rc == BLE_HS_EDONE) {
- pTaskData->rc = 0;
- xTaskNotifyGive(pTaskData->task);
- } else if(rc != 0) {
- // Error; abort discovery.
- pTaskData->rc = rc;
- xTaskNotifyGive(pTaskData->task);
- }
-
- NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", pTaskData->rc);
- return rc;
-}
-
-
-/**
- * @brief callback from NimBLE when the next characteristic of the service is discovered.
- */
-int NimBLERemoteCharacteristic::nextCharCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- const struct ble_gatt_chr *chr, void *arg)
-{
- int rc = error->status;
- NIMBLE_LOGD(LOG_TAG, "Next Characteristic >> status: %d handle: %d",
- rc, (rc == 0) ? chr->val_handle : -1);
-
- ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
- NimBLERemoteCharacteristic *pChar = (NimBLERemoteCharacteristic*)pTaskData->pATT;
-
- if (pChar->getRemoteService()->getClient()->getConnId() != conn_handle) {
- return 0;
+ if (pChr->getHandle() != chr_val_handle) {
+ rc = BLE_HS_EDONE; // descriptor not for this characteristic
}
if (rc == 0) {
- pChar->m_endHandle = chr->def_handle - 1;
- rc = BLE_HS_EDONE;
- } else if (rc == BLE_HS_EDONE) {
- pChar->m_endHandle = pChar->getRemoteService()->getEndHandle();
- } else {
- pTaskData->rc = rc;
+ if (uuidFilter != nullptr) {
+ if (ble_uuid_cmp(uuidFilter->getBase(), &dsc->uuid.u) == 0) {
+ rc = BLE_HS_EDONE; // Found the descriptor, stop the search
+ } else {
+ return 0; // Not the descriptor we are looking for
+ }
+ }
+
+ pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc));
+ }
+
+ if (rc != 0) {
+ pTaskData->rc = rc == BLE_HS_EDONE ? 0 : rc;
+ NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
+ xTaskNotifyGive(pTaskData->task);
}
- xTaskNotifyGive(pTaskData->task);
return rc;
}
-
/**
* @brief Populate the descriptors (if any) for this characteristic.
* @param [in] the end handle of the characteristic, or the service, whichever comes first.
*/
-bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
+bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
- // If this is the last handle then there are no descriptors
- if (m_handle == getRemoteService()->getEndHandle()) {
- return true;
- }
+ TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
+ ble_task_data_t taskData = {const_cast(this), cur_task, 0, nullptr};
+ desc_filter_t filter = {uuidFilter, &taskData};
- int rc = 0;
- TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
- ble_task_data_t taskData = {this, cur_task, 0, nullptr};
-
- // If we don't know the end handle of this characteristic retrieve the next one in the service
- // The end handle is the next characteristic definition handle -1.
- if (m_endHandle == 0) {
- rc = ble_gattc_disc_all_chrs(getRemoteService()->getClient()->getConnId(),
- m_handle,
+ int rc = ble_gattc_disc_all_dscs(getClient()->getConnId(),
+ getHandle(),
getRemoteService()->getEndHandle(),
- NimBLERemoteCharacteristic::nextCharCB,
- &taskData);
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Error getting end handle rc=%d", rc);
- return false;
- }
-
-#ifdef ulTaskNotifyValueClear
- // Clear the task notification value to ensure we block
- ulTaskNotifyValueClear(cur_task, ULONG_MAX);
-#endif
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
-
- if (taskData.rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Could not retrieve end handle rc=%d", taskData.rc);
- return false;
- }
- }
-
- if (m_handle == m_endHandle) {
- return true;
- }
-
- desc_filter_t filter = {uuid_filter, &taskData};
-
- rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
- m_handle,
- m_endHandle,
- NimBLERemoteCharacteristic::descriptorDiscCB,
- &filter);
-
+ NimBLERemoteCharacteristic::descriptorDiscCB,
+ &filter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
-#ifdef ulTaskNotifyValueClear
+# ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
-#endif
+# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d",
- m_handle, m_endHandle, taskData.rc);
+ NIMBLE_LOGE(LOG_TAG,
+ "<< retrieveDescriptors(): failed: rc=%d %s",
+ taskData.rc,
+ NimBLEUtils::returnCodeToString(taskData.rc));
+ } else {
+ NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size());
}
- NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
- return (taskData.rc == 0);
+ return taskData.rc == 0;
} // retrieveDescriptors
-
/**
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
* @param [in] uuid The UUID of the descriptor to find.
- * @return The Remote descriptor (if present) or null if not present.
+ * @return The Remote descriptor (if present) or nullptr if not present.
*/
-NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID &uuid) {
+NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
+ NimBLERemoteDescriptor* pDsc = nullptr;
+ size_t prev_size = m_vDescriptors.size();
- for(auto &it: m_descriptorVector) {
- if(it->getUUID() == uuid) {
- NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
- return it;
+ for (const auto& it : m_vDescriptors) {
+ if (it->getUUID() == uuid) {
+ pDsc = it;
+ goto Done;
}
}
- size_t prev_size = m_descriptorVector.size();
- if(retrieveDescriptors(&uuid)) {
- if(m_descriptorVector.size() > prev_size) {
- return m_descriptorVector.back();
+ if (retrieveDescriptors(&uuid)) {
+ if (m_vDescriptors.size() > prev_size) {
+ pDsc = m_vDescriptors.back();
+ goto Done;
}
// If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid.
- if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
- uuid.bitSize() == BLE_UUID_TYPE_32)
- {
+ if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
NimBLEUUID uuid128(uuid);
uuid128.to128();
- if(retrieveDescriptors(&uuid128)) {
- if(m_descriptorVector.size() > prev_size) {
- return m_descriptorVector.back();
+ if (retrieveDescriptors(&uuid128)) {
+ if (m_vDescriptors.size() > prev_size) {
+ pDsc = m_vDescriptors.back();
}
}
} else {
@@ -339,20 +173,20 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
- if(retrieveDescriptors(&uuid16)) {
- if(m_descriptorVector.size() > prev_size) {
- return m_descriptorVector.back();
+ if (retrieveDescriptors(&uuid16)) {
+ if (m_vDescriptors.size() > prev_size) {
+ pDsc = m_vDescriptors.back();
}
}
}
}
}
- NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
- return nullptr;
+Done:
+ NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", pDsc ? "" : "not ");
+ return pDsc;
} // getDescriptor
-
/**
* @brief Get a pointer to the vector of found descriptors.
* @param [in] refresh If true the current descriptor vector will be cleared and\n
@@ -361,201 +195,39 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
* of this characteristic.
* @return A pointer to the vector of descriptors for this characteristic.
*/
-std::vector* NimBLERemoteCharacteristic::getDescriptors(bool refresh) {
- if(refresh) {
+const std::vector& NimBLERemoteCharacteristic::getDescriptors(bool refresh) const {
+ if (refresh) {
deleteDescriptors();
-
- if (!retrieveDescriptors()) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors");
- }
- else{
- NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size());
- }
+ retrieveDescriptors();
}
- return &m_descriptorVector;
-} // getDescriptors
+ return m_vDescriptors;
+} // getDescriptors
/**
* @brief Get iterator to the beginning of the vector of remote descriptor pointers.
* @return An iterator to the beginning of the vector of remote descriptor pointers.
*/
-std::vector::iterator NimBLERemoteCharacteristic::begin() {
- return m_descriptorVector.begin();
+std::vector::iterator NimBLERemoteCharacteristic::begin() const {
+ return m_vDescriptors.begin();
}
-
/**
* @brief Get iterator to the end of the vector of remote descriptor pointers.
* @return An iterator to the end of the vector of remote descriptor pointers.
*/
-std::vector::iterator NimBLERemoteCharacteristic::end() {
- return m_descriptorVector.end();
+std::vector::iterator NimBLERemoteCharacteristic::end() const {
+ return m_vDescriptors.end();
}
-
-/**
- * @brief Get the handle for this characteristic.
- * @return The handle for this characteristic.
- */
-uint16_t NimBLERemoteCharacteristic::getHandle() {
- return m_handle;
-} // getHandle
-
-/**
- * @brief Get the handle for this characteristics definition.
- * @return The handle for this characteristic definition.
- */
-uint16_t NimBLERemoteCharacteristic::getDefHandle() {
- return m_defHandle;
-} // getDefHandle
-
-
/**
* @brief Get the remote service associated with this characteristic.
* @return The remote service associated with this characteristic.
*/
-NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() {
+const NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() const {
return m_pRemoteService;
} // getRemoteService
-
-/**
- * @brief Get the UUID for this characteristic.
- * @return The UUID for this characteristic.
- */
-NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
- return m_uuid;
-} // getUUID
-
-
-/**
- * @brief Get the value of the remote characteristic.
- * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
- * @return The value of the remote characteristic.
- */
-NimBLEAttValue NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
- if(timestamp != nullptr) {
- *timestamp = m_value.getTimeStamp();
- }
-
- return m_value;
-} // getValue
-
-
-/**
- * @brief Read the value of the remote characteristic.
- * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
- * @return The value of the remote characteristic.
- */
-NimBLEAttValue NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
- NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x",
- getUUID().toString().c_str(), getHandle(), getHandle());
-
- NimBLEClient* pClient = getRemoteService()->getClient();
- NimBLEAttValue value;
-
- if (!pClient->isConnected()) {
- NIMBLE_LOGE(LOG_TAG, "Disconnected");
- return value;
- }
-
- int rc = 0;
- int retryCount = 1;
- TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
- ble_task_data_t taskData = {this, cur_task, 0, &value};
-
- do {
- rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
- NimBLERemoteCharacteristic::onReadCB,
- &taskData);
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s",
- rc, NimBLEUtils::returnCodeToString(rc));
- return value;
- }
-
-#ifdef ulTaskNotifyValueClear
- // Clear the task notification value to ensure we block
- ulTaskNotifyValueClear(cur_task, ULONG_MAX);
-#endif
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- rc = taskData.rc;
-
- switch(rc){
- case 0:
- case BLE_HS_EDONE:
- rc = 0;
- break;
- // Characteristic is not long-readable, return with what we have.
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
- NIMBLE_LOGI(LOG_TAG, "Attribute not long");
- rc = 0;
- break;
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
- if (retryCount && pClient->secureConnection())
- break;
- /* Else falls through. */
- default:
- NIMBLE_LOGE(LOG_TAG, "<< readValue rc=%d", rc);
- return value;
- }
- } while(rc != 0 && retryCount--);
-
- value.setTimeStamp();
- m_value = value;
- if(timestamp != nullptr) {
- *timestamp = value.getTimeStamp();
- }
-
- NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc);
- return value;
-} // readValue
-
-
-/**
- * @brief Callback for characteristic read operation.
- * @return success == 0 or error code.
- */
-int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg)
-{
- ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
- NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
- uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId();
-
- if(conn_id != conn_handle) {
- return 0;
- }
-
- NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
-
- NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
- int rc = error->status;
-
- if(rc == 0) {
- if(attr) {
- uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
- if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
- rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
- } else {
- NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
- valBuf->append(attr->om->om_data, data_len);
- return 0;
- }
- }
- }
-
- pTaskData->rc = rc;
- xTaskNotifyGive(pTaskData->task);
-
- return rc;
-} // onReadCB
-
-
/**
* @brief Subscribe or unsubscribe for notifications or indications.
* @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
@@ -564,23 +236,20 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* If NULL is provided then no callback is performed.
* @return false if writing to the descriptor failed.
*/
-bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
- NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
-
- m_notifyCallback = notifyCallback;
+bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) const {
+ NIMBLE_LOGD(LOG_TAG, ">> setNotify()");
+ m_notifyCallback = notifyCallback;
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
- if(desc == nullptr) {
+ if (desc == nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
return true;
}
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
-
- return desc->writeValue((uint8_t *)&val, 2, response);
+ return desc->writeValue(reinterpret_cast(&val), 2, response);
} // setNotify
-
/**
* @brief Subscribe for notifications or indications.
* @param [in] notifications If true, subscribe for notifications, false subscribe for indications.
@@ -589,71 +258,127 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
* If NULL is provided then no callback is performed.
* @return false if writing to the descriptor failed.
*/
-bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
- if(notifications) {
- return setNotify(0x01, notifyCallback, response);
- } else {
- return setNotify(0x02, notifyCallback, response);
- }
+bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) const {
+ return setNotify(notifications ? 0x01 : 0x02, notifyCallback, response);
} // subscribe
-
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] response bool if true, require a write response from the descriptor write operation.
* @return false if writing to the descriptor failed.
*/
-bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
+bool NimBLERemoteCharacteristic::unsubscribe(bool response) const {
return setNotify(0x00, nullptr, response);
} // unsubscribe
-
/**
* @brief Delete the descriptors in the descriptor vector.
- * @details We maintain a vector called m_descriptorVector that contains pointers to NimBLERemoteDescriptors
+ * @details We maintain a vector called m_vDescriptors that contains pointers to NimBLERemoteDescriptors
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
*/
-void NimBLERemoteCharacteristic::deleteDescriptors() {
+void NimBLERemoteCharacteristic::deleteDescriptors() const {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
- for(auto &it: m_descriptorVector) {
+ for (const auto& it : m_vDescriptors) {
delete it;
}
- m_descriptorVector.clear();
+ std::vector().swap(m_vDescriptors);
+
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors");
} // deleteDescriptors
-
/**
* @brief Delete descriptor by UUID
* @param [in] uuid The UUID of the descriptor to be deleted.
* @return Number of descriptors left in the vector.
*/
-size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) {
+size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor");
- for(auto it = m_descriptorVector.begin(); it != m_descriptorVector.end(); ++it) {
- if((*it)->getUUID() == uuid) {
- delete *it;
- m_descriptorVector.erase(it);
+ for (auto it = m_vDescriptors.begin(); it != m_vDescriptors.end(); ++it) {
+ if ((*it)->getUUID() == uuid) {
+ delete (*it);
+ m_vDescriptors.erase(it);
break;
}
}
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor");
-
- return m_descriptorVector.size();
+ return m_vDescriptors.size();
} // deleteDescriptor
+/**
+ * @brief Does the characteristic support value broadcasting?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::canBroadcast() const {
+ return (m_properties & BLE_GATT_CHR_PROP_BROADCAST) != 0;
+};
+
+/**
+ * @brief Does the characteristic support reading?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::canRead() const {
+ return (m_properties & BLE_GATT_CHR_PROP_READ);
+};
+
+/**
+ * @brief Does the characteristic support writing without a response?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::canWriteNoResponse() const {
+ return (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP);
+};
+
+/**
+ * @brief Does the characteristic support writing?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::canWrite() const {
+ return (m_properties & BLE_GATT_CHR_PROP_WRITE);
+};
+
+/**
+ * @brief Does the characteristic support reading with encryption?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::canNotify() const {
+ return (m_properties & BLE_GATT_CHR_PROP_NOTIFY);
+};
+
+/**
+ * @brief Does the characteristic support indication?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::canIndicate() const {
+ return (m_properties & BLE_GATT_CHR_PROP_INDICATE);
+};
+
+/**
+ * @brief Does the characteristic support signed writing?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::canWriteSigned() const {
+ return (m_properties & BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE);
+};
+
+/**
+ * @brief Does the characteristic support extended properties?
+ * @return True if supported.
+ */
+bool NimBLERemoteCharacteristic::hasExtendedProps() const {
+ return (m_properties & BLE_GATT_CHR_PROP_EXTENDED);
+};
/**
* @brief Convert a NimBLERemoteCharacteristic to a string representation;
* @return a String representation.
*/
-std::string NimBLERemoteCharacteristic::toString() {
+std::string NimBLERemoteCharacteristic::toString() const {
std::string res = "Characteristic: uuid: " + m_uuid.toString();
- char val[6];
+ char val[6];
res += ", handle: ";
snprintf(val, sizeof(val), "%d", getHandle());
res += val;
@@ -662,145 +387,18 @@ std::string NimBLERemoteCharacteristic::toString() {
res += val;
res += ", props: ";
res += " 0x";
- snprintf(val, sizeof(val), "%02x", m_charProp);
+ snprintf(val, sizeof(val), "%02x", m_properties);
res += val;
- for(auto &it: m_descriptorVector) {
+ for (const auto& it : m_vDescriptors) {
res += "\n" + it->toString();
}
return res;
} // toString
-
-/**
- * @brief Write a new value to the remote characteristic from a std::vector.
- * @param [in] vec A std::vector value to write to the remote characteristic.
- * @param [in] response Whether we require a response from the write.
- * @return false if not connected or otherwise cannot perform write.
- */
-bool NimBLERemoteCharacteristic::writeValue(const std::vector& vec, bool response) {
- return writeValue((uint8_t*)&vec[0], vec.size(), response);
-} // writeValue
-
-
-/**
- * @brief Write a new value to the remote characteristic from a const char*.
- * @param [in] char_s A character string to write to the remote characteristic.
- * @param [in] response Whether we require a response from the write.
- * @return false if not connected or otherwise cannot perform write.
- */
-bool NimBLERemoteCharacteristic::writeValue(const char* char_s, bool response) {
- return writeValue((uint8_t*)char_s, strlen(char_s), response);
-} // writeValue
-
-
-/**
- * @brief Write a new value to the remote characteristic from a data buffer.
- * @param [in] data A pointer to a data buffer.
- * @param [in] length The length of the data in the data buffer.
- * @param [in] response Whether we require a response from the write.
- * @return false if not connected or otherwise cannot perform write.
- */
-bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) {
-
- NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length);
-
- NimBLEClient* pClient = getRemoteService()->getClient();
-
- if (!pClient->isConnected()) {
- NIMBLE_LOGE(LOG_TAG, "Disconnected");
- return false;
- }
-
- int rc = 0;
- int retryCount = 1;
- uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3;
-
- // Check if the data length is longer than we can write in one connection event.
- // If so we must do a long write which requires a response.
- if(length <= mtu && !response) {
- rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
- return (rc==0);
- }
-
- TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
- ble_task_data_t taskData = {this, cur_task, 0, nullptr};
-
- do {
- if(length > mtu) {
- NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
- os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
- rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
- NimBLERemoteCharacteristic::onWriteCB,
- &taskData);
- } else {
- rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
- data, length,
- NimBLERemoteCharacteristic::onWriteCB,
- &taskData);
- }
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc);
- return false;
- }
-
-#ifdef ulTaskNotifyValueClear
- // Clear the task notification value to ensure we block
- ulTaskNotifyValueClear(cur_task, ULONG_MAX);
-#endif
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- rc = taskData.rc;
-
- switch(rc){
- case 0:
- case BLE_HS_EDONE:
- rc = 0;
- break;
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
- NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
- retryCount++;
- length = mtu;
- break;
-
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
- if (retryCount && pClient->secureConnection())
- break;
- /* Else falls through. */
- default:
- NIMBLE_LOGE(LOG_TAG, "<< writeValue, rc: %d", rc);
- return false;
- }
- } while(rc != 0 && retryCount--);
-
- NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc);
- return (rc == 0);
-} // writeValue
-
-
-/**
- * @brief Callback for characteristic write operation.
- * @return success == 0 or error code.
- */
-int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg)
-{
- ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
- NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
-
- if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
- return 0;
- }
-
- NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
-
- pTaskData->rc = error->status;
- xTaskNotifyGive(pTaskData->task);
-
- return 0;
-}
+NimBLEClient* NimBLERemoteCharacteristic::getClient() const {
+ return getRemoteService()->getClient();
+} // getClient
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
diff --git a/src/NimBLERemoteCharacteristic.h b/src/NimBLERemoteCharacteristic.h
index a0f8f55..64756bc 100644
--- a/src/NimBLERemoteCharacteristic.h
+++ b/src/NimBLERemoteCharacteristic.h
@@ -12,169 +12,67 @@
* Author: kolban
*/
-#ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
-#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
+#ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
+#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#include "NimBLERemoteService.h"
-#include "NimBLERemoteDescriptor.h"
-
-#include
-#include
-#include "NimBLELog.h"
+# include "NimBLERemoteValueAttribute.h"
+# include
+# include
class NimBLERemoteService;
class NimBLERemoteDescriptor;
-
-typedef std::function notify_callback;
-
-typedef struct {
- const NimBLEUUID *uuid;
- void *task_data;
-} desc_filter_t;
-
-
/**
- * @brief A model of a remote %BLE characteristic.
+ * @brief A model of a remote BLE characteristic.
*/
-class NimBLERemoteCharacteristic {
-public:
+class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
+ public:
+ std::string toString() const;
+ const NimBLERemoteService* getRemoteService() const;
+ void deleteDescriptors() const;
+ size_t deleteDescriptor(const NimBLEUUID& uuid) const;
+ bool canBroadcast() const;
+ bool canRead() const;
+ bool canWriteNoResponse() const;
+ bool canWrite() const;
+ bool canNotify() const;
+ bool canIndicate() const;
+ bool canWriteSigned() const;
+ bool hasExtendedProps() const;
+ NimBLEClient* getClient() const override;
+
+ typedef std::function notify_callback;
+
+ bool subscribe(bool notifications = true, const notify_callback notifyCallback = nullptr, bool response = true) const;
+ bool unsubscribe(bool response = true) const;
+
+ std::vector::iterator begin() const;
+ std::vector::iterator end() const;
+ NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID& uuid) const;
+ const std::vector& getDescriptors(bool refresh = false) const;
+
+ private:
+ friend class NimBLEClient;
+ friend class NimBLERemoteService;
+
+ NimBLERemoteCharacteristic(const NimBLERemoteService* pRemoteService, const ble_gatt_chr* chr);
~NimBLERemoteCharacteristic();
- // Public member functions
- bool canBroadcast();
- bool canIndicate();
- bool canNotify();
- bool canRead();
- bool canWrite();
- bool canWriteNoResponse();
- std::vector::iterator begin();
- std::vector::iterator end();
- NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid);
- std::vector* getDescriptors(bool refresh = false);
- void deleteDescriptors();
- size_t deleteDescriptor(const NimBLEUUID &uuid);
- uint16_t getHandle();
- uint16_t getDefHandle();
- NimBLEUUID getUUID();
- NimBLEAttValue readValue(time_t *timestamp = nullptr);
- std::string toString();
- NimBLERemoteService* getRemoteService();
- NimBLEAttValue getValue(time_t *timestamp = nullptr);
- bool subscribe(bool notifications = true,
- notify_callback notifyCallback = nullptr,
- bool response = true);
- bool unsubscribe(bool response = true);
- bool writeValue(const uint8_t* data,
- size_t length,
- bool response = false);
- bool writeValue(const std::vector& v, bool response = false);
- bool writeValue(const char* s, bool response = false);
+ bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const;
+ bool retrieveDescriptors(const NimBLEUUID* uuidFilter = nullptr) const;
+ static int descriptorDiscCB(
+ uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg);
- /*********************** Template Functions ************************/
+ const NimBLERemoteService* m_pRemoteService{nullptr};
+ uint8_t m_properties{0};
+ mutable notify_callback m_notifyCallback{nullptr};
+ mutable std::vector m_vDescriptors{};
- /**
- * @brief Template to set the remote characteristic value to val.
- * @param [in] s The value to write.
- * @param [in] response True == request write response.
- * @details Only used for non-arrays and types without a `c_str()` method.
- */
- template
-#ifdef _DOXYGEN_
- bool
-#else
- typename std::enable_if::value && !Has_c_str_len::value, bool>::type
-#endif
- writeValue(const T& s, bool response = false) {
- return writeValue((uint8_t*)&s, sizeof(T), response);
- }
-
- /**
- * @brief Template to set the remote characteristic value to val.
- * @param [in] s The value to write.
- * @param [in] response True == request write response.
- * @details Only used if the has a `c_str()` method.
- */
- template
-#ifdef _DOXYGEN_
- bool
-#else
- typename std::enable_if::value, bool>::type
-#endif
- writeValue(const T& s, bool response = false) {
- return writeValue((uint8_t*)s.c_str(), s.length(), response);
- }
-
- /**
- * @brief Template to convert the remote characteristic data to .
- * @tparam T The type to convert the data to.
- * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
- * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is
- * less than sizeof().
- * @details Use: getValue(×tamp, skipSizeCheck);
- */
- template
- T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
- if(!skipSizeCheck && m_value.size() < sizeof(T)) return T();
- return *((T *)m_value.getValue(timestamp));
- }
-
- /**
- * @brief Template to convert the remote characteristic data to .
- * @tparam T The type to convert the data to.
- * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
- * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is
- * less than sizeof().
- * @details Use: readValue(×tamp, skipSizeCheck);
- */
- template
- T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
- NimBLEAttValue value = readValue();
- if(!skipSizeCheck && value.size() < sizeof(T)) return T();
- return *((T *)value.getValue(timestamp));
- }
-
-private:
-
- NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr);
-
- friend class NimBLEClient;
- friend class NimBLERemoteService;
- friend class NimBLERemoteDescriptor;
-
- // Private member functions
- bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true);
- bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr);
- static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg);
- static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg);
- static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error,
- uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
- void *arg);
- static int nextCharCB(uint16_t conn_handle, const struct ble_gatt_error *error,
- const struct ble_gatt_chr *chr, void *arg);
-
- // Private properties
- NimBLEUUID m_uuid;
- uint8_t m_charProp;
- uint16_t m_handle;
- uint16_t m_defHandle;
- uint16_t m_endHandle;
- NimBLERemoteService* m_pRemoteService;
- NimBLEAttValue m_value;
- notify_callback m_notifyCallback;
-
- // We maintain a vector of descriptors owned by this characteristic.
- std::vector m_descriptorVector;
}; // NimBLERemoteCharacteristic
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
-#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */
+#endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */
diff --git a/src/NimBLERemoteDescriptor.cpp b/src/NimBLERemoteDescriptor.cpp
index b4992f4..cbcc5fb 100644
--- a/src/NimBLERemoteDescriptor.cpp
+++ b/src/NimBLERemoteDescriptor.cpp
@@ -15,183 +15,33 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#include "NimBLERemoteDescriptor.h"
-#include "NimBLEUtils.h"
-#include "NimBLELog.h"
-
-#include
-
-static const char* LOG_TAG = "NimBLERemoteDescriptor";
+# include "NimBLERemoteDescriptor.h"
+# include "NimBLERemoteCharacteristic.h"
/**
* @brief Remote descriptor constructor.
* @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to.
* @param [in] dsc A pointer to the struct that contains the descriptor information.
*/
-NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
- const struct ble_gatt_dsc *dsc)
-{
- NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
- switch (dsc->uuid.u.type) {
- case BLE_UUID_TYPE_16:
- m_uuid = NimBLEUUID(dsc->uuid.u16.value);
- break;
- case BLE_UUID_TYPE_32:
- m_uuid = NimBLEUUID(dsc->uuid.u32.value);
- break;
- case BLE_UUID_TYPE_128:
- m_uuid = NimBLEUUID(const_cast(&dsc->uuid.u128));
- break;
- default:
- break;
- }
-
- m_handle = dsc->handle;
- m_pRemoteCharacteristic = pRemoteCharacteristic;
-
- NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
-}
-
-
-/**
- * @brief Retrieve the handle associated with this remote descriptor.
- * @return The handle associated with this remote descriptor.
- */
-uint16_t NimBLERemoteDescriptor::getHandle() {
- return m_handle;
-} // getHandle
-
+NimBLERemoteDescriptor::NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic,
+ const ble_gatt_dsc* dsc)
+ : NimBLERemoteValueAttribute{dsc->uuid, dsc->handle}, m_pRemoteCharacteristic{pRemoteCharacteristic} {} // NimBLERemoteDescriptor
/**
* @brief Get the characteristic that owns this descriptor.
* @return The characteristic that owns this descriptor.
*/
-NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() {
- return m_pRemoteCharacteristic;
+NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() const {
+ return const_cast(m_pRemoteCharacteristic);
} // getRemoteCharacteristic
-
-/**
- * @brief Retrieve the UUID associated this remote descriptor.
- * @return The UUID associated this remote descriptor.
- */
-NimBLEUUID NimBLERemoteDescriptor::getUUID() {
- return m_uuid;
-} // getUUID
-
-
-/**
- * @brief Read the value of the remote descriptor.
- * @return The value of the remote descriptor.
- */
-NimBLEAttValue NimBLERemoteDescriptor::readValue() {
- NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
-
- NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
- NimBLEAttValue value;
-
- if (!pClient->isConnected()) {
- NIMBLE_LOGE(LOG_TAG, "Disconnected");
- return value;
- }
-
- int rc = 0;
- int retryCount = 1;
- TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
- ble_task_data_t taskData = {this, cur_task, 0, &value};
-
- do {
- rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
- NimBLERemoteDescriptor::onReadCB,
- &taskData);
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to read descriptor; rc=%d, %s",
- rc, NimBLEUtils::returnCodeToString(rc));
- return value;
- }
-
-#ifdef ulTaskNotifyValueClear
- // Clear the task notification value to ensure we block
- ulTaskNotifyValueClear(cur_task, ULONG_MAX);
-#endif
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- rc = taskData.rc;
-
- switch(rc){
- case 0:
- case BLE_HS_EDONE:
- rc = 0;
- break;
- // Descriptor is not long-readable, return with what we have.
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
- NIMBLE_LOGI(LOG_TAG, "Attribute not long");
- rc = 0;
- break;
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
- if (retryCount && pClient->secureConnection())
- break;
- /* Else falls through. */
- default:
- return value;
- }
- } while(rc != 0 && retryCount--);
-
- NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %u rc=%d", value.length(), rc);
- return value;
-} // readValue
-
-
-/**
- * @brief Callback for Descriptor read operation.
- * @return success == 0 or error code.
- */
-int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg)
-{
- (void)attr;
- ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
- NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)pTaskData->pATT;
- uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId();
-
- if(conn_id != conn_handle){
- return 0;
- }
-
- NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
-
- NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
- int rc = error->status;
-
- if(rc == 0) {
- if(attr) {
- uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
- if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
- rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
- } else {
- NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
- valBuf->append(attr->om->om_data, data_len);
- return 0;
- }
- }
- }
-
- pTaskData->rc = rc;
- xTaskNotifyGive(pTaskData->task);
-
- return rc;
-}
-
-
/**
* @brief Return a string representation of this Remote Descriptor.
* @return A string representation of this Remote Descriptor.
*/
-std::string NimBLERemoteDescriptor::toString() {
+std::string NimBLERemoteDescriptor::toString() const {
std::string res = "Descriptor: uuid: " + getUUID().toString();
- char val[6];
+ char val[6];
res += ", handle: ";
snprintf(val, sizeof(val), "%d", getHandle());
res += val;
@@ -199,137 +49,8 @@ std::string NimBLERemoteDescriptor::toString() {
return res;
} // toString
-
-/**
- * @brief Callback for descriptor write operation.
- * @return success == 0 or error code.
- */
-int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg)
-{
- ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
- NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)pTaskData->pATT;
-
- if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){
- return 0;
- }
-
- NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
-
- pTaskData->rc = error->status;
- xTaskNotifyGive(pTaskData->task);
-
- return 0;
+NimBLEClient* NimBLERemoteDescriptor::getClient() const {
+ return m_pRemoteCharacteristic->getClient();
}
-
-/**
- * @brief Write a new value to a remote descriptor from a std::vector.
- * @param [in] vec A std::vector value to write to the remote descriptor.
- * @param [in] response Whether we require a response from the write.
- * @return false if not connected or otherwise cannot perform write.
- */
-bool NimBLERemoteDescriptor::writeValue(const std::vector& vec, bool response) {
- return writeValue((uint8_t*)&vec[0], vec.size(), response);
-} // writeValue
-
-
-/**
- * @brief Write a new value to the remote descriptor from a const char*.
- * @param [in] char_s A character string to write to the remote descriptor.
- * @param [in] response Whether we require a response from the write.
- * @return false if not connected or otherwise cannot perform write.
- */
-bool NimBLERemoteDescriptor::writeValue(const char* char_s, bool response) {
- return writeValue((uint8_t*)char_s, strlen(char_s), response);
-} // writeValue
-
-
-/**
- * @brief Write a new value to a remote descriptor.
- * @param [in] data The data to send to the remote descriptor.
- * @param [in] length The length of the data to send.
- * @param [in] response True if we expect a write response.
- * @return false if not connected or otherwise cannot perform write.
- */
-bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
-
- NIMBLE_LOGD(LOG_TAG, ">> Descriptor writeValue: %s", toString().c_str());
-
- NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
-
- // Check to see that we are connected.
- if (!pClient->isConnected()) {
- NIMBLE_LOGE(LOG_TAG, "Disconnected");
- return false;
- }
-
- int rc = 0;
- int retryCount = 1;
- uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3;
-
- // Check if the data length is longer than we can write in 1 connection event.
- // If so we must do a long write which requires a response.
- if(length <= mtu && !response) {
- rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
- return (rc == 0);
- }
-
- TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
- ble_task_data_t taskData = {this, cur_task, 0, nullptr};
-
- do {
- if(length > mtu) {
- NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
- os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
- rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
- NimBLERemoteDescriptor::onWriteCB,
- &taskData);
- } else {
- rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
- data, length,
- NimBLERemoteDescriptor::onWriteCB,
- &taskData);
- }
-
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc);
- return false;
- }
-
-#ifdef ulTaskNotifyValueClear
- // Clear the task notification value to ensure we block
- ulTaskNotifyValueClear(cur_task, ULONG_MAX);
-#endif
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- rc = taskData.rc;
-
- switch(rc) {
- case 0:
- case BLE_HS_EDONE:
- rc = 0;
- break;
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
- NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
- retryCount++;
- length = mtu;
- break;
-
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
- case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
- if (retryCount && pClient->secureConnection())
- break;
- /* Else falls through. */
- default:
- return false;
- }
- } while(rc != 0 && retryCount--);
-
- NIMBLE_LOGD(LOG_TAG, "<< Descriptor writeValue, rc: %d",rc);
- return (rc == 0);
-} // writeValue
-
-
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
diff --git a/src/NimBLERemoteDescriptor.h b/src/NimBLERemoteDescriptor.h
index 756beb3..ece24b4 100644
--- a/src/NimBLERemoteDescriptor.h
+++ b/src/NimBLERemoteDescriptor.h
@@ -12,93 +12,34 @@
* Author: kolban
*/
-#ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
-#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
+#ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
+#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#include "NimBLERemoteCharacteristic.h"
+# include "NimBLERemoteValueAttribute.h"
class NimBLERemoteCharacteristic;
+class NimBLEClient;
+
/**
- * @brief A model of remote %BLE descriptor.
+ * @brief A model of remote BLE descriptor.
*/
-class NimBLERemoteDescriptor {
-public:
- uint16_t getHandle();
- NimBLERemoteCharacteristic* getRemoteCharacteristic();
- NimBLEUUID getUUID();
- NimBLEAttValue readValue();
- std::string toString(void);
- bool writeValue(const uint8_t* data, size_t length, bool response = false);
- bool writeValue(const std::vector& v, bool response = false);
- bool writeValue(const char* s, bool response = false);
+class NimBLERemoteDescriptor : public NimBLERemoteValueAttribute {
+ public:
+ NimBLERemoteCharacteristic* getRemoteCharacteristic() const;
+ std::string toString(void) const;
+ NimBLEClient* getClient() const override;
+ private:
+ friend class NimBLERemoteCharacteristic;
- /*********************** Template Functions ************************/
+ NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic, const ble_gatt_dsc* dsc);
+ ~NimBLERemoteDescriptor() = default;
- /**
- * @brief Template to set the remote descriptor value to val.
- * @param [in] s The value to write.
- * @param [in] response True == request write response.
- * @details Only used for non-arrays and types without a `c_str()` method.
- */
- template
-#ifdef _DOXYGEN_
- bool
-#else
- typename std::enable_if::value && !Has_c_str_len::value, bool>::type
-#endif
- writeValue(const T& s, bool response = false) {
- return writeValue((uint8_t*)&s, sizeof(T), response);
- }
-
- /**
- * @brief Template to set the remote descriptor value to val.
- * @param [in] s The value to write.
- * @param [in] response True == request write response.
- * @details Only used if the has a `c_str()` method.
- */
- template
-#ifdef _DOXYGEN_
- bool
-#else
- typename std::enable_if::value, bool>::type
-#endif
- writeValue(const T& s, bool response = false) {
- return writeValue((uint8_t*)s.c_str(), s.length(), response);
- }
-
- /**
- * @brief Template to convert the remote descriptor data to .
- * @tparam T The type to convert the data to.
- * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
- * @return The data converted to or NULL if skipSizeCheck is false and the data is
- * less than sizeof().
- * @details Use: readValue(skipSizeCheck);
- */
- template
- T readValue(bool skipSizeCheck = false) {
- NimBLEAttValue value = readValue();
- if(!skipSizeCheck && value.size() < sizeof(T)) return T();
- return *((T *)value.data());
- }
-
-private:
- friend class NimBLERemoteCharacteristic;
-
- NimBLERemoteDescriptor (NimBLERemoteCharacteristic* pRemoteCharacteristic,
- const struct ble_gatt_dsc *dsc);
- static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg);
- static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
- struct ble_gatt_attr *attr, void *arg);
-
- uint16_t m_handle;
- NimBLEUUID m_uuid;
- NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
+ const NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
};
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
-#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */
+#endif /* NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ */
diff --git a/src/NimBLERemoteService.cpp b/src/NimBLERemoteService.cpp
index 51e6244..0a09e8d 100644
--- a/src/NimBLERemoteService.cpp
+++ b/src/NimBLERemoteService.cpp
@@ -15,12 +15,14 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#include "NimBLERemoteService.h"
-#include "NimBLEUtils.h"
-#include "NimBLEDevice.h"
-#include "NimBLELog.h"
+# include "NimBLERemoteService.h"
+# include "NimBLERemoteCharacteristic.h"
+# include "NimBLEClient.h"
+# include "NimBLEAttValue.h"
+# include "NimBLEUtils.h"
+# include "NimBLELog.h"
-#include
+# include
static const char* LOG_TAG = "NimBLERemoteService";
@@ -29,28 +31,8 @@ static const char* LOG_TAG = "NimBLERemoteService";
* @param [in] pClient A pointer to the client this belongs to.
* @param [in] service A pointer to the structure with the service information.
*/
-NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) {
-
- NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()");
- m_pClient = pClient;
- switch (service->uuid.u.type) {
- case BLE_UUID_TYPE_16:
- m_uuid = NimBLEUUID(service->uuid.u16.value);
- break;
- case BLE_UUID_TYPE_32:
- m_uuid = NimBLEUUID(service->uuid.u32.value);
- break;
- case BLE_UUID_TYPE_128:
- m_uuid = NimBLEUUID(const_cast(&service->uuid.u128));
- break;
- default:
- break;
- }
- m_startHandle = service->start_handle;
- m_endHandle = service->end_handle;
- NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
-}
-
+NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const ble_gatt_svc* service)
+ : NimBLEAttribute{service->uuid, service->start_handle}, m_pClient{pClient}, m_endHandle{service->end_handle} {}
/**
* @brief When deleting the service make sure we delete all characteristics and descriptors.
@@ -59,66 +41,62 @@ NimBLERemoteService::~NimBLERemoteService() {
deleteCharacteristics();
}
-
/**
* @brief Get iterator to the beginning of the vector of remote characteristic pointers.
* @return An iterator to the beginning of the vector of remote characteristic pointers.
*/
-std::vector::iterator NimBLERemoteService::begin() {
- return m_characteristicVector.begin();
+std::vector::iterator NimBLERemoteService::begin() const {
+ return m_vChars.begin();
}
-
/**
* @brief Get iterator to the end of the vector of remote characteristic pointers.
* @return An iterator to the end of the vector of remote characteristic pointers.
*/
-std::vector::iterator NimBLERemoteService::end() {
- return m_characteristicVector.end();
+std::vector::iterator NimBLERemoteService::end() const {
+ return m_vChars.end();
}
-
/**
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
* @return A pointer to the remote characteristic object.
*/
-NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) {
+NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) const {
return getCharacteristic(NimBLEUUID(uuid));
} // getCharacteristic
-
/**
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
* @return A pointer to the characteristic object, or nullptr if not found.
*/
-NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
+NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
+ NimBLERemoteCharacteristic* pChar = nullptr;
+ size_t prev_size = m_vChars.size();
- for(auto &it: m_characteristicVector) {
- if(it->getUUID() == uuid) {
- NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
- return it;
+ for (const auto& it : m_vChars) {
+ if (it->getUUID() == uuid) {
+ pChar = it;
+ goto Done;
}
}
- size_t prev_size = m_characteristicVector.size();
- if(retrieveCharacteristics(&uuid)) {
- if(m_characteristicVector.size() > prev_size) {
- return m_characteristicVector.back();
+ if (retrieveCharacteristics(&uuid)) {
+ if (m_vChars.size() > prev_size) {
+ pChar = m_vChars.back();
+ goto Done;
}
// If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid.
- if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
- uuid.bitSize() == BLE_UUID_TYPE_32)
- {
+ if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
NimBLEUUID uuid128(uuid);
uuid128.to128();
if (retrieveCharacteristics(&uuid128)) {
- if(m_characteristicVector.size() > prev_size) {
- return m_characteristicVector.back();
+ if (m_vChars.size() > prev_size) {
+ pChar = m_vChars.back();
}
}
} else {
@@ -128,283 +106,189 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
- if(retrieveCharacteristics(&uuid16)) {
- if(m_characteristicVector.size() > prev_size) {
- return m_characteristicVector.back();
+ if (retrieveCharacteristics(&uuid16)) {
+ if (m_vChars.size() > prev_size) {
+ pChar = m_vChars.back();
}
}
}
}
}
- NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
- return nullptr;
+Done:
+ NIMBLE_LOGD(LOG_TAG, "<< Characteristic %sfound", pChar ? "" : "not ");
+ return pChar;
} // getCharacteristic
-
/**
* @brief Get a pointer to the vector of found characteristics.
* @param [in] refresh If true the current characteristics vector will cleared and
* all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics of this service.
- * @return A pointer to the vector of descriptors for this characteristic.
+ * @return A read-only reference to the vector of characteristics retrieved for this service.
*/
-std::vector* NimBLERemoteService::getCharacteristics(bool refresh) {
- if(refresh) {
+const std::vector& NimBLERemoteService::getCharacteristics(bool refresh) const {
+ if (refresh) {
deleteCharacteristics();
-
- if (!retrieveCharacteristics()) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
- }
- else{
- NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size());
- }
+ retrieveCharacteristics();
}
- return &m_characteristicVector;
-} // getCharacteristics
+ return m_vChars;
+} // getCharacteristics
/**
* @brief Callback for Characteristic discovery.
* @return success == 0 or error code.
*/
-int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- const struct ble_gatt_chr *chr, void *arg)
-{
- NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d",
- error->status, (error->status == 0) ? chr->val_handle : -1);
-
- ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
- NimBLERemoteService *service = (NimBLERemoteService*)pTaskData->pATT;
+int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
+ const ble_gatt_error* error,
+ const ble_gatt_chr* chr,
+ void* arg) {
+ NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>");
+ auto pTaskData = (ble_task_data_t*)arg;
+ const auto pSvc = (NimBLERemoteService*)pTaskData->pATT;
// Make sure the discovery is for this device
- if(service->getClient()->getConnId() != conn_handle){
+ if (pSvc->getClient()->getConnId() != conn_handle) {
return 0;
}
- if(error->status == 0) {
- // Found a service - add it to the vector
- NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr);
- service->m_characteristicVector.push_back(pRemoteCharacteristic);
- return 0;
- }
-
- if(error->status == BLE_HS_EDONE) {
- pTaskData->rc = 0;
+ if (error->status == 0) {
+ pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr));
} else {
- NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s",
- error->status,
- NimBLEUtils::returnCodeToString(error->status));
- pTaskData->rc = error->status;
+ pTaskData->rc = error->status == BLE_HS_EDONE ? 0 : error->status;
+ NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery");
+ xTaskNotifyGive(pTaskData->task);
}
- xTaskNotifyGive(pTaskData->task);
-
- NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered");
return error->status;
}
-
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return True if successful.
*/
-bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) {
- NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
+bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const {
+ NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()");
+ int rc = 0;
+ TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
+ ble_task_data_t taskData = {const_cast(this), cur_task, 0, nullptr};
- int rc = 0;
- TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
- ble_task_data_t taskData = {this, cur_task, 0, nullptr};
-
- if(uuid_filter == nullptr) {
+ if (uuidFilter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
- m_startHandle,
- m_endHandle,
- NimBLERemoteService::characteristicDiscCB,
- &taskData);
+ getHandle(),
+ getEndHandle(),
+ NimBLERemoteService::characteristicDiscCB,
+ &taskData);
} else {
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
- m_startHandle,
- m_endHandle,
- uuid_filter->getBase(),
- NimBLERemoteService::characteristicDiscCB,
- &taskData);
+ getHandle(),
+ getEndHandle(),
+ uuidFilter->getBase(),
+ NimBLERemoteService::characteristicDiscCB,
+ &taskData);
}
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
- return false;
+ if (rc == 0) {
+# ifdef ulTaskNotifyValueClear
+ // Clear the task notification value to ensure we block
+ ulTaskNotifyValueClear(cur_task, ULONG_MAX);
+# endif
+ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
-#ifdef ulTaskNotifyValueClear
- // Clear the task notification value to ensure we block
- ulTaskNotifyValueClear(cur_task, ULONG_MAX);
-#endif
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
-
- if(taskData.rc == 0){
- if (uuid_filter == nullptr) {
- if (m_characteristicVector.size() > 1) {
- for (auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it ) {
- auto nx = std::next(it, 1);
- if (nx == m_characteristicVector.end()) {
- break;
- }
- (*it)->m_endHandle = (*nx)->m_defHandle - 1;
- }
- }
-
- if (m_characteristicVector.size() > 0) {
- m_characteristicVector.back()->m_endHandle = getEndHandle();
- }
- }
-
+ if (taskData.rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "<< retrieveCharacteristics() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ } else {
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
- return true;
}
- NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics");
- return false;
-
+ return taskData.rc == 0;
} // retrieveCharacteristics
-
/**
* @brief Get the client associated with this service.
* @return A reference to the client associated with this service.
*/
-NimBLEClient* NimBLERemoteService::getClient() {
+NimBLEClient* NimBLERemoteService::getClient() const {
return m_pClient;
} // getClient
-
-/**
- * @brief Get the service end handle.
- */
-uint16_t NimBLERemoteService::getEndHandle() {
- return m_endHandle;
-} // getEndHandle
-
-
-/**
- * @brief Get the service start handle.
- */
-uint16_t NimBLERemoteService::getStartHandle() {
- return m_startHandle;
-} // getStartHandle
-
-
-/**
- * @brief Get the service UUID.
- */
-NimBLEUUID NimBLERemoteService::getUUID() {
- return m_uuid;
-}
-
-
/**
* @brief Read the value of a characteristic associated with this service.
- * @param [in] characteristicUuid The characteristic to read.
+ * @param [in] uuid The characteristic to read.
* @returns a string containing the value or an empty string if not found or error.
*/
-std::string NimBLERemoteService::getValue(const NimBLEUUID &characteristicUuid) {
- NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str());
-
- std::string ret = "";
- NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
-
- if(pChar != nullptr) {
- ret = pChar->readValue();
+NimBLEAttValue NimBLERemoteService::getValue(const NimBLEUUID& uuid) const {
+ const auto pChar = getCharacteristic(uuid);
+ if (pChar) {
+ return pChar->readValue();
}
- NIMBLE_LOGD(LOG_TAG, "<< readValue");
- return ret;
+ return NimBLEAttValue{};
} // readValue
-
/**
* @brief Set the value of a characteristic.
- * @param [in] characteristicUuid The characteristic to set.
+ * @param [in] uuid The characteristic UUID to set.
* @param [in] value The value to set.
* @returns true on success, false if not found or error
*/
-bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const std::string &value) {
- NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str());
-
- bool ret = false;
- NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
-
- if(pChar != nullptr) {
- ret = pChar->writeValue(value);
+bool NimBLERemoteService::setValue(const NimBLEUUID& uuid, const NimBLEAttValue& value) const {
+ const auto pChar = getCharacteristic(uuid);
+ if (pChar) {
+ return pChar->writeValue(value);
}
- NIMBLE_LOGD(LOG_TAG, "<< setValue");
- return ret;
+ return false;
} // setValue
-
/**
* @brief Delete the characteristics in the characteristics vector.
* @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
*/
-void NimBLERemoteService::deleteCharacteristics() {
- NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics");
- for(auto &it: m_characteristicVector) {
+void NimBLERemoteService::deleteCharacteristics() const {
+ for (const auto& it : m_vChars) {
delete it;
}
- m_characteristicVector.clear();
- NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics");
+ std::vector{}.swap(m_vChars);
} // deleteCharacteristics
-
/**
* @brief Delete characteristic by UUID
* @param [in] uuid The UUID of the characteristic to be removed from the local database.
* @return Number of characteristics left.
*/
-size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) {
- NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic");
-
- for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) {
- if((*it)->getUUID() == uuid) {
- delete *it;
- m_characteristicVector.erase(it);
+size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID& uuid) const {
+ for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) {
+ if ((*it)->getUUID() == uuid) {
+ delete (*it);
+ m_vChars.erase(it);
break;
}
}
- NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic");
-
- return m_characteristicVector.size();
+ return m_vChars.size();
} // deleteCharacteristic
-
/**
* @brief Create a string representation of this remote service.
* @return A string representation of this remote service.
*/
-std::string NimBLERemoteService::toString() {
- std::string res = "Service: uuid: " + m_uuid.toString();
- char val[6];
- res += ", start_handle: ";
- snprintf(val, sizeof(val), "%d", m_startHandle);
+std::string NimBLERemoteService::toString() const {
+ std::string res = "Service: uuid: " + m_uuid.toString() + ", start_handle: 0x";
+ char val[5];
+ snprintf(val, sizeof(val), "%04x", getHandle());
res += val;
- snprintf(val, sizeof(val), "%04x", m_startHandle);
- res += " 0x";
- res += val;
- res += ", end_handle: ";
- snprintf(val, sizeof(val), "%d", m_endHandle);
- res += val;
- snprintf(val, sizeof(val), "%04x", m_endHandle);
- res += " 0x";
+ res += ", end_handle: 0x";
+ snprintf(val, sizeof(val), "%04x", getEndHandle());
res += val;
- for (auto &it: m_characteristicVector) {
- res += "\n" + it->toString();
+ for (const auto& chr : m_vChars) {
+ res += "\n" + chr->toString();
}
return res;
diff --git a/src/NimBLERemoteService.h b/src/NimBLERemoteService.h
index 0443cfd..a9ae274 100644
--- a/src/NimBLERemoteService.h
+++ b/src/NimBLERemoteService.h
@@ -12,74 +12,54 @@
* Author: kolban
*/
-#ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_
-#define COMPONENTS_NIMBLEREMOTESERVICE_H_
+#ifndef NIMBLE_CPP_REMOTE_SERVICE_H_
+#define NIMBLE_CPP_REMOTE_SERVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-#include "NimBLEClient.h"
-#include "NimBLEUUID.h"
-#include "NimBLERemoteCharacteristic.h"
+# include "NimBLEAttribute.h"
+# include
-#include
-
-class NimBLEClient;
class NimBLERemoteCharacteristic;
-
+class NimBLEClient;
+class NimBLEAttValue;
/**
- * @brief A model of a remote %BLE service.
+ * @brief A model of a remote BLE service.
*/
-class NimBLERemoteService {
-public:
- virtual ~NimBLERemoteService();
+class NimBLERemoteService : public NimBLEAttribute {
+ public:
+ NimBLERemoteCharacteristic* getCharacteristic(const char* uuid) const;
+ NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID& uuid) const;
+ void deleteCharacteristics() const;
+ size_t deleteCharacteristic(const NimBLEUUID& uuid) const;
+ NimBLEClient* getClient(void) const;
+ NimBLEAttValue getValue(const NimBLEUUID& characteristicUuid) const;
+ bool setValue(const NimBLEUUID& characteristicUuid, const NimBLEAttValue& value) const;
+ std::string toString(void) const;
+ uint16_t getStartHandle() const { return getHandle(); }
+ uint16_t getEndHandle() const { return m_endHandle; }
- // Public methods
- std::vector::iterator begin();
- std::vector::iterator end();
- NimBLERemoteCharacteristic* getCharacteristic(const char* uuid);
- NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid);
- void deleteCharacteristics();
- size_t deleteCharacteristic(const NimBLEUUID &uuid);
- NimBLEClient* getClient(void);
- //uint16_t getHandle();
- NimBLEUUID getUUID(void);
- std::string getValue(const NimBLEUUID &characteristicUuid);
- bool setValue(const NimBLEUUID &characteristicUuid,
- const std::string &value);
- std::string toString(void);
- std::vector* getCharacteristics(bool refresh = false);
+ const std::vector& getCharacteristics(bool refresh = false) const;
+ std::vector::iterator begin() const;
+ std::vector::iterator end() const;
-private:
- // Private constructor ... never meant to be created by a user application.
- NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc *service);
-
- // Friends
+ private:
friend class NimBLEClient;
- friend class NimBLERemoteCharacteristic;
- // Private methods
- bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr);
- static int characteristicDiscCB(uint16_t conn_handle,
- const struct ble_gatt_error *error,
- const struct ble_gatt_chr *chr,
- void *arg);
+ NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service);
+ ~NimBLERemoteService();
+ bool retrieveCharacteristics(const NimBLEUUID* uuidFilter = nullptr) const;
+ static int characteristicDiscCB(uint16_t conn_handle,
+ const struct ble_gatt_error* error,
+ const struct ble_gatt_chr* chr,
+ void* arg);
- uint16_t getStartHandle();
- uint16_t getEndHandle();
- void releaseSemaphores();
-
- // Properties
-
- // We maintain a vector of characteristics owned by this service.
- std::vector m_characteristicVector;
-
- NimBLEClient* m_pClient;
- NimBLEUUID m_uuid;
- uint16_t m_startHandle;
- uint16_t m_endHandle;
+ mutable std::vector m_vChars{};
+ NimBLEClient* m_pClient{nullptr};
+ uint16_t m_endHandle{0};
}; // NimBLERemoteService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
-#endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */
+#endif /* NIMBLE_CPP_REMOTE_SERVICE_H_*/
diff --git a/src/NimBLERemoteValueAttribute.cpp b/src/NimBLERemoteValueAttribute.cpp
new file mode 100644
index 0000000..444fa62
--- /dev/null
+++ b/src/NimBLERemoteValueAttribute.cpp
@@ -0,0 +1,212 @@
+/*
+ * NimBLERemoteValueAttribute.cpp
+ *
+ * Created: on July 28 2024
+ * Author H2zero
+ */
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
+
+# include "NimBLERemoteValueAttribute.h"
+# include "NimBLEClient.h"
+
+# include
+
+const char* LOG_TAG = "NimBLERemoteValueAttribute";
+
+bool NimBLERemoteValueAttribute::writeValue(const uint8_t* data, size_t length, bool response) const {
+ NIMBLE_LOGD(LOG_TAG, ">> writeValue()");
+
+ const NimBLEClient* pClient = getClient();
+ TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
+ ble_task_data_t taskData = {const_cast(this), cur_task, 0, nullptr};
+ int retryCount = 1;
+ int rc = 0;
+ uint16_t mtu = pClient->getMTU() - 3;
+
+ // Check if the data length is longer than we can write in one connection event.
+ // If so we must do a long write which requires a response.
+ if (length <= mtu && !response) {
+ rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), getHandle(), data, length);
+ goto Done;
+ }
+
+ do {
+ if (length > mtu) {
+ NIMBLE_LOGI(LOG_TAG, "writeValue: long write");
+ os_mbuf* om = ble_hs_mbuf_from_flat(data, length);
+ rc = ble_gattc_write_long(pClient->getConnId(), getHandle(), 0, om, NimBLERemoteValueAttribute::onWriteCB, &taskData);
+ } else {
+ rc = ble_gattc_write_flat(pClient->getConnId(),
+ getHandle(),
+ data,
+ length,
+ NimBLERemoteValueAttribute::onWriteCB,
+ &taskData);
+ }
+
+ if (rc != 0) {
+ goto Done;
+ }
+
+# ifdef ulTaskNotifyValueClear
+ // Clear the task notification value to ensure we block
+ ulTaskNotifyValueClear(cur_task, ULONG_MAX);
+# endif
+ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+ rc = taskData.rc;
+
+ switch (rc) {
+ case 0:
+ case BLE_HS_EDONE:
+ rc = 0;
+ break;
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
+ NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
+ retryCount++;
+ length = mtu;
+ break;
+
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
+ if (retryCount && pClient->secureConnection()) break;
+ /* Else falls through. */
+ default:
+ goto Done;
+ }
+ } while (rc != 0 && retryCount--);
+
+Done:
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "<< writeValue failed, rc: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ } else {
+ NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc);
+ }
+
+ return (rc == 0);
+} // writeValue
+
+/**
+ * @brief Callback for characteristic write operation.
+ * @return success == 0 or error code.
+ */
+int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) {
+ auto pTaskData = static_cast(arg);
+ const auto pAtt = static_cast(pTaskData->pATT);
+
+ if (pAtt->getClient()->getConnId() != conn_handle) {
+ return 0;
+ }
+
+ NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d", error->status);
+ pTaskData->rc = error->status;
+ xTaskNotifyGive(pTaskData->task);
+ return 0;
+}
+
+/**
+ * @brief Read the value of the remote characteristic.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
+ * @return The value of the remote characteristic.
+ */
+NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) const {
+ NIMBLE_LOGD(LOG_TAG, ">> readValue()");
+
+ NimBLEAttValue value{};
+ const NimBLEClient* pClient = getClient();
+ int rc = 0;
+ int retryCount = 1;
+ TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
+ ble_task_data_t taskData = {const_cast(this), cur_task, 0, &value};
+
+ do {
+ rc = ble_gattc_read_long(pClient->getConnId(), getHandle(), 0, NimBLERemoteValueAttribute::onReadCB, &taskData);
+ if (rc != 0) {
+ goto Done;
+ }
+
+# ifdef ulTaskNotifyValueClear
+ // Clear the task notification value to ensure we block
+ ulTaskNotifyValueClear(cur_task, ULONG_MAX);
+# endif
+ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+ rc = taskData.rc;
+
+ switch (rc) {
+ case 0:
+ case BLE_HS_EDONE:
+ rc = 0;
+ break;
+ // Characteristic is not long-readable, return with what we have.
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
+ NIMBLE_LOGI(LOG_TAG, "Attribute not long");
+ rc = ble_gattc_read(pClient->getConnId(), getHandle(), NimBLERemoteValueAttribute::onReadCB, &taskData);
+ if (rc != 0) {
+ goto Done;
+ }
+ retryCount++;
+ break;
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
+ case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
+ if (retryCount && pClient->secureConnection()) break;
+ /* Else falls through. */
+ default:
+ goto Done;
+ }
+ } while (rc != 0 && retryCount--);
+
+ value.setTimeStamp();
+ m_value = value;
+ if (timestamp != nullptr) {
+ *timestamp = value.getTimeStamp();
+ }
+
+Done:
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "<< readValue failed rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
+ } else {
+ NIMBLE_LOGD(LOG_TAG, "<< readValue rc=%d", rc);
+ }
+
+ return value;
+} // readValue
+
+/**
+ * @brief Callback for characteristic read operation.
+ * @return success == 0 or error code.
+ */
+int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) {
+ auto pTaskData = static_cast(arg);
+ const auto pAtt = static_cast(pTaskData->pATT);
+
+ if (pAtt->getClient()->getConnId() != conn_handle) {
+ return 0;
+ }
+
+ int rc = error->status;
+ NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d", rc);
+
+ if (rc == 0) {
+ if (attr) {
+ auto valBuf = static_cast(pTaskData->buf);
+ uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
+ if ((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
+ rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ } else {
+ NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
+ valBuf->append(attr->om->om_data, data_len);
+ return 0;
+ }
+ }
+ }
+
+ pTaskData->rc = rc;
+ xTaskNotifyGive(pTaskData->task);
+
+ return rc;
+} // onReadCB
+
+#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL
diff --git a/src/NimBLERemoteValueAttribute.h b/src/NimBLERemoteValueAttribute.h
new file mode 100644
index 0000000..6eabb27
--- /dev/null
+++ b/src/NimBLERemoteValueAttribute.h
@@ -0,0 +1,166 @@
+/*
+ * NimBLERemoteValueAttribute.h
+ *
+ * Created: on July 28 2024
+ * Author H2zero
+ */
+
+#ifndef NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
+#define NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
+
+# if defined(CONFIG_NIMBLE_CPP_IDF)
+# include
+# else
+# include
+# endif
+
+/**** FIX COMPILATION ****/
+# undef min
+# undef max
+/**************************/
+
+# include "NimBLEAttribute.h"
+# include "NimBLEAttValue.h"
+
+class NimBLEClient;
+
+class NimBLERemoteValueAttribute : public NimBLEAttribute {
+ public:
+ /**
+ * @brief Read the value of the remote attribute.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
+ * @return The value of the remote attribute.
+ */
+ NimBLEAttValue readValue(time_t* timestamp = nullptr) const;
+
+ /**
+ * @brief Get the length of the remote attribute value.
+ * @return The length of the remote attribute value.
+ */
+ size_t getLength() const { return m_value.size(); }
+
+ /**
+ * @brief Get the value of the remote attribute.
+ * @return The value of the remote attribute.
+ * @details This returns a copy of the value to avoid potential race conditions.
+ */
+ NimBLEAttValue getValue() const { return m_value; }
+
+ /**
+ * Get the client instance that owns this attribute.
+ */
+ virtual NimBLEClient* getClient() const = 0;
+
+ /**
+ * @brief Write a new value to the remote characteristic from a data buffer.
+ * @param [in] data A pointer to a data buffer.
+ * @param [in] length The length of the data in the data buffer.
+ * @param [in] response Whether we require a response from the write.
+ * @return false if not connected or otherwise cannot perform write.
+ */
+ bool writeValue(const uint8_t* data, size_t length, bool response = false) const;
+
+ /**
+ * @brief Write a new value to the remote characteristic from a std::vector.
+ * @param [in] vec A std::vector value to write to the remote characteristic.
+ * @param [in] response Whether we require a response from the write.
+ * @return false if not connected or otherwise cannot perform write.
+ */
+ bool writeValue(const std::vector& v, bool response = false) const {
+ return writeValue(&v[0], v.size(), response);
+ }
+
+ /**
+ * @brief Write a new value to the remote characteristic from a const char*.
+ * @param [in] str A character string to write to the remote characteristic.
+ * @param [in] length (optional) The length of the character string, uses strlen if omitted.
+ * @param [in] response Whether we require a response from the write.
+ * @return false if not connected or otherwise cannot perform write.
+ */
+ bool writeValue(const char* str, size_t length = 0, bool response = false) const {
+ return writeValue(reinterpret_cast(str), length ? length : strlen(str), response);
+ }
+
+ /**
+ * @brief Template to set the remote characteristic value to val.
+ * @param [in] s The value to write.
+ * @param [in] response True == request write response.
+ * @details Only used for non-arrays and types without a `c_str()` method.
+ */
+ template
+# ifdef _DOXYGEN_
+ bool
+# else
+ typename std::enable_if::value && !Has_c_str_len::value, bool>::type
+# endif
+ writeValue(const T& v, bool response = false) const {
+ return writeValue(reinterpret_cast(&v), sizeof(T), response);
+ }
+
+ /**
+ * @brief Template to set the remote characteristic value to val.
+ * @param [in] s The value to write.
+ * @param [in] response True == request write response.
+ * @details Only used if the has a `c_str()` method.
+ */
+ template
+# ifdef _DOXYGEN_
+ bool
+# else
+ typename std::enable_if::value, bool>::type
+# endif
+ writeValue(const T& s, bool response = false) const {
+ return writeValue(reinterpret_cast(s.c_str()), s.length(), response);
+ }
+
+ /**
+ * @brief Template to convert the remote characteristic data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
+ template
+ T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
+ return m_value.getValue(timestamp, skipSizeCheck);
+ }
+
+ /**
+ * @brief Template to convert the remote characteristic data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: readValue(×tamp, skipSizeCheck);
+ */
+ template
+ T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
+ readValue();
+ return m_value.getValue(timestamp, skipSizeCheck);
+ }
+
+ protected:
+ /**
+ * @brief Construct a new NimBLERemoteValueAttribute object.
+ */
+ NimBLERemoteValueAttribute(const ble_uuid_any_t& uuid, uint16_t handle) : NimBLEAttribute(uuid, handle) {}
+
+ /**
+ * @brief Destroy the NimBLERemoteValueAttribute object.
+ */
+ virtual ~NimBLERemoteValueAttribute() = default;
+
+ static int onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);
+ static int onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);
+
+ mutable NimBLEAttValue m_value{};
+};
+
+#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
+#endif // NIMBLE_CPP_REMOTE_VALUE_ATTRIBUTE_H_
diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp
index 072605c..f4c20e6 100644
--- a/src/NimBLEServer.cpp
+++ b/src/NimBLEServer.cpp
@@ -61,7 +61,7 @@ NimBLEServer::NimBLEServer() {
* @brief Destructor: frees all resources / attributes created.
*/
NimBLEServer::~NimBLEServer() {
- for(auto &it : m_svcVec) {
+ for(const auto &it : m_svcVec) {
delete it;
}
@@ -218,7 +218,7 @@ void NimBLEServer::start() {
// Get the assigned service handles and build a vector of characteristics
// with Notify / Indicate capabilities for event handling
for(auto &svc : m_svcVec) {
- if(svc->m_removed == 0) {
+ if(svc->getRemoved() == 0) {
rc = ble_gatts_find_svc(svc->getUUID().getBase(), &svc->m_handle);
if(rc != 0) {
NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s",
@@ -227,13 +227,20 @@ void NimBLEServer::start() {
}
}
- for(auto &chr : svc->m_chrVec) {
+ for(auto &chr : svc->m_vChars) {
// if Notify / Indicate is enabled but we didn't create the descriptor
// we do it now.
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
m_notifyChrVec.push_back(chr);
}
+
+ for (auto &desc : chr->m_vDescriptors) {
+ ble_gatts_find_dsc(svc->getUUID().getBase(),
+ chr->getUUID().getBase(),
+ desc->getUUID().getBase(),
+ &desc->m_handle);
+ }
}
}
@@ -561,7 +568,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
}
}
- it->setSubscribe(event);
+ it->setSubscribe(event, peerInfo);
break;
}
}
@@ -720,6 +727,68 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
} // handleGapEvent
+/**
+ * @brief STATIC callback to handle events from the NimBLE stack.
+ */
+int NimBLEServer::handleGattEvent(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg) {
+ NIMBLE_LOGD(LOG_TAG, "Gatt %s event", (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ||
+ ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write");
+ auto pAtt = static_cast(arg);
+ const auto& val = pAtt->getAttVal();
+ NimBLEConnInfo peerInfo{};
+ ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
+
+ switch(ctxt->op) {
+ case BLE_GATT_ACCESS_OP_READ_DSC:
+ case BLE_GATT_ACCESS_OP_READ_CHR: {
+ // If the packet header is only 8 bytes this is a follow up of a long read
+ // so we don't want to call the onRead() callback again.
+ if(ctxt->om->om_pkthdr_len > 8 ||
+ conn_handle == BLE_HS_CONN_HANDLE_NONE ||
+ val.size() <= (ble_att_mtu(conn_handle) - 3)) {
+ pAtt->readEvent(peerInfo);
+ }
+
+ ble_npl_hw_enter_critical();
+ int rc = os_mbuf_append(ctxt->om, val.data(), val.size());
+ ble_npl_hw_exit_critical(0);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ case BLE_GATT_ACCESS_OP_WRITE_DSC:
+ case BLE_GATT_ACCESS_OP_WRITE_CHR: {
+ uint16_t att_max_len = val.max_size();
+ if (ctxt->om->om_len > att_max_len) {
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+
+ uint8_t buf[att_max_len];
+ uint16_t len = ctxt->om->om_len;
+ memcpy(buf, ctxt->om->om_data,len);
+
+ os_mbuf *next;
+ next = SLIST_NEXT(ctxt->om, om_next);
+ while(next != NULL){
+ if((len + next->om_len) > att_max_len) {
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+ memcpy(&buf[len], next->om_data, next->om_len);
+ len += next->om_len;
+ next = SLIST_NEXT(next, om_next);
+ }
+
+ pAtt->writeEvent(buf, len, peerInfo);
+ return 0;
+ }
+
+ default:
+ break;
+ }
+
+ return BLE_ATT_ERR_UNLIKELY;
+} // handleGattEvent
+
/**
* @brief Set the server callbacks.
*
@@ -762,7 +831,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
// Check if the service was already removed and if so check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
- if(service->m_removed > 0) {
+ if(service->getRemoved() > 0) {
if(deleteSvc) {
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
if ((*it) == service) {
@@ -781,7 +850,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
return;
}
- service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
+ service->setRemoved(deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
serviceChanged();
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
@@ -805,12 +874,12 @@ void NimBLEServer::addService(NimBLEService* service) {
// If adding a service that was not removed add it and return.
// Else reset GATT and send service changed notification.
- if(service->m_removed == 0) {
+ if(service->getRemoved() == 0) {
m_svcVec.push_back(service);
return;
}
- service->m_removed = 0;
+ service->setRemoved(0);
serviceChanged();
}
@@ -829,8 +898,8 @@ void NimBLEServer::resetGATT() {
ble_svc_gatt_init();
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
- if ((*it)->m_removed > 0) {
- if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
+ if ((*it)->getRemoved() > 0) {
+ if ((*it)->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) {
delete *it;
it = m_svcVec.erase(it);
} else {
@@ -1004,20 +1073,20 @@ uint32_t NimBLEServerCallbacks::onPassKeyDisplay(){
return 123456;
} //onPassKeyDisplay
-void NimBLEServerCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
+void NimBLEServerCallbacks::onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pin){
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
NimBLEDevice::injectConfirmPIN(connInfo, true);
} // onConfirmPIN
-void NimBLEServerCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
+void NimBLEServerCallbacks::onIdentity(NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEServerCallbacks", "onIdentity: default");
} // onIdentity
-void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
+void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
} // onAuthenticationComplete
-void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name){
+void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo, const std::string& name){
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
} // onAuthenticationComplete
diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h
index bbb4ebf..251de22 100644
--- a/src/NimBLEServer.h
+++ b/src/NimBLEServer.h
@@ -18,10 +18,8 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#define NIMBLE_ATT_REMOVE_HIDE 1
-#define NIMBLE_ATT_REMOVE_DELETE 2
-
-#define onMtuChanged onMTUChange
+class NimBLEServer;
+class NimBLEServerCallbacks;
#include "NimBLEUtils.h"
#include "NimBLEAddress.h"
@@ -31,13 +29,13 @@
#include "NimBLEAdvertising.h"
#endif
#include "NimBLEService.h"
+#include "NimBLECharacteristic.h"
#include "NimBLEConnInfo.h"
+#define NIMBLE_ATT_REMOVE_HIDE 1
+#define NIMBLE_ATT_REMOVE_DELETE 2
-class NimBLEService;
-class NimBLECharacteristic;
-class NimBLEServerCallbacks;
-
+#define onMtuChanged onMTUChange
/**
* @brief The model of a %BLE server.
@@ -123,6 +121,9 @@ private:
bool setIndicateWait(uint16_t conn_handle);
void clearIndicateWait(uint16_t conn_handle);
+ static int handleGattEvent(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg);
+
}; // NimBLEServer
@@ -182,14 +183,14 @@ public:
* Should be passed back to NimBLEDevice::injectConfirmPIN
* @param [in] pin The pin to compare with the client.
*/
- virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin);
+ virtual void onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pin);
/**
* @brief Called when the pairing procedure is complete.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
* about the peer connection parameters.
*/
- virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
+ virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
/**
* @brief Called when the pairing procedure is complete.
@@ -197,13 +198,13 @@ public:
* @param [in] name The name of the connected peer device.
* about the peer connection parameters.
*/
- virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name);
+ virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo, const std::string& name);
/**
* @brief Called when the peer identity address is resolved.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
*/
- virtual void onIdentity(const NimBLEConnInfo& connInfo);
+ virtual void onIdentity(NimBLEConnInfo& connInfo);
}; // NimBLEServerCallbacks
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp
index 07b1e46..0c915b3 100644
--- a/src/NimBLEService.cpp
+++ b/src/NimBLEService.cpp
@@ -17,91 +17,67 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#include "NimBLEDevice.h"
-#include "NimBLEService.h"
-#include "NimBLEUtils.h"
-#include "NimBLELog.h"
+# include "NimBLEDevice.h"
+# include "NimBLEService.h"
+# include "NimBLEUtils.h"
+# include "NimBLELog.h"
-#include
-
-static const char* LOG_TAG = "NimBLEService"; // Tag for logging.
-
-#define NULL_HANDLE (0xffff)
+# include
+static const char* LOG_TAG = "NimBLEService";
/**
* @brief Construct an instance of the NimBLEService
* @param [in] uuid The UUID of the service.
*/
-NimBLEService::NimBLEService(const char* uuid)
-: NimBLEService(NimBLEUUID(uuid)) {
-}
-
+NimBLEService::NimBLEService(const char* uuid) : NimBLEService(NimBLEUUID(uuid)) {}
/**
* @brief Construct an instance of the BLEService
* @param [in] uuid The UUID of the service.
*/
-NimBLEService::NimBLEService(const NimBLEUUID &uuid) {
- m_uuid = uuid;
- m_handle = NULL_HANDLE;
- m_pSvcDef = nullptr;
- m_removed = 0;
-
-} // NimBLEService
-
+NimBLEService::NimBLEService(const NimBLEUUID& uuid)
+ : NimBLELocalAttribute{uuid, 0}, m_pSvcDef{{0, getUUID().getBase(), nullptr, nullptr},{}} {}
+/**
+ * @brief Destructor, make sure we release the resources allocated for the service.
+ */
NimBLEService::~NimBLEService() {
- if(m_pSvcDef != nullptr) {
- if(m_pSvcDef->characteristics != nullptr) {
- for(int i=0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) {
- if(m_pSvcDef->characteristics[i].descriptors) {
- delete(m_pSvcDef->characteristics[i].descriptors);
- }
- }
- delete(m_pSvcDef->characteristics);
+ if (m_pSvcDef->characteristics) {
+ if (m_pSvcDef->characteristics->descriptors) {
+ delete[] m_pSvcDef->characteristics->descriptors;
}
-
- delete(m_pSvcDef);
+ delete[] m_pSvcDef->characteristics;
}
- for(auto &it : m_chrVec) {
+ for (const auto& it : m_vChars) {
delete it;
}
-}
+} // ~NimBLEService
/**
* @brief Dump details of this BLE GATT service.
*/
-void NimBLEService::dump() {
- NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x",
- m_uuid.toString().c_str(),
- m_handle);
+void NimBLEService::dump() const {
+ NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x", getUUID().toString().c_str(), getHandle());
std::string res;
- int count = 0;
- char hex[5];
- for (auto &it: m_chrVec) {
- if (count > 0) {res += "\n";}
+ int count = 0;
+ char hex[5];
+ for (const auto& it : m_vChars) {
+ if (count > 0) {
+ res += "\n";
+ }
snprintf(hex, sizeof(hex), "%04x", it->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + std::string(it->getUUID());
}
+
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str());
} // dump
-
-/**
- * @brief Get the UUID of the service.
- * @return the UUID of the service.
- */
-NimBLEUUID NimBLEService::getUUID() {
- return m_uuid;
-} // getUUID
-
-
/**
* @brief Builds the database of characteristics/descriptors for the service
* and registers it with the NimBLE stack.
@@ -111,149 +87,99 @@ bool NimBLEService::start() {
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
// Rebuild the service definition if the server attributes have changed.
- if(getServer()->m_svcChanged && m_pSvcDef != nullptr) {
- if(m_pSvcDef[0].characteristics) {
- if(m_pSvcDef[0].characteristics[0].descriptors) {
- delete(m_pSvcDef[0].characteristics[0].descriptors);
+ if (getServer()->m_svcChanged) {
+ if (m_pSvcDef->characteristics) {
+ if (m_pSvcDef->characteristics->descriptors) {
+ delete[] m_pSvcDef->characteristics->descriptors;
}
- delete(m_pSvcDef[0].characteristics);
+ delete[] m_pSvcDef->characteristics;
}
- delete(m_pSvcDef);
- m_pSvcDef = nullptr;
+ m_pSvcDef->type = 0;
}
- if(m_pSvcDef == nullptr) {
- // Nimble requires an array of services to be sent to the api
- // Since we are adding 1 at a time we create an array of 2 and set the type
- // of the second service to 0 to indicate the end of the array.
- ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
- ble_gatt_chr_def* pChr_a = nullptr;
- ble_gatt_dsc_def* pDsc_a = nullptr;
-
- svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY;
- svc[0].uuid = m_uuid.getBase();
- svc[0].includes = NULL;
-
- int removedCount = 0;
- for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ) {
- if ((*it)->m_removed > 0) {
- if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
- delete *it;
- it = m_chrVec.erase(it);
- } else {
- ++removedCount;
- ++it;
- }
+ if (!m_pSvcDef->type) {
+ m_pSvcDef->type = BLE_GATT_SVC_TYPE_PRIMARY;
+ size_t numChrs = 0;
+ for (const auto& chr : m_vChars) {
+ if (chr->getRemoved()) {
continue;
}
-
- ++it;
+ ++numChrs;
}
- size_t numChrs = m_chrVec.size() - removedCount;
- NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
+ NIMBLE_LOGD(LOG_TAG, "Adding %d characteristics for service %s", numChrs, toString().c_str());
+ if (numChrs) {
+ int i = 0;
- if(!numChrs){
- svc[0].characteristics = NULL;
- }else{
// Nimble requires the last characteristic to have it's uuid = 0 to indicate the end
// of the characteristics for the service. We create 1 extra and set it to null
// for this purpose.
- pChr_a = new ble_gatt_chr_def[numChrs + 1]{};
- int i = 0;
- for(auto chr_it = m_chrVec.begin(); chr_it != m_chrVec.end(); ++chr_it) {
- if((*chr_it)->m_removed > 0) {
+ ble_gatt_chr_def* pChrs = new ble_gatt_chr_def[numChrs + 1]{};
+ for (const auto& chr : m_vChars) {
+ if (chr->getRemoved()) {
continue;
}
- removedCount = 0;
- for(auto it = (*chr_it)->m_dscVec.begin(); it != (*chr_it)->m_dscVec.end(); ) {
- if ((*it)->m_removed > 0) {
- if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
- delete *it;
- it = (*chr_it)->m_dscVec.erase(it);
- } else {
- ++removedCount;
- ++it;
- }
+ size_t numDscs = 0;
+ for (const auto& dsc : chr->m_vDescriptors) {
+ if (dsc->getRemoved()) {
continue;
}
-
- ++it;
+ ++numDscs;
}
- size_t numDscs = (*chr_it)->m_dscVec.size() - removedCount;
+ if (numDscs) {
+ int j = 0;
- if(!numDscs){
- pChr_a[i].descriptors = NULL;
- } else {
// Must have last descriptor uuid = 0 so we have to create 1 extra
- pDsc_a = new ble_gatt_dsc_def[numDscs+1];
- int d = 0;
- for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) {
- if((*dsc_it)->m_removed > 0) {
+ ble_gatt_dsc_def* pDscs = new ble_gatt_dsc_def[numDscs + 1]{};
+ for (const auto& dsc : chr->m_vDescriptors) {
+ if (dsc->getRemoved()) {
continue;
}
- pDsc_a[d].uuid = (*dsc_it)->m_uuid.getBase();
- pDsc_a[d].att_flags = (*dsc_it)->m_properties;
- pDsc_a[d].min_key_size = 0;
- pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
- pDsc_a[d].arg = (*dsc_it);
- ++d;
+
+ printf("adding disc %s\n", dsc->toString().c_str());
+
+ pDscs[j].uuid = dsc->getUUID().getBase();
+ pDscs[j].att_flags = dsc->getProperties();
+ pDscs[j].min_key_size = 0;
+ pDscs[j].access_cb = NimBLEServer::handleGattEvent;
+ pDscs[j].arg = dsc;
+ ++j;
}
- pDsc_a[numDscs].uuid = NULL;
- pChr_a[i].descriptors = pDsc_a;
+ pChrs[i].descriptors = pDscs;
}
- pChr_a[i].uuid = (*chr_it)->m_uuid.getBase();
- pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent;
- pChr_a[i].arg = (*chr_it);
- pChr_a[i].flags = (*chr_it)->m_properties;
- pChr_a[i].min_key_size = 0;
- pChr_a[i].val_handle = &(*chr_it)->m_handle;
+ pChrs[i].uuid = chr->getUUID().getBase();
+ pChrs[i].access_cb = NimBLEServer::handleGattEvent;
+ pChrs[i].arg = chr;
+ pChrs[i].flags = chr->getProperties();
+ pChrs[i].min_key_size = 0;
+ pChrs[i].val_handle = &chr->m_handle;
++i;
}
- pChr_a[numChrs].uuid = NULL;
- svc[0].characteristics = pChr_a;
+ m_pSvcDef->characteristics = pChrs;
}
-
- // end of services must indicate to api with type = 0
- svc[1].type = 0;
- m_pSvcDef = svc;
}
- int rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef);
+ int rc = ble_gatts_count_cfg(m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
- rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)m_pSvcDef);
+ rc = ble_gatts_add_svcs(m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
-
}
NIMBLE_LOGD(LOG_TAG, "<< start()");
return true;
} // start
-
-/**
- * @brief Get the handle associated with this service.
- * @return The handle associated with this service.
- */
-uint16_t NimBLEService::getHandle() {
- if (m_handle == NULL_HANDLE) {
- ble_gatts_find_svc(getUUID().getBase(), &m_handle);
- }
- return m_handle;
-} // getHandle
-
-
/**
* @brief Create a new BLE Characteristic associated with this service.
* @param [in] uuid - The UUID of the characteristic.
@@ -263,8 +189,7 @@ uint16_t NimBLEService::getHandle() {
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) {
return createCharacteristic(NimBLEUUID(uuid), properties, max_len);
-}
-
+} // createCharacteristic
/**
* @brief Create a new BLE Characteristic associated with this service.
@@ -273,59 +198,62 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
* @return The new BLE characteristic.
*/
-NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
- NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, max_len, this);
-
+NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) {
+ NimBLECharacteristic* pChar = new NimBLECharacteristic(uuid, properties, max_len, this);
if (getCharacteristic(uuid) != nullptr) {
- NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
- std::string(uuid).c_str());
+ NIMBLE_LOGD(LOG_TAG, "Adding a duplicate characteristic with UUID: %s", std::string(uuid).c_str());
}
- addCharacteristic(pCharacteristic);
- return pCharacteristic;
+ addCharacteristic(pChar);
+ return pChar;
} // createCharacteristic
-
/**
* @brief Add a characteristic to the service.
- * @param[in] pCharacteristic A pointer to the characteristic instance to add to the service.
+ * @param[in] pChar A pointer to the characteristic instance to add to the service.
*/
-void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) {
+void NimBLEService::addCharacteristic(NimBLECharacteristic* pChar) {
bool foundRemoved = false;
-
- if(pCharacteristic->m_removed > 0) {
- for(auto& it : m_chrVec) {
- if(it == pCharacteristic) {
+ if (pChar->getRemoved() > 0) {
+ for (const auto& chr : m_vChars) {
+ if (chr == pChar) {
foundRemoved = true;
- pCharacteristic->m_removed = 0;
+ pChar->setRemoved(0);
}
}
}
- if(!foundRemoved) {
- m_chrVec.push_back(pCharacteristic);
+ // Check if the characteristic is already in the service and if so, return.
+ for (const auto& chr : m_vChars) {
+ if (chr == pChar) {
+ pChar->setService(this); // Update the service pointer in the characteristic.
+ return;
+ }
}
- pCharacteristic->setService(this);
+ if (!foundRemoved) {
+ m_vChars.push_back(pChar);
+ }
+
+ pChar->setService(this);
getServer()->serviceChanged();
} // addCharacteristic
-
/**
* @brief Remove a characteristic from the service.
- * @param[in] pCharacteristic A pointer to the characteristic instance to remove from the service.
+ * @param[in] pChar A pointer to the characteristic instance to remove from the service.
* @param[in] deleteChr If true it will delete the characteristic instance and free it's resources.
*/
-void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr) {
+void NimBLEService::removeCharacteristic(NimBLECharacteristic* pChar, bool deleteChr) {
// Check if the characteristic was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
- if(pCharacteristic->m_removed > 0) {
- if(deleteChr) {
- for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) {
- if ((*it) == pCharacteristic) {
- m_chrVec.erase(it);
- delete *it;
+ if (pChar->getRemoved() > 0) {
+ if (deleteChr) {
+ for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) {
+ if ((*it) == pChar) {
+ delete (*it);
+ m_vChars.erase(it);
break;
}
}
@@ -334,80 +262,82 @@ void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic,
return;
}
- pCharacteristic->m_removed = deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
+ pChar->setRemoved(deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
getServer()->serviceChanged();
} // removeCharacteristic
+/**
+ * @brief Get a pointer to the characteristic object with the specified UUID.
+ * @param [in] uuid The UUID of the characteristic.
+ * @param idx The index of the characteristic to return (used when multiple characteristics have the same UUID).
+ * @return A pointer to the characteristic object or nullptr if not found.
+ */
+NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t idx) const {
+ return getCharacteristic(NimBLEUUID(uuid), idx);
+} // getCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
- * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
+ * @param idx The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found.
*/
-NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) {
- return getCharacteristic(NimBLEUUID(uuid), instanceId);
-}
-
-/**
- * @brief Get a pointer to the characteristic object with the specified UUID.
- * @param [in] uuid The UUID of the characteristic.
- * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
- * @return A pointer to the characteristic object or nullptr if not found.
- */
-NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) {
+NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID& uuid, uint16_t idx) const {
uint16_t position = 0;
- for (auto &it : m_chrVec) {
- if (it->getUUID() == uuid) {
- if (position == instanceId) {
- return it;
+ for (const auto& chr : m_vChars) {
+ if (chr->getUUID() == uuid) {
+ if (position == idx) {
+ return chr;
}
position++;
}
}
+
return nullptr;
-}
+} // getCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified handle.
* @param handle The handle of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
-NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) {
- for (auto &it : m_chrVec) {
- if (it->getHandle() == handle) {
- return it;
+NimBLECharacteristic* NimBLEService::getCharacteristicByHandle(uint16_t handle) const {
+ for (const auto& chr : m_vChars) {
+ if (chr->getHandle() == handle) {
+ return chr;
}
}
+
return nullptr;
-}
+} // getCharacteristicByHandle
/**
* @return A vector containing pointers to each characteristic associated with this service.
*/
-std::vector NimBLEService::getCharacteristics() {
- return m_chrVec;
-}
+const std::vector& NimBLEService::getCharacteristics() const {
+ return m_vChars;
+} // getCharacteristics
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
-std::vector NimBLEService::getCharacteristics(const char *uuid) {
+std::vector NimBLEService::getCharacteristics(const char* uuid) const {
return getCharacteristics(NimBLEUUID(uuid));
-}
+} // getCharacteristics
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
-std::vector NimBLEService::getCharacteristics(const NimBLEUUID &uuid) {
+std::vector NimBLEService::getCharacteristics(const NimBLEUUID& uuid) const {
std::vector result;
- for (auto &it : m_chrVec) {
- if (it->getUUID() == uuid) {
- result.push_back(it);
+ for (const auto& chr : m_vChars) {
+ if (chr->getUUID() == uuid) {
+ result.push_back(chr);
}
}
+
return result;
-}
+} // getCharacteristics
/**
* @brief Return a string representation of this service.
@@ -416,31 +346,29 @@ std::vector NimBLEService::getCharacteristics(const NimB
* * Its handle
* @return A string representation of this service.
*/
-std::string NimBLEService::toString() {
+std::string NimBLEService::toString() const {
std::string res = "UUID: " + getUUID().toString();
- char hex[5];
+ char hex[5];
snprintf(hex, sizeof(hex), "%04x", getHandle());
res += ", handle: 0x";
res += hex;
return res;
} // toString
-
/**
* @brief Get the BLE server associated with this service.
* @return The BLEServer associated with this service.
*/
-NimBLEServer* NimBLEService::getServer() {
+NimBLEServer* NimBLEService::getServer() const {
return NimBLEDevice::getServer();
-}// getServer
-
+} // getServer
/**
* @brief Checks if the service has been started.
* @return True if the service has been started.
*/
-bool NimBLEService::isStarted() {
- return m_pSvcDef != nullptr;
-}
+bool NimBLEService::isStarted() const {
+ return m_pSvcDef->type > 0;
+} // isStarted
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
diff --git a/src/NimBLEService.h b/src/NimBLEService.h
index 73cbd87..13bad3f 100644
--- a/src/NimBLEService.h
+++ b/src/NimBLEService.h
@@ -12,76 +12,59 @@
* Author: kolban
*/
-#ifndef MAIN_NIMBLESERVICE_H_
-#define MAIN_NIMBLESERVICE_H_
+#ifndef NIMBLE_CPP_SERVICE_H_
+#define NIMBLE_CPP_SERVICE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#include "NimBLEServer.h"
-#include "NimBLECharacteristic.h"
-#include "NimBLEUUID.h"
-
-
-class NimBLEServer;
-class NimBLECharacteristic;
+class NimBLEService;
+# include "NimBLEAttribute.h"
+# include "NimBLEServer.h"
+# include "NimBLECharacteristic.h"
/**
- * @brief The model of a %BLE service.
+ * @brief The model of a BLE service.
*
*/
-class NimBLEService {
-public:
-
+class NimBLEService : public NimBLELocalAttribute {
+ public:
NimBLEService(const char* uuid);
- NimBLEService(const NimBLEUUID &uuid);
+ NimBLEService(const NimBLEUUID& uuid);
~NimBLEService();
- NimBLEServer* getServer();
-
- NimBLEUUID getUUID();
- uint16_t getHandle();
- std::string toString();
- void dump();
- bool isStarted();
+ NimBLEServer* getServer() const;
+ std::string toString() const;
+ void dump() const;
+ bool isStarted() const;
bool start();
-
NimBLECharacteristic* createCharacteristic(const char* uuid,
- uint32_t properties =
- NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
-
- NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
- uint32_t properties =
- NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
+ uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
+ NimBLECharacteristic* createCharacteristic(const NimBLEUUID& uuid,
+ uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);
- NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0);
- NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0);
- NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);
+ NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0) const;
+ NimBLECharacteristic* getCharacteristic(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
+ NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle) const;
- std::vector getCharacteristics();
- std::vector getCharacteristics(const char* uuid);
- std::vector getCharacteristics(const NimBLEUUID &uuid);
+ const std::vector& getCharacteristics() const;
+ std::vector getCharacteristics(const char* uuid) const;
+ std::vector getCharacteristics(const NimBLEUUID& uuid) const;
+ private:
+ friend class NimBLEServer;
-private:
-
- friend class NimBLEServer;
- friend class NimBLEDevice;
-
- uint16_t m_handle;
- NimBLEUUID m_uuid;
- ble_gatt_svc_def* m_pSvcDef;
- uint8_t m_removed;
- std::vector m_chrVec;
-
+ std::vector m_vChars{};
+ // Nimble requires an array of services to be sent to the api
+ // Since we are adding 1 at a time we create an array of 2 and set the type
+ // of the second service to 0 to indicate the end of the array.
+ ble_gatt_svc_def m_pSvcDef[2]{};
}; // NimBLEService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
-#endif /* MAIN_NIMBLESERVICE_H_ */
+#endif /* NIMBLE_CPP_SERVICE_H_ */
diff --git a/src/NimBLEUUID.cpp b/src/NimBLEUUID.cpp
index d5d400e..c9f9502 100644
--- a/src/NimBLEUUID.cpp
+++ b/src/NimBLEUUID.cpp
@@ -25,6 +25,12 @@ static const char* LOG_TAG = "NimBLEUUID";
static const uint8_t ble_base_uuid[] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+/**
+ * @brief Create a UUID from the native UUID.
+ * @param [in] uuid The native UUID.
+ */
+NimBLEUUID::NimBLEUUID(const ble_uuid_any_t& uuid) : m_uuid{uuid} {}
+
/**
* @brief Create a UUID from a string.
*
@@ -323,4 +329,4 @@ NimBLEUUID::operator std::string() const {
return ble_uuid_to_str(&m_uuid.u, buf);
} // operator std::string
-#endif /* CONFIG_BT_ENABLED */
+# endif /* CONFIG_BT_ENABLED */
diff --git a/src/NimBLEUUID.h b/src/NimBLEUUID.h
index 18d933e..7863995 100644
--- a/src/NimBLEUUID.h
+++ b/src/NimBLEUUID.h
@@ -41,6 +41,7 @@ class NimBLEUUID {
* @brief Created a blank UUID.
*/
NimBLEUUID() = default;
+ NimBLEUUID(const ble_uuid_any_t& uuid);
NimBLEUUID(const std::string& uuid);
NimBLEUUID(uint16_t uuid);
NimBLEUUID(uint32_t uuid);