mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2024-11-23 21:50:55 +01:00
[BREAKING] Refactor attributes
Refactor attributes to reduce code duplication and improve maintainability. * Add attribute base classes to provide common code. * Add const where possible to functions and parameters. * `NimBLECharacteristic::notify` no longer takes a `bool is_notification` parameter, instead `indicate()` should be called to send indications. * `NimBLECharacteristic::indicate` now takes the same parameters as `notify`. * `NimBLECharacteristicCallbacks` and `NimBLEDescriptorCallbacks` methods now take `const NimBLEConnInfo&` instead of non-const. * `NimBLECharacteristic::onNotify` callback removed as unnecessary, the library does not call notify without app input. * `NimBLERemoteCharacteristic::getRemoteService` now returns a `const NimBLERemoteService*` instead of non-const. * Add NimBLEUUID constructor that takes a reference to `ble_uuid_any_t`. * `NimBLERemoteService::getCharacteristics` now returns a `const std::vector<NimBLERemoteCharacteristic*>&` instead of non-const `std::vector<NimBLERemoteCharacteristic*>*` * `NimBLERemoteService::getValue` now returns `NimBLEAttValue` instead of `std::string` * `NimBLEService::getCharacteristics` now returns a `const std::vector<NimBLECharacteristic*>&` instead of a copy of std::vector<NimBLECharacteristic *>. * Remove const requirement for NimBLEConnInfo parameter in callbacks. Const is unnecessary as the data can't be changed by application code. * Change NimBLERemoteCharacteristic::getRemoteService to return const pointer.
This commit is contained in:
parent
b4b3b0c455
commit
91210b8610
35 changed files with 1757 additions and 2536 deletions
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
<br/>
|
||||
|
||||
> `void onPassKeyEntry(const NimBLEConnInfo& connInfo)`
|
||||
> `void onPassKeyEntry(NimBLEConnInfo& connInfo)`
|
||||
|
||||
Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
|
||||
<br/>
|
||||
|
@ -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.
|
||||
<br/>
|
||||
|
||||
> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)`
|
||||
> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
|
||||
|
||||
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
|
||||
<br/>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -192,12 +192,7 @@ class NimBLEAttValue {
|
|||
return setValue(reinterpret_cast<const uint8_t*>(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<const T*>(getValue(timestamp)));
|
||||
if (timestamp != nullptr) {
|
||||
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||
*timestamp = m_timestamp;
|
||||
# else
|
||||
*timestamp = 0;
|
||||
# endif
|
||||
}
|
||||
|
||||
return *(reinterpret_cast<const T*>(m_attr_value));
|
||||
}
|
||||
|
||||
/*********************** Operators ************************/
|
||||
|
|
50
src/NimBLEAttribute.h
Normal file
50
src/NimBLEAttribute.h
Normal file
|
@ -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_
|
|
@ -18,14 +18,12 @@
|
|||
# include "NimBLEDevice.h"
|
||||
# include "NimBLELog.h"
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
# define NIMBLE_SUB_NOTIFY 0x0001
|
||||
# define NIMBLE_SUB_INDICATE 0x0002
|
||||
|
||||
static NimBLECharacteristicCallbacks defaultCallback;
|
||||
static const char* LOG_TAG = "NimBLECharacteristic";
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a characteristic
|
||||
* @param [in] uuid - UUID (const char*) for the characteristic.
|
||||
|
@ -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.
|
||||
|
@ -97,32 +85,37 @@ 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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_dscVec.push_back(pDescriptor);
|
||||
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.
|
||||
|
@ -132,12 +125,12 @@ void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool
|
|||
// 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 (pDescriptor->getRemoved() > 0) {
|
||||
if (deleteDsc) {
|
||||
for(auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) {
|
||||
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,174 +171,47 @@ 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) {
|
||||
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)) {
|
||||
subVal |= NIMBLE_SUB_NOTIFY;
|
||||
|
@ -356,24 +220,22 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
|
|||
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);
|
||||
NimBLEDevice::getServer()->clearIndicateWait(connInfo.getConnHandle());
|
||||
}
|
||||
|
||||
|
||||
auto it = m_subscribedVec.begin();
|
||||
for (; it != m_subscribedVec.end(); ++it) {
|
||||
if((*it).first == event->subscribe.conn_handle) {
|
||||
if ((*it).first == connInfo.getConnHandle()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (subVal > 0) {
|
||||
if (it == m_subscribedVec.end()) {
|
||||
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
|
||||
m_subscribedVec.push_back({connInfo.getConnHandle(), subVal});
|
||||
} else {
|
||||
(*it).second = subVal;
|
||||
}
|
||||
|
@ -381,150 +243,158 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
|
|||
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<uint8_t> 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<uint8_t>& value) {
|
||||
notify(value.data(), value.size(), false);
|
||||
void NimBLECharacteristic::indicate(const std::vector<uint8_t>& 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<uint8_t> 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<uint8_t>& value, bool is_notification, uint16_t conn_handle) {
|
||||
notify(value.data(), value.size(), is_notification, conn_handle);
|
||||
void NimBLECharacteristic::notify(const std::vector<uint8_t>& 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);
|
||||
if (!om) {
|
||||
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
|
||||
if (is_notification) {
|
||||
ble_gattc_notify_custom(it.first, getHandle(), om);
|
||||
} else {
|
||||
if (!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
|
||||
NIMBLE_LOGE(LOG_TAG, "prior Indication in progress");
|
||||
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.
|
||||
|
@ -539,50 +409,21 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
|
|||
}
|
||||
} // 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<uint8_t>`.\n
|
||||
* @param [in] vec The std::vector<uint8_t> reference to set the characteristic value from.
|
||||
*/
|
||||
void NimBLECharacteristic::setValue(const std::vector<uint8_t>& vec) {
|
||||
return setValue((uint8_t*)&vec[0], vec.size());
|
||||
}// setValue
|
||||
|
||||
|
||||
/**
|
||||
* @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);
|
||||
snprintf(hex, sizeof(hex), "%04x", getHandle());
|
||||
res += hex;
|
||||
res += " ";
|
||||
if (m_properties & BLE_GATT_CHR_PROP_READ) res += "Read ";
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -11,37 +11,16 @@
|
|||
* 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;
|
||||
class NimBLECharacteristicCallbacks;
|
||||
class NimBLECharacteristic;
|
||||
|
||||
# include "NimBLELocalValueAttribute.h"
|
||||
# include "NimBLEServer.h"
|
||||
# include "NimBLEService.h"
|
||||
# include "NimBLEDescriptor.h"
|
||||
# include "NimBLEAttValue.h"
|
||||
|
@ -50,92 +29,53 @@ typedef enum {
|
|||
# include <string>
|
||||
# include <vector>
|
||||
|
||||
class NimBLEService;
|
||||
class NimBLEDescriptor;
|
||||
class NimBLECharacteristicCallbacks;
|
||||
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
||||
public:
|
||||
NimBLECharacteristic(const char* uuid,
|
||||
uint16_t properties =
|
||||
NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::WRITE,
|
||||
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 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<uint8_t>& 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<uint8_t>& value, bool is_notification = true, uint16_t conn_handle = BLE_HCI_LE_CONN_HANDLE_MAX + 1);
|
||||
size_t getSubscribedCount();
|
||||
std::string toString() const;
|
||||
size_t getSubscribedCount() const;
|
||||
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<uint8_t>& vec);
|
||||
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<uint8_t>& 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<uint8_t>& 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,
|
||||
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();
|
||||
|
||||
NimBLECharacteristicCallbacks* getCallbacks() const;
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Template to set the characteristic value to <type\>val.
|
||||
* @param [in] s The value to set.
|
||||
*/
|
||||
template<typename T>
|
||||
void setValue(const T &s) { m_value.setValue<T>(s); }
|
||||
|
||||
/**
|
||||
* @brief Template to convert the characteristic data to <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
|
||||
return m_value.getValue<T>(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.
|
||||
|
@ -149,8 +89,8 @@ public:
|
|||
# else
|
||||
typename std::enable_if<Has_c_str_len<T>::value, void>::type
|
||||
# endif
|
||||
notify(const T& value, bool is_notification = true) {
|
||||
notify((uint8_t*)value.c_str(), value.length(), is_notification);
|
||||
notify(const T& value, bool is_notification = true) const {
|
||||
notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), is_notification);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,33 +105,29 @@ public:
|
|||
# else
|
||||
typename std::enable_if<Has_c_str_len<T>::value, void>::type
|
||||
# endif
|
||||
indicate(const T& value) {
|
||||
indicate((uint8_t*)value.c_str(), value.length());
|
||||
indicate(const T& value) const {
|
||||
indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
friend class NimBLEServer;
|
||||
friend class NimBLEService;
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
NimBLEUUID m_uuid;
|
||||
uint16_t m_handle;
|
||||
uint16_t m_properties;
|
||||
NimBLECharacteristicCallbacks* m_pCallbacks;
|
||||
NimBLEService* m_pService;
|
||||
NimBLEAttValue m_value;
|
||||
std::vector<NimBLEDescriptor*> m_dscVec;
|
||||
uint8_t m_removed;
|
||||
|
||||
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
|
||||
NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
|
||||
NimBLEService* m_pService{nullptr};
|
||||
std::vector<NimBLEDescriptor*> m_vDescriptors{};
|
||||
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec{};
|
||||
}; // NimBLECharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
|
||||
*
|
||||
|
@ -204,10 +140,9 @@ 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_*/
|
||||
|
|
|
@ -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<NimBLEClient*>(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);
|
||||
}
|
||||
|
|
|
@ -23,15 +23,14 @@
|
|||
#include "NimBLEConnInfo.h"
|
||||
#include "NimBLEAttValue.h"
|
||||
#include "NimBLEAdvertisedDevice.h"
|
||||
#include "NimBLERemoteService.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
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<NimBLERemoteService*>* 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,16 +92,16 @@ 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;
|
||||
mutable 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;
|
||||
mutable ble_task_data_t* m_pTaskData;
|
||||
ble_npl_callout m_dcTimer;
|
||||
#if CONFIG_BT_NIMBLE_EXT_ADV
|
||||
uint8_t m_phyMask;
|
||||
|
@ -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 */
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
#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;
|
||||
|
||||
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); }
|
||||
|
@ -54,5 +52,15 @@ public:
|
|||
|
||||
/** @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; }
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -21,11 +21,18 @@
|
|||
|
||||
# include <string>
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
|
||||
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,194 +41,52 @@ 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.
|
||||
|
@ -234,39 +99,6 @@ void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) {
|
|||
}
|
||||
} // 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<uint8_t>`.\n
|
||||
* @param [in] vec The std::vector<uint8_t> reference to set the descriptor value from.
|
||||
*/
|
||||
void NimBLEDescriptor::setValue(const std::vector<uint8_t>& 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
|
||||
|
||||
|
|
|
@ -12,12 +12,16 @@
|
|||
* 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)
|
||||
|
||||
class NimBLEDescriptor;
|
||||
class NimBLEDescriptorCallbacks;
|
||||
|
||||
# include "NimBLELocalValueAttribute.h"
|
||||
# include "NimBLECharacteristic.h"
|
||||
# include "NimBLEUUID.h"
|
||||
# include "NimBLEAttValue.h"
|
||||
|
@ -25,81 +29,35 @@
|
|||
|
||||
# include <string>
|
||||
|
||||
class NimBLEService;
|
||||
class NimBLECharacteristic;
|
||||
class NimBLEDescriptorCallbacks;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A model of a %BLE descriptor.
|
||||
* @brief A model of a BLE descriptor.
|
||||
*/
|
||||
class NimBLEDescriptor {
|
||||
class NimBLEDescriptor : public NimBLELocalValueAttribute {
|
||||
public:
|
||||
NimBLEDescriptor(const char* uuid, uint16_t properties,
|
||||
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();
|
||||
|
||||
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<uint8_t>& vec);
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Template to set the characteristic value to <type\>val.
|
||||
* @param [in] s The value to set.
|
||||
*/
|
||||
template<typename T>
|
||||
void setValue(const T &s) { m_value.setValue<T>(s); }
|
||||
|
||||
/**
|
||||
* @brief Template to convert the descriptor data to <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
|
||||
return m_value.getValue<T>(timestamp, skipSizeCheck);
|
||||
}
|
||||
NimBLECharacteristic* getCharacteristic() const;
|
||||
|
||||
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 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.
|
||||
*
|
||||
|
@ -109,7 +67,7 @@ private:
|
|||
*/
|
||||
class NimBLEDescriptorCallbacks {
|
||||
public:
|
||||
virtual ~NimBLEDescriptorCallbacks(){}
|
||||
virtual ~NimBLEDescriptorCallbacks() = default;
|
||||
virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
||||
virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
||||
};
|
||||
|
@ -117,4 +75,4 @@ public:
|
|||
# include "NimBLE2904.h"
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||
#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */
|
||||
#endif /* NIMBLE_CPP_DESCRIPTOR_H_ */
|
||||
|
|
|
@ -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 <algorithm>
|
||||
|
||||
static const char* LOG_TAG = "NimBLEDevice";
|
||||
|
|
|
@ -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 <string>
|
||||
#include <array>
|
||||
|
||||
#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_
|
||||
|
|
48
src/NimBLELocalAttribute.h
Normal file
48
src/NimBLELocalAttribute.h
Normal file
|
@ -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_
|
156
src/NimBLELocalValueAttribute.h
Normal file
156
src/NimBLELocalValueAttribute.h
Normal file
|
@ -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 <vector>
|
||||
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<uint8_t>& vec) { m_value.setValue(vec); }
|
||||
|
||||
/**
|
||||
* @brief Template to set the value to <type\>val.
|
||||
* @param [in] val The value to set.
|
||||
*/
|
||||
template <typename T>
|
||||
void setValue(const T& val) {
|
||||
m_value.setValue<T>(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to convert the data to <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template <typename T>
|
||||
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
||||
return m_value.getValue<T>(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_
|
|
@ -16,16 +16,24 @@
|
|||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||
|
||||
# include "NimBLERemoteCharacteristic.h"
|
||||
# include "NimBLERemoteDescriptor.h"
|
||||
# include "NimBLERemoteService.h"
|
||||
# include "NimBLEClient.h"
|
||||
# include "NimBLEUtils.h"
|
||||
# include "NimBLELog.h"
|
||||
|
||||
# include <climits>
|
||||
|
||||
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<ble_uuid128_t*>(&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,214 +56,60 @@ 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();
|
||||
if (uuidFilter != nullptr) {
|
||||
if (ble_uuid_cmp(uuidFilter->getBase(), &dsc->uuid.u) == 0) {
|
||||
rc = BLE_HS_EDONE; // Found the descriptor, stop the search
|
||||
} else {
|
||||
pTaskData->rc = rc;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
|
||||
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
|
||||
ble_task_data_t taskData = {const_cast<NimBLERemoteCharacteristic*>(this), cur_task, 0, nullptr};
|
||||
desc_filter_t filter = {uuidFilter, &taskData};
|
||||
|
||||
// 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);
|
||||
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
return false;
|
||||
|
@ -290,46 +122,48 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
|
|||
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) {
|
||||
for (const auto& it : m_vDescriptors) {
|
||||
if (it->getUUID() == uuid) {
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
|
||||
return it;
|
||||
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 (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 (m_vDescriptors.size() > prev_size) {
|
||||
pDsc = m_vDescriptors.back();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -340,19 +174,19 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
|
|||
// 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 (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<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors(bool refresh) {
|
||||
const std::vector<NimBLERemoteDescriptor*>& NimBLERemoteCharacteristic::getDescriptors(bool refresh) const {
|
||||
if (refresh) {
|
||||
deleteDescriptors();
|
||||
retrieveDescriptors();
|
||||
}
|
||||
|
||||
if (!retrieveDescriptors()) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors");
|
||||
}
|
||||
else{
|
||||
NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size());
|
||||
}
|
||||
}
|
||||
return &m_descriptorVector;
|
||||
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<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::begin() {
|
||||
return m_descriptorVector.begin();
|
||||
std::vector<NimBLERemoteDescriptor*>::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<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end() {
|
||||
return m_descriptorVector.end();
|
||||
std::vector<NimBLERemoteDescriptor*>::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,11 +236,10 @@ 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);
|
||||
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) {
|
||||
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
|
||||
|
@ -576,11 +247,9 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
|
|||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
|
||||
|
||||
return desc->writeValue((uint8_t *)&val, 2, response);
|
||||
return desc->writeValue(reinterpret_cast<uint8_t*>(&val), 2, response);
|
||||
} // setNotify
|
||||
|
||||
|
||||
/**
|
||||
* @brief Subscribe for notifications or indications.
|
||||
* @param [in] notifications If true, subscribe for notifications, false subscribe for indications.
|
||||
|
@ -589,69 +258,125 @@ 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<NimBLERemoteDescriptor*>().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) {
|
||||
for (auto it = m_vDescriptors.begin(); it != m_vDescriptors.end(); ++it) {
|
||||
if ((*it)->getUUID() == uuid) {
|
||||
delete *it;
|
||||
m_descriptorVector.erase(it);
|
||||
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];
|
||||
res += ", handle: ";
|
||||
|
@ -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<uint8_t>.
|
||||
* @param [in] vec A std::vector<uint8_t> 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<uint8_t>& 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 */
|
||||
|
|
|
@ -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 "NimBLERemoteValueAttribute.h"
|
||||
# include <vector>
|
||||
# include <functional>
|
||||
#include "NimBLELog.h"
|
||||
|
||||
class NimBLERemoteService;
|
||||
class NimBLERemoteDescriptor;
|
||||
|
||||
|
||||
typedef std::function<void (NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
|
||||
uint8_t* pData, size_t length, bool isNotify)> 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 {
|
||||
class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
|
||||
public:
|
||||
~NimBLERemoteCharacteristic();
|
||||
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;
|
||||
|
||||
// Public member functions
|
||||
bool canBroadcast();
|
||||
bool canIndicate();
|
||||
bool canNotify();
|
||||
bool canRead();
|
||||
bool canWrite();
|
||||
bool canWriteNoResponse();
|
||||
std::vector<NimBLERemoteDescriptor*>::iterator begin();
|
||||
std::vector<NimBLERemoteDescriptor*>::iterator end();
|
||||
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid);
|
||||
std::vector<NimBLERemoteDescriptor*>* 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<uint8_t>& v, bool response = false);
|
||||
bool writeValue(const char* s, bool response = false);
|
||||
typedef std::function<void(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)> notify_callback;
|
||||
|
||||
bool subscribe(bool notifications = true, const notify_callback notifyCallback = nullptr, bool response = true) const;
|
||||
bool unsubscribe(bool response = true) const;
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Template to set the remote characteristic value to <type\>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<typename T>
|
||||
#ifdef _DOXYGEN_
|
||||
bool
|
||||
#else
|
||||
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::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 <type\>val.
|
||||
* @param [in] s The value to write.
|
||||
* @param [in] response True == request write response.
|
||||
* @details Only used if the <type\> has a `c_str()` method.
|
||||
*/
|
||||
template<typename T>
|
||||
#ifdef _DOXYGEN_
|
||||
bool
|
||||
#else
|
||||
typename std::enable_if<Has_c_str_len<T>::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 <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
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 <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>readValue<type>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
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));
|
||||
}
|
||||
std::vector<NimBLERemoteDescriptor*>::iterator begin() const;
|
||||
std::vector<NimBLERemoteDescriptor*>::iterator end() const;
|
||||
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID& uuid) const;
|
||||
const std::vector<NimBLERemoteDescriptor*>& getDescriptors(bool refresh = false) const;
|
||||
|
||||
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);
|
||||
NimBLERemoteCharacteristic(const NimBLERemoteService* pRemoteService, const ble_gatt_chr* chr);
|
||||
~NimBLERemoteCharacteristic();
|
||||
|
||||
// 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;
|
||||
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);
|
||||
|
||||
const NimBLERemoteService* m_pRemoteService{nullptr};
|
||||
uint8_t m_properties{0};
|
||||
mutable notify_callback m_notifyCallback{nullptr};
|
||||
mutable std::vector<NimBLERemoteDescriptor*> m_vDescriptors{};
|
||||
|
||||
// We maintain a vector of descriptors owned by this characteristic.
|
||||
std::vector<NimBLERemoteDescriptor*> m_descriptorVector;
|
||||
}; // NimBLERemoteCharacteristic
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||
#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */
|
||||
#endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */
|
||||
|
|
|
@ -16,180 +16,30 @@
|
|||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||
|
||||
# include "NimBLERemoteDescriptor.h"
|
||||
#include "NimBLEUtils.h"
|
||||
#include "NimBLELog.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
static const char* LOG_TAG = "NimBLERemoteDescriptor";
|
||||
# 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<ble_uuid128_t*>(&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<NimBLERemoteCharacteristic*>(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];
|
||||
res += ", handle: ";
|
||||
|
@ -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;
|
||||
NimBLEClient* NimBLERemoteDescriptor::getClient() const {
|
||||
return m_pRemoteCharacteristic->getClient();
|
||||
}
|
||||
|
||||
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
|
||||
|
||||
pTaskData->rc = error->status;
|
||||
xTaskNotifyGive(pTaskData->task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write a new value to a remote descriptor from a std::vector<uint8_t>.
|
||||
* @param [in] vec A std::vector<uint8_t> 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<uint8_t>& 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 */
|
||||
|
|
|
@ -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 {
|
||||
class NimBLERemoteDescriptor : public NimBLERemoteValueAttribute {
|
||||
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<uint8_t>& v, bool response = false);
|
||||
bool writeValue(const char* s, bool response = false);
|
||||
|
||||
|
||||
/*********************** Template Functions ************************/
|
||||
|
||||
/**
|
||||
* @brief Template to set the remote descriptor value to <type\>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<typename T>
|
||||
#ifdef _DOXYGEN_
|
||||
bool
|
||||
#else
|
||||
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::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 <type\>val.
|
||||
* @param [in] s The value to write.
|
||||
* @param [in] response True == request write response.
|
||||
* @details Only used if the <type\> has a `c_str()` method.
|
||||
*/
|
||||
template<typename T>
|
||||
#ifdef _DOXYGEN_
|
||||
bool
|
||||
#else
|
||||
typename std::enable_if<Has_c_str_len<T>::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 <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt>
|
||||
*/
|
||||
template<typename T>
|
||||
T readValue(bool skipSizeCheck = false) {
|
||||
NimBLEAttValue value = readValue();
|
||||
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
|
||||
return *((T *)value.data());
|
||||
}
|
||||
NimBLERemoteCharacteristic* getRemoteCharacteristic() const;
|
||||
std::string toString(void) const;
|
||||
NimBLEClient* getClient() const override;
|
||||
|
||||
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);
|
||||
NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic, const ble_gatt_dsc* dsc);
|
||||
~NimBLERemoteDescriptor() = default;
|
||||
|
||||
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_ */
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||
|
||||
# include "NimBLERemoteService.h"
|
||||
# include "NimBLERemoteCharacteristic.h"
|
||||
# include "NimBLEClient.h"
|
||||
# include "NimBLEAttValue.h"
|
||||
# include "NimBLEUtils.h"
|
||||
#include "NimBLEDevice.h"
|
||||
# include "NimBLELog.h"
|
||||
|
||||
# include <climits>
|
||||
|
@ -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<ble_uuid128_t*>(&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<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() {
|
||||
return m_characteristicVector.begin();
|
||||
std::vector<NimBLERemoteCharacteristic*>::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<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() {
|
||||
return m_characteristicVector.end();
|
||||
std::vector<NimBLERemoteCharacteristic*>::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) {
|
||||
for (const auto& it : m_vChars) {
|
||||
if (it->getUUID() == uuid) {
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
|
||||
return it;
|
||||
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 (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 {
|
||||
|
@ -129,282 +107,188 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
|
|||
// 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 (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<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) {
|
||||
const std::vector<NimBLERemoteCharacteristic*>& NimBLERemoteService::getCharacteristics(bool refresh) const {
|
||||
if (refresh) {
|
||||
deleteCharacteristics();
|
||||
retrieveCharacteristics();
|
||||
}
|
||||
|
||||
if (!retrieveCharacteristics()) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
|
||||
}
|
||||
else{
|
||||
NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size());
|
||||
}
|
||||
}
|
||||
return &m_characteristicVector;
|
||||
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;
|
||||
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;
|
||||
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 = {this, cur_task, 0, nullptr};
|
||||
ble_task_data_t taskData = {const_cast<NimBLERemoteService*>(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,
|
||||
getHandle(),
|
||||
getEndHandle(),
|
||||
NimBLERemoteService::characteristicDiscCB,
|
||||
&taskData);
|
||||
} else {
|
||||
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
|
||||
m_startHandle,
|
||||
m_endHandle,
|
||||
uuid_filter->getBase(),
|
||||
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);
|
||||
|
||||
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<NimBLERemoteCharacteristic*>{}.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) {
|
||||
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_characteristicVector.erase(it);
|
||||
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;
|
||||
|
|
|
@ -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 <vector>
|
||||
|
||||
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 {
|
||||
class NimBLERemoteService : public NimBLEAttribute {
|
||||
public:
|
||||
virtual ~NimBLERemoteService();
|
||||
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<NimBLERemoteCharacteristic*>::iterator begin();
|
||||
std::vector<NimBLERemoteCharacteristic*>::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<NimBLERemoteCharacteristic*>* getCharacteristics(bool refresh = false);
|
||||
const std::vector<NimBLERemoteCharacteristic*>& getCharacteristics(bool refresh = false) const;
|
||||
std::vector<NimBLERemoteCharacteristic*>::iterator begin() const;
|
||||
std::vector<NimBLERemoteCharacteristic*>::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
|
||||
friend class NimBLEClient;
|
||||
friend class NimBLERemoteCharacteristic;
|
||||
|
||||
// Private methods
|
||||
bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr);
|
||||
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<NimBLERemoteCharacteristic*> m_characteristicVector;
|
||||
|
||||
NimBLEClient* m_pClient;
|
||||
NimBLEUUID m_uuid;
|
||||
uint16_t m_startHandle;
|
||||
uint16_t m_endHandle;
|
||||
mutable std::vector<NimBLERemoteCharacteristic*> 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_*/
|
||||
|
|
212
src/NimBLERemoteValueAttribute.cpp
Normal file
212
src/NimBLERemoteValueAttribute.cpp
Normal file
|
@ -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 <climits>
|
||||
|
||||
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<NimBLERemoteValueAttribute*>(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<ble_task_data_t*>(arg);
|
||||
const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(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<NimBLERemoteValueAttribute*>(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<ble_task_data_t*>(arg);
|
||||
const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(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<NimBLEAttValue*>(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
|
166
src/NimBLERemoteValueAttribute.h
Normal file
166
src/NimBLERemoteValueAttribute.h
Normal file
|
@ -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 <host/ble_gatt.h>
|
||||
# else
|
||||
# include <nimble/nimble/host/include/host/ble_gatt.h>
|
||||
# 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<uint8_t>.
|
||||
* @param [in] vec A std::vector<uint8_t> 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<uint8_t>& 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<const uint8_t*>(str), length ? length : strlen(str), response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to set the remote characteristic value to <type\>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 <typename T>
|
||||
# ifdef _DOXYGEN_
|
||||
bool
|
||||
# else
|
||||
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
|
||||
# endif
|
||||
writeValue(const T& v, bool response = false) const {
|
||||
return writeValue(reinterpret_cast<const uint8_t*>(&v), sizeof(T), response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to set the remote characteristic value to <type\>val.
|
||||
* @param [in] s The value to write.
|
||||
* @param [in] response True == request write response.
|
||||
* @details Only used if the <type\> has a `c_str()` method.
|
||||
*/
|
||||
template <typename T>
|
||||
# ifdef _DOXYGEN_
|
||||
bool
|
||||
# else
|
||||
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
|
||||
# endif
|
||||
writeValue(const T& s, bool response = false) const {
|
||||
return writeValue(reinterpret_cast<const uint8_t*>(s.c_str()), s.length(), response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to convert the remote characteristic data to <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>getValue<type>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template <typename T>
|
||||
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
||||
return m_value.getValue<T>(timestamp, skipSizeCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template to convert the remote characteristic data to <type\>.
|
||||
* @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 <tt>sizeof(<type\>)</tt>.
|
||||
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
|
||||
* less than <tt>sizeof(<type\>)</tt>.
|
||||
* @details <b>Use:</b> <tt>readValue<type>(×tamp, skipSizeCheck);</tt>
|
||||
*/
|
||||
template <typename T>
|
||||
T readValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
|
||||
readValue();
|
||||
return m_value.getValue<T>(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_
|
|
@ -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<NimBLELocalValueAttribute*>(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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -24,84 +24,60 @@
|
|||
|
||||
# include <string>
|
||||
|
||||
static const char* LOG_TAG = "NimBLEService"; // Tag for logging.
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
|
||||
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);
|
||||
if (m_pSvcDef->characteristics) {
|
||||
if (m_pSvcDef->characteristics->descriptors) {
|
||||
delete[] m_pSvcDef->characteristics->descriptors;
|
||||
}
|
||||
}
|
||||
delete(m_pSvcDef->characteristics);
|
||||
delete[] m_pSvcDef->characteristics;
|
||||
}
|
||||
|
||||
delete(m_pSvcDef);
|
||||
}
|
||||
|
||||
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";}
|
||||
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());
|
||||
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.
|
||||
|
@ -274,58 +199,61 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
|
|||
* @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* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundRemoved) {
|
||||
m_chrVec.push_back(pCharacteristic);
|
||||
m_vChars.push_back(pChar);
|
||||
}
|
||||
|
||||
pCharacteristic->setService(this);
|
||||
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 (pChar->getRemoved() > 0) {
|
||||
if (deleteChr) {
|
||||
for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) {
|
||||
if ((*it) == pCharacteristic) {
|
||||
m_chrVec.erase(it);
|
||||
delete *it;
|
||||
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<NimBLECharacteristic *> NimBLEService::getCharacteristics() {
|
||||
return m_chrVec;
|
||||
}
|
||||
const std::vector<NimBLECharacteristic*>& 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<NimBLECharacteristic *> NimBLEService::getCharacteristics(const char *uuid) {
|
||||
std::vector<NimBLECharacteristic*> 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<NimBLECharacteristic *> NimBLEService::getCharacteristics(const NimBLEUUID &uuid) {
|
||||
std::vector<NimBLECharacteristic*> NimBLEService::getCharacteristics(const NimBLEUUID& uuid) const {
|
||||
std::vector<NimBLECharacteristic*> 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,7 +346,7 @@ std::vector<NimBLECharacteristic *> 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];
|
||||
snprintf(hex, sizeof(hex), "%04x", getHandle());
|
||||
|
@ -425,22 +355,20 @@ std::string NimBLEService::toString() {
|
|||
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
|
||||
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
|
|
|
@ -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)
|
||||
|
||||
class NimBLEService;
|
||||
|
||||
# include "NimBLEAttribute.h"
|
||||
# include "NimBLEServer.h"
|
||||
# include "NimBLECharacteristic.h"
|
||||
#include "NimBLEUUID.h"
|
||||
|
||||
|
||||
class NimBLEServer;
|
||||
class NimBLECharacteristic;
|
||||
|
||||
|
||||
/**
|
||||
* @brief The model of a %BLE service.
|
||||
* @brief The model of a BLE service.
|
||||
*
|
||||
*/
|
||||
class NimBLEService {
|
||||
class NimBLEService : public NimBLELocalAttribute {
|
||||
public:
|
||||
|
||||
NimBLEService(const char* 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,
|
||||
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,
|
||||
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);
|
||||
|
||||
std::vector<NimBLECharacteristic*> getCharacteristics();
|
||||
std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid);
|
||||
std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID &uuid);
|
||||
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;
|
||||
|
||||
const std::vector<NimBLECharacteristic*>& getCharacteristics() const;
|
||||
std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid) const;
|
||||
std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID& uuid) const;
|
||||
|
||||
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<NimBLECharacteristic*> m_chrVec;
|
||||
|
||||
std::vector<NimBLECharacteristic*> 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_ */
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue