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/NimBLERemoteCharacteristic.cpp"
|
||||||
"src/NimBLERemoteDescriptor.cpp"
|
"src/NimBLERemoteDescriptor.cpp"
|
||||||
"src/NimBLERemoteService.cpp"
|
"src/NimBLERemoteService.cpp"
|
||||||
|
"src/NimBLERemoteValueAttribute.cpp"
|
||||||
"src/NimBLEScan.cpp"
|
"src/NimBLEScan.cpp"
|
||||||
"src/NimBLEServer.cpp"
|
"src/NimBLEServer.cpp"
|
||||||
"src/NimBLEService.cpp"
|
"src/NimBLEService.cpp"
|
||||||
|
|
|
@ -383,13 +383,13 @@ The security callback methods are now incorporated in the `NimBLEServerCallbacks
|
||||||
|
|
||||||
The callback methods are:
|
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.
|
Receives the pin when using numeric comparison authentication.
|
||||||
Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject.
|
Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject.
|
||||||
<br/>
|
<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);`
|
Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
|
||||||
<br/>
|
<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.
|
Server callback; should return the passkey (pin) expected from the client.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)`
|
> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
|
||||||
|
|
||||||
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
|
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
|
||||||
|
|
||||||
/********************* Security handled here **********************
|
/********************* Security handled here **********************
|
||||||
****** Note: these are the same return values as defaults ********/
|
****** Note: these are the same return values as defaults ********/
|
||||||
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
|
void onPassKeyEntry(NimBLEConnInfo& connInfo){
|
||||||
printf("Server Passkey Entry\n");
|
printf("Server Passkey Entry\n");
|
||||||
/** This should prompt the user to enter the passkey displayed
|
/** This should prompt the user to enter the passkey displayed
|
||||||
* on the peer device.
|
* on the peer device.
|
||||||
|
@ -47,14 +47,14 @@ class ClientCallbacks : public NimBLEClientCallbacks {
|
||||||
NimBLEDevice::injectPassKey(connInfo, 123456);
|
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);
|
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||||
/** Inject false if passkeys don't match. */
|
/** Inject false if passkeys don't match. */
|
||||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Pairing process complete, we can check the results in connInfo */
|
/** Pairing process complete, we can check the results in connInfo */
|
||||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||||
if(!connInfo.isEncrypted()) {
|
if(!connInfo.isEncrypted()) {
|
||||||
printf("Encrypt connection failed - disconnecting\n");
|
printf("Encrypt connection failed - disconnecting\n");
|
||||||
/** Find the client with the connection handle provided in desc */
|
/** 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){
|
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
|
||||||
std::string str = (isNotify == true) ? "Notification" : "Indication";
|
std::string str = (isNotify == true) ? "Notification" : "Indication";
|
||||||
str += " from ";
|
str += " from ";
|
||||||
str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
|
str += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
|
||||||
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
|
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
|
||||||
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
|
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
|
||||||
str += ", Value = " + std::string((char*)pData, length);
|
str += ", Value = " + std::string((char*)pData, length);
|
||||||
|
|
|
@ -52,13 +52,13 @@ class ServerCallbacks: public NimBLEServerCallbacks {
|
||||||
return 123456;
|
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);
|
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||||
/** Inject false if passkeys don't match. */
|
/** Inject false if passkeys don't match. */
|
||||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||||
/** Check that encryption was successful, if not we disconnect the client */
|
/** Check that encryption was successful, if not we disconnect the client */
|
||||||
if(!connInfo.isEncrypted()) {
|
if(!connInfo.isEncrypted()) {
|
||||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||||
|
@ -142,7 +142,7 @@ void notifyTask(void * parameter){
|
||||||
if(pSvc) {
|
if(pSvc) {
|
||||||
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
|
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
|
||||||
if(pChr) {
|
if(pChr) {
|
||||||
pChr->notify(true);
|
pChr->notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class ServerCallbacks : public NimBLEServerCallbacks {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as before but now includes the name parameter
|
// 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()) {
|
if (!connInfo.isEncrypted()) {
|
||||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||||
printf("Encrypt connection failed - disconnecting client\n");
|
printf("Encrypt connection failed - disconnecting client\n");
|
||||||
|
|
|
@ -51,7 +51,7 @@ class MyClientCallback : public BLEClientCallbacks {
|
||||||
}
|
}
|
||||||
/***************** New - Security handled here ********************
|
/***************** New - Security handled here ********************
|
||||||
****** Note: these are the same return values as defaults ********/
|
****** Note: these are the same return values as defaults ********/
|
||||||
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
|
void onPassKeyEntry(NimBLEConnInfo& connInfo){
|
||||||
printf("Server Passkey Entry\n");
|
printf("Server Passkey Entry\n");
|
||||||
/** This should prompt the user to enter the passkey displayed
|
/** This should prompt the user to enter the passkey displayed
|
||||||
* on the peer device.
|
* on the peer device.
|
||||||
|
@ -59,14 +59,14 @@ class MyClientCallback : public BLEClientCallbacks {
|
||||||
NimBLEDevice::injectPassKey(connInfo, 123456);
|
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);
|
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||||
/** Inject false if passkeys don't match. */
|
/** Inject false if passkeys don't match. */
|
||||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Pairing process complete, we can check the results in connInfo */
|
/** Pairing process complete, we can check the results in connInfo */
|
||||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||||
if(!connInfo.isEncrypted()) {
|
if(!connInfo.isEncrypted()) {
|
||||||
printf("Encrypt connection failed - disconnecting\n");
|
printf("Encrypt connection failed - disconnecting\n");
|
||||||
/** Find the client with the connection handle provided in desc */
|
/** Find the client with the connection handle provided in desc */
|
||||||
|
|
|
@ -65,13 +65,13 @@ class MyServerCallbacks: public BLEServerCallbacks {
|
||||||
return 123456;
|
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);
|
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||||
/** Inject false if passkeys don't match. */
|
/** Inject false if passkeys don't match. */
|
||||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||||
/** Check that encryption was successful, if not we disconnect the client */
|
/** Check that encryption was successful, if not we disconnect the client */
|
||||||
if(!connInfo.isEncrypted()) {
|
if(!connInfo.isEncrypted()) {
|
||||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||||
|
|
|
@ -67,13 +67,13 @@ class MyServerCallbacks: public BLEServerCallbacks {
|
||||||
return 123456;
|
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);
|
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||||
/** Inject false if passkeys don't match. */
|
/** Inject false if passkeys don't match. */
|
||||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||||
/** Check that encryption was successful, if not we disconnect the client */
|
/** Check that encryption was successful, if not we disconnect the client */
|
||||||
if(!connInfo.isEncrypted()) {
|
if(!connInfo.isEncrypted()) {
|
||||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||||
|
|
|
@ -192,12 +192,7 @@ class NimBLEAttValue {
|
||||||
return setValue(reinterpret_cast<const uint8_t*>(s), len);
|
return setValue(reinterpret_cast<const uint8_t*>(s), len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
|
||||||
* @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 {
|
|
||||||
if (timestamp != nullptr) {
|
if (timestamp != nullptr) {
|
||||||
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||||
*timestamp = m_timestamp;
|
*timestamp = m_timestamp;
|
||||||
|
@ -205,7 +200,7 @@ class NimBLEAttValue {
|
||||||
*timestamp = 0;
|
*timestamp = 0;
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
return m_attr_value;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -271,7 +266,15 @@ class NimBLEAttValue {
|
||||||
if (!skipSizeCheck && size() < sizeof(T)) {
|
if (!skipSizeCheck && size() < sizeof(T)) {
|
||||||
return 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 ************************/
|
/*********************** 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_
|
|
@ -13,19 +13,17 @@
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
#include "NimBLECharacteristic.h"
|
# include "NimBLECharacteristic.h"
|
||||||
#include "NimBLE2904.h"
|
# include "NimBLE2904.h"
|
||||||
#include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
#include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
#define NULL_HANDLE (0xffff)
|
# define NIMBLE_SUB_NOTIFY 0x0001
|
||||||
#define NIMBLE_SUB_NOTIFY 0x0001
|
# define NIMBLE_SUB_INDICATE 0x0002
|
||||||
#define NIMBLE_SUB_INDICATE 0x0002
|
|
||||||
|
|
||||||
static NimBLECharacteristicCallbacks defaultCallback;
|
static NimBLECharacteristicCallbacks defaultCallback;
|
||||||
static const char* LOG_TAG = "NimBLECharacteristic";
|
static const char* LOG_TAG = "NimBLECharacteristic";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a characteristic
|
* @brief Construct a characteristic
|
||||||
* @param [in] uuid - UUID (const char*) for the 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] 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.
|
* @param [in] pService - pointer to the service instance this characteristic belongs to.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties,
|
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
|
||||||
uint16_t max_len, NimBLEService* pService)
|
: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {}
|
||||||
: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a characteristic
|
* @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] 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.
|
* @param [in] pService - pointer to the service instance this characteristic belongs to.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties,
|
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
|
||||||
uint16_t max_len, NimBLEService* pService)
|
: NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallback}, m_pService{pService} {
|
||||||
: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
|
setProperties(properties);
|
||||||
m_uuid = uuid;
|
|
||||||
m_handle = NULL_HANDLE;
|
|
||||||
m_properties = properties;
|
|
||||||
m_pCallbacks = &defaultCallback;
|
|
||||||
m_pService = pService;
|
|
||||||
m_removed = 0;
|
|
||||||
} // NimBLECharacteristic
|
} // NimBLECharacteristic
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destructor.
|
* @brief Destructor.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic::~NimBLECharacteristic() {
|
NimBLECharacteristic::~NimBLECharacteristic() {
|
||||||
for(auto &it : m_dscVec) {
|
for (const auto& dsc : m_vDescriptors) {
|
||||||
delete it;
|
delete dsc;
|
||||||
}
|
}
|
||||||
} // ~NimBLECharacteristic
|
} // ~NimBLECharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new BLE Descriptor associated with this characteristic.
|
* @brief Create a new BLE Descriptor associated with this characteristic.
|
||||||
* @param [in] uuid - The UUID of the descriptor.
|
* @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);
|
return createDescriptor(NimBLEUUID(uuid), properties, max_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new BLE Descriptor associated with this characteristic.
|
* @brief Create a new BLE Descriptor associated with this characteristic.
|
||||||
* @param [in] uuid - The UUID of the descriptor.
|
* @param [in] uuid - The UUID of the descriptor.
|
||||||
|
@ -85,7 +73,7 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
|
||||||
* @param [in] max_len - The max length in bytes of the descriptor value.
|
* @param [in] max_len - The max length in bytes of the descriptor value.
|
||||||
* @return The new BLE descriptor.
|
* @return The new BLE descriptor.
|
||||||
*/
|
*/
|
||||||
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
|
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) {
|
||||||
NimBLEDescriptor* pDescriptor = nullptr;
|
NimBLEDescriptor* pDescriptor = nullptr;
|
||||||
if (uuid == NimBLEUUID(uint16_t(0x2904))) {
|
if (uuid == NimBLEUUID(uint16_t(0x2904))) {
|
||||||
pDescriptor = new NimBLE2904(this);
|
pDescriptor = new NimBLE2904(this);
|
||||||
|
@ -97,47 +85,52 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
|
||||||
return pDescriptor;
|
return pDescriptor;
|
||||||
} // createDescriptor
|
} // createDescriptor
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add a descriptor to the characteristic.
|
* @brief Add a descriptor to the characteristic.
|
||||||
* @param [in] pDescriptor A pointer to the descriptor to add.
|
* @param [in] pDescriptor A pointer to the descriptor to add.
|
||||||
*/
|
*/
|
||||||
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) {
|
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) {
|
||||||
bool foundRemoved = false;
|
bool foundRemoved = false;
|
||||||
|
if (pDescriptor->getRemoved() > 0) {
|
||||||
if(pDescriptor->m_removed > 0) {
|
for (const auto& dsc : m_vDescriptors) {
|
||||||
for(auto& it : m_dscVec) {
|
if (dsc == pDescriptor) {
|
||||||
if(it == pDescriptor) {
|
|
||||||
foundRemoved = true;
|
foundRemoved = true;
|
||||||
pDescriptor->m_removed = 0;
|
pDescriptor->setRemoved(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!foundRemoved) {
|
// Check if the descriptor is already in the vector and if so, return.
|
||||||
m_dscVec.push_back(pDescriptor);
|
for (const auto& dsc : m_vDescriptors) {
|
||||||
|
if (dsc == pDescriptor) {
|
||||||
|
pDescriptor->setCharacteristic(this); // Update the characteristic pointer in the descriptor.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundRemoved) {
|
||||||
|
m_vDescriptors.push_back(pDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pDescriptor->setCharacteristic(this);
|
pDescriptor->setCharacteristic(this);
|
||||||
NimBLEDevice::getServer()->serviceChanged();
|
NimBLEDevice::getServer()->serviceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove a descriptor from the characteristic.
|
* @brief Remove a descriptor from the characteristic.
|
||||||
* @param[in] pDescriptor A pointer to the descriptor instance to remove from the characteristic.
|
* @param[in] pDescriptor A pointer to the descriptor instance to remove from the characteristic.
|
||||||
* @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources.
|
* @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources.
|
||||||
*/
|
*/
|
||||||
void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc) {
|
void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc) {
|
||||||
// Check if the descriptor was already removed and if so, check if this
|
// 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.
|
// is being called to delete the object and do so if requested.
|
||||||
// Otherwise, ignore the call and return.
|
// Otherwise, ignore the call and return.
|
||||||
if(pDescriptor->m_removed > 0) {
|
if (pDescriptor->getRemoved() > 0) {
|
||||||
if(deleteDsc) {
|
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) {
|
if ((*it) == pDescriptor) {
|
||||||
delete *it;
|
delete (*it);
|
||||||
m_dscVec.erase(it);
|
m_vDescriptors.erase(it);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,30 +139,28 @@ void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool
|
||||||
return;
|
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();
|
NimBLEDevice::getServer()->serviceChanged();
|
||||||
} // removeDescriptor
|
} // removeDescriptor
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the BLE Descriptor for the given UUID.
|
* @brief Return the BLE Descriptor for the given UUID.
|
||||||
* @param [in] uuid The UUID of the descriptor.
|
* @param [in] uuid The UUID of the descriptor.
|
||||||
* @return A pointer to the descriptor object or nullptr if not found.
|
* @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));
|
return getDescriptorByUUID(NimBLEUUID(uuid));
|
||||||
} // getDescriptorByUUID
|
} // getDescriptorByUUID
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the BLE Descriptor for the given UUID.
|
* @brief Return the BLE Descriptor for the given UUID.
|
||||||
* @param [in] uuid The UUID of the descriptor.
|
* @param [in] uuid The UUID of the descriptor.
|
||||||
* @return A pointer to the descriptor object or nullptr if not found.
|
* @return A pointer to the descriptor object or nullptr if not found.
|
||||||
*/
|
*/
|
||||||
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
|
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID& uuid) const {
|
||||||
for (auto &it : m_dscVec) {
|
for (const auto& dsc : m_vDescriptors) {
|
||||||
if (it->getUUID() == uuid) {
|
if (dsc->getUUID() == uuid) {
|
||||||
return it;
|
return dsc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -180,351 +171,230 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
|
||||||
* @param [in] handle The handle of the descriptor.
|
* @param [in] handle The handle of the descriptor.
|
||||||
* @return A pointer to the descriptor object or nullptr if not found.
|
* @return A pointer to the descriptor object or nullptr if not found.
|
||||||
*/
|
*/
|
||||||
NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) {
|
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) const {
|
||||||
for (auto &it : m_dscVec) {
|
for (const auto& dsc : m_vDescriptors) {
|
||||||
if (it->getHandle() == handle) {
|
if (dsc->getHandle() == handle) {
|
||||||
return it;
|
return dsc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
} // getDescriptorByHandle
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the handle of the characteristic.
|
|
||||||
* @return The handle of the characteristic.
|
|
||||||
*/
|
|
||||||
uint16_t NimBLECharacteristic::getHandle() {
|
|
||||||
return m_handle;
|
|
||||||
} // getHandle
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the properties of the characteristic.
|
* @brief Get the properties of the characteristic.
|
||||||
* @return The properties of the characteristic.
|
* @return The properties of the characteristic.
|
||||||
*/
|
*/
|
||||||
uint16_t NimBLECharacteristic::getProperties() {
|
uint16_t NimBLECharacteristic::getProperties() const {
|
||||||
return m_properties;
|
return m_properties;
|
||||||
} // getProperties
|
} // 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;
|
return m_pService;
|
||||||
} // getService
|
} // getService
|
||||||
|
|
||||||
|
void NimBLECharacteristic::setService(NimBLEService* pService) {
|
||||||
void NimBLECharacteristic::setService(NimBLEService *pService) {
|
|
||||||
m_pService = pService;
|
m_pService = pService;
|
||||||
}
|
} // setService
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the number of clients subscribed to the characteristic.
|
* @brief Get the number of clients subscribed to the characteristic.
|
||||||
* @returns Number of clients subscribed to notifications / indications.
|
* @returns Number of clients subscribed to notifications / indications.
|
||||||
*/
|
*/
|
||||||
size_t NimBLECharacteristic::getSubscribedCount() {
|
size_t NimBLECharacteristic::getSubscribedCount() const {
|
||||||
return m_subscribedVec.size();
|
return m_subscribedVec.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the subscribe status for this characteristic.\n
|
* @brief Set the subscribe status for this characteristic.\n
|
||||||
* This will maintain a vector of subscribed clients and their indicate/notify status.
|
* This will maintain a vector of subscribed clients and their indicate/notify status.
|
||||||
*/
|
*/
|
||||||
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
|
void NimBLECharacteristic::setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo) {
|
||||||
NimBLEConnInfo peerInfo;
|
|
||||||
if(ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc) != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t subVal = 0;
|
uint16_t subVal = 0;
|
||||||
if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
|
if (event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
|
||||||
subVal |= NIMBLE_SUB_NOTIFY;
|
subVal |= NIMBLE_SUB_NOTIFY;
|
||||||
}
|
}
|
||||||
if(event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
|
if (event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
|
||||||
subVal |= NIMBLE_SUB_INDICATE;
|
subVal |= NIMBLE_SUB_INDICATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
|
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", connInfo.getConnHandle(), subVal);
|
||||||
event->subscribe.conn_handle, subVal);
|
|
||||||
|
|
||||||
if(!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
|
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();
|
auto it = m_subscribedVec.begin();
|
||||||
for(;it != m_subscribedVec.end(); ++it) {
|
for (; it != m_subscribedVec.end(); ++it) {
|
||||||
if((*it).first == event->subscribe.conn_handle) {
|
if ((*it).first == connInfo.getConnHandle()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(subVal > 0) {
|
if (subVal > 0) {
|
||||||
if(it == m_subscribedVec.end()) {
|
if (it == m_subscribedVec.end()) {
|
||||||
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
|
m_subscribedVec.push_back({connInfo.getConnHandle(), subVal});
|
||||||
} else {
|
} else {
|
||||||
(*it).second = subVal;
|
(*it).second = subVal;
|
||||||
}
|
}
|
||||||
} else if(it != m_subscribedVec.end()) {
|
} else if (it != m_subscribedVec.end()) {
|
||||||
m_subscribedVec.erase(it);
|
m_subscribedVec.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pCallbacks->onSubscribe(this, peerInfo, subVal);
|
m_pCallbacks->onSubscribe(this, connInfo, subVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send an indication.
|
* @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() {
|
void NimBLECharacteristic::indicate(uint16_t conn_handle) const {
|
||||||
notify(false);
|
sendValue(m_value.data(), m_value.size(), false, conn_handle);
|
||||||
} // indicate
|
} // indicate
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send an indication.
|
* @brief Send an indication.
|
||||||
* @param[in] value A pointer to the data to send.
|
* @param[in] value A pointer to the data to send.
|
||||||
* @param[in] length The length of 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) {
|
void NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_t conn_handle) const {
|
||||||
notify(value, length, false);
|
sendValue(value, length, false, conn_handle);
|
||||||
} // indicate
|
} // indicate
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send an indication.
|
* @brief Send an indication.
|
||||||
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
|
* @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) {
|
void NimBLECharacteristic::indicate(const std::vector<uint8_t>& value, uint16_t conn_handle) const {
|
||||||
notify(value.data(), value.size(), false);
|
sendValue(value.data(), value.size(), false, conn_handle);
|
||||||
} // indicate
|
} // indicate
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send a notification or indication.
|
* @brief Send a notification.
|
||||||
* @param[in] is_notification if true sends a notification, false sends an indication.
|
* @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
|
||||||
* @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.
|
* the notification to all subscribed clients.
|
||||||
*/
|
*/
|
||||||
void NimBLECharacteristic::notify(bool is_notification, uint16_t conn_handle) {
|
void NimBLECharacteristic::notify(uint16_t conn_handle) const {
|
||||||
notify(m_value.data(), m_value.length(), is_notification, conn_handle);
|
sendValue(m_value.data(), m_value.size(), true, conn_handle);
|
||||||
} // notify
|
} // 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] 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 an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
|
||||||
* @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.
|
* the notification to all subscribed clients.
|
||||||
*/
|
*/
|
||||||
void NimBLECharacteristic::notify(const std::vector<uint8_t>& value, bool is_notification, uint16_t conn_handle) {
|
void NimBLECharacteristic::notify(const std::vector<uint8_t>& value, uint16_t conn_handle) const {
|
||||||
notify(value.data(), value.size(), is_notification, conn_handle);
|
sendValue(value.data(), value.size(), true, conn_handle);
|
||||||
} // notify
|
} // 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] value A pointer to the data to send.
|
||||||
* @param[in] length The length of 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] 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) {
|
void NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length);
|
NIMBLE_LOGD(LOG_TAG, ">> sendValue");
|
||||||
|
|
||||||
if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
|
if (is_notification && !(getProperties() & NIMBLE_PROPERTY::NOTIFY)) {
|
||||||
!(m_properties & NIMBLE_PROPERTY::INDICATE))
|
NIMBLE_LOGE(LOG_TAG, "<< sendValue: notification not enabled for characteristic");
|
||||||
{
|
|
||||||
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.");
|
|
||||||
return;
|
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) ||
|
if (!m_subscribedVec.size()) {
|
||||||
(m_properties & BLE_GATT_CHR_F_READ_AUTHOR) ||
|
NIMBLE_LOGD(LOG_TAG, "<< sendValue: No clients subscribed.");
|
||||||
(m_properties & BLE_GATT_CHR_F_READ_ENC);
|
return;
|
||||||
int rc = 0;
|
}
|
||||||
|
|
||||||
for (auto &it : m_subscribedVec) {
|
for (const auto& it : m_subscribedVec) {
|
||||||
// check if need a specific client
|
// 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)) {
|
if ((conn_handle <= BLE_HCI_LE_CONN_HANDLE_MAX) && (it.first != conn_handle)) {
|
||||||
continue;
|
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 (!is_notification && !(it.second & NIMBLE_SUB_INDICATE)) {
|
||||||
if(_mtu == 0 || it.second == 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if security requirements are satisfied
|
// check if security requirements are satisfied
|
||||||
if(reqSec) {
|
if ((getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || (getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) ||
|
||||||
struct ble_gap_conn_desc desc;
|
(getProperties() & BLE_GATT_CHR_F_READ_ENC)) {
|
||||||
rc = ble_gap_conn_find(it.first, &desc);
|
ble_gap_conn_desc desc;
|
||||||
if(rc != 0 || !desc.sec_state.encrypted) {
|
if (ble_gap_conn_find(it.first, &desc) != 0 || !desc.sec_state.encrypted) {
|
||||||
continue;
|
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
|
// 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 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.
|
// We also must create it in each loop iteration because it is consumed with each host call.
|
||||||
os_mbuf *om = ble_hs_mbuf_from_flat(value, length);
|
os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
|
||||||
|
if (!om) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
|
if (is_notification) {
|
||||||
if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
|
ble_gattc_notify_custom(it.first, getHandle(), om);
|
||||||
NIMBLE_LOGE(LOG_TAG, "prior Indication in progress");
|
} else {
|
||||||
|
if (!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
|
||||||
|
NIMBLE_LOGE(LOG_TAG, "<< sendValue: waiting for previous indicate");
|
||||||
os_mbuf_free_chain(om);
|
os_mbuf_free_chain(om);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = ble_gattc_indicate_custom(it.first, m_handle, om);
|
if (ble_gattc_indicate_custom(it.first, getHandle(), om) != 0) {
|
||||||
if(rc != 0){
|
|
||||||
NimBLEDevice::getServer()->clearIndicateWait(it.first);
|
NimBLEDevice::getServer()->clearIndicateWait(it.first);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ble_gattc_notify_custom(it.first, m_handle, om);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< notify");
|
NIMBLE_LOGD(LOG_TAG, "<< sendValue");
|
||||||
} // Notify
|
} // 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.
|
* @brief Set the callback handlers for this characteristic.
|
||||||
|
@ -532,60 +402,31 @@ void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_n
|
||||||
* used to define any callbacks for the characteristic.
|
* used to define any callbacks for the characteristic.
|
||||||
*/
|
*/
|
||||||
void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) {
|
void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) {
|
||||||
if (pCallbacks != nullptr){
|
if (pCallbacks != nullptr) {
|
||||||
m_pCallbacks = pCallbacks;
|
m_pCallbacks = pCallbacks;
|
||||||
} else {
|
} else {
|
||||||
m_pCallbacks = &defaultCallback;
|
m_pCallbacks = &defaultCallback;
|
||||||
}
|
}
|
||||||
} // setCallbacks
|
} // setCallbacks
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the callback handlers for this characteristic.
|
* @brief Get the callback handlers for this characteristic.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
|
NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() const {
|
||||||
return m_pCallbacks;
|
return m_pCallbacks;
|
||||||
} //getCallbacks
|
} // 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.
|
* @brief Return a string representation of the characteristic.
|
||||||
* @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";
|
std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x";
|
||||||
char hex[5];
|
char hex[5];
|
||||||
snprintf(hex, sizeof(hex), "%04x", m_handle);
|
snprintf(hex, sizeof(hex), "%04x", getHandle());
|
||||||
res += hex;
|
res += hex;
|
||||||
res += " ";
|
res += " ";
|
||||||
if (m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read ";
|
if (m_properties & BLE_GATT_CHR_PROP_READ) res += "Read ";
|
||||||
if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write ";
|
if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write ";
|
||||||
if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse ";
|
if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse ";
|
||||||
if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast ";
|
if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast ";
|
||||||
|
@ -594,7 +435,6 @@ std::string NimBLECharacteristic::toString() {
|
||||||
return res;
|
return res;
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function to support a read request.
|
* @brief Callback function to support a read request.
|
||||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
* @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");
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
|
||||||
} // onRead
|
} // onRead
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function to support a write request.
|
* @brief Callback function to support a write request.
|
||||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
* @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");
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
|
||||||
} // onWrite
|
} // 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.
|
* @brief Callback function to support a Notify/Indicate Status report.
|
||||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
* @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");
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
|
||||||
} // onStatus
|
} // onStatus
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function called when a client changes subscription status.
|
* @brief Callback function called when a client changes subscription status.
|
||||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
* @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,
|
void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
|
||||||
NimBLEConnInfo& connInfo,
|
NimBLEConnInfo& connInfo,
|
||||||
uint16_t subValue)
|
uint16_t subValue) {
|
||||||
{
|
|
||||||
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
|
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,131 +11,71 @@
|
||||||
* Author: kolban
|
* Author: kolban
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MAIN_NIMBLECHARACTERISTIC_H_
|
#ifndef NIMBLE_CPP_CHARACTERISTIC_H_
|
||||||
#define MAIN_NIMBLECHARACTERISTIC_H_
|
#define NIMBLE_CPP_CHARACTERISTIC_H_
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
|
||||||
#include "host/ble_hs.h"
|
|
||||||
#else
|
|
||||||
#include "nimble/nimble/host/include/host/ble_hs.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**** FIX COMPILATION ****/
|
|
||||||
#undef min
|
|
||||||
#undef max
|
|
||||||
/**************************/
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
READ = BLE_GATT_CHR_F_READ,
|
|
||||||
READ_ENC = BLE_GATT_CHR_F_READ_ENC,
|
|
||||||
READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN,
|
|
||||||
READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR,
|
|
||||||
WRITE = BLE_GATT_CHR_F_WRITE,
|
|
||||||
WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP,
|
|
||||||
WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC,
|
|
||||||
WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN,
|
|
||||||
WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR,
|
|
||||||
BROADCAST = BLE_GATT_CHR_F_BROADCAST,
|
|
||||||
NOTIFY = BLE_GATT_CHR_F_NOTIFY,
|
|
||||||
INDICATE = BLE_GATT_CHR_F_INDICATE
|
|
||||||
} NIMBLE_PROPERTY;
|
|
||||||
|
|
||||||
#include "NimBLEService.h"
|
|
||||||
#include "NimBLEDescriptor.h"
|
|
||||||
#include "NimBLEAttValue.h"
|
|
||||||
#include "NimBLEConnInfo.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class NimBLEService;
|
|
||||||
class NimBLEDescriptor;
|
|
||||||
class NimBLECharacteristicCallbacks;
|
class NimBLECharacteristicCallbacks;
|
||||||
|
class NimBLECharacteristic;
|
||||||
|
|
||||||
|
# include "NimBLELocalValueAttribute.h"
|
||||||
|
# include "NimBLEServer.h"
|
||||||
|
# include "NimBLEService.h"
|
||||||
|
# include "NimBLEDescriptor.h"
|
||||||
|
# include "NimBLEAttValue.h"
|
||||||
|
# include "NimBLEConnInfo.h"
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
# include <vector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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
|
* 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.
|
* can be read and written to by a BLE client.
|
||||||
*/
|
*/
|
||||||
class NimBLECharacteristic {
|
class NimBLECharacteristic : public NimBLELocalValueAttribute {
|
||||||
public:
|
public:
|
||||||
NimBLECharacteristic(const char* uuid,
|
NimBLECharacteristic(const char* uuid,
|
||||||
uint16_t properties =
|
uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
|
||||||
NIMBLE_PROPERTY::READ |
|
|
||||||
NIMBLE_PROPERTY::WRITE,
|
|
||||||
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
|
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
|
||||||
NimBLEService* pService = nullptr);
|
NimBLEService* pService = nullptr);
|
||||||
NimBLECharacteristic(const NimBLEUUID &uuid,
|
NimBLECharacteristic(const NimBLEUUID& uuid,
|
||||||
uint16_t properties =
|
uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
|
||||||
NIMBLE_PROPERTY::READ |
|
|
||||||
NIMBLE_PROPERTY::WRITE,
|
|
||||||
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
|
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
|
||||||
NimBLEService* pService = nullptr);
|
NimBLEService* pService = nullptr);
|
||||||
|
|
||||||
~NimBLECharacteristic();
|
~NimBLECharacteristic();
|
||||||
|
|
||||||
uint16_t getHandle();
|
std::string toString() const;
|
||||||
NimBLEUUID getUUID();
|
size_t getSubscribedCount() const;
|
||||||
std::string toString();
|
void addDescriptor(NimBLEDescriptor* pDescriptor);
|
||||||
void indicate();
|
void removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc = false);
|
||||||
void indicate(const uint8_t* value, size_t length);
|
uint16_t getProperties() const;
|
||||||
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();
|
|
||||||
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);
|
|
||||||
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
|
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,
|
NimBLEDescriptor* createDescriptor(const char* uuid,
|
||||||
uint32_t properties =
|
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
|
||||||
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);
|
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 ************************/
|
/*********************** 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.
|
* @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.
|
* @tparam T The a reference to a class containing the data to send.
|
||||||
|
@ -143,14 +83,14 @@ public:
|
||||||
* @param[in] is_notification if true sends a notification, false sends an indication.
|
* @param[in] is_notification if true sends a notification, false sends an indication.
|
||||||
* @details Only used if the <type\> has a `c_str()` method.
|
* @details Only used if the <type\> has a `c_str()` method.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template <typename T>
|
||||||
#ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
void
|
void
|
||||||
#else
|
# else
|
||||||
typename std::enable_if<Has_c_str_len<T>::value, void>::type
|
typename std::enable_if<Has_c_str_len<T>::value, void>::type
|
||||||
#endif
|
# endif
|
||||||
notify(const T& value, bool is_notification = true) {
|
notify(const T& value, bool is_notification = true) const {
|
||||||
notify((uint8_t*)value.c_str(), value.length(), is_notification);
|
notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), is_notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,39 +99,35 @@ public:
|
||||||
* @param[in] value The <type\>value to set.
|
* @param[in] value The <type\>value to set.
|
||||||
* @details Only used if the <type\> has a `c_str()` method.
|
* @details Only used if the <type\> has a `c_str()` method.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template <typename T>
|
||||||
#ifdef _DOXYGEN_
|
# ifdef _DOXYGEN_
|
||||||
void
|
void
|
||||||
#else
|
# else
|
||||||
typename std::enable_if<Has_c_str_len<T>::value, void>::type
|
typename std::enable_if<Has_c_str_len<T>::value, void>::type
|
||||||
#endif
|
# endif
|
||||||
indicate(const T& value) {
|
indicate(const T& value) const {
|
||||||
indicate((uint8_t*)value.c_str(), value.length());
|
indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class NimBLEServer;
|
friend class NimBLEServer;
|
||||||
friend class NimBLEService;
|
friend class NimBLEService;
|
||||||
|
|
||||||
void setService(NimBLEService *pService);
|
void setService(NimBLEService* pService);
|
||||||
void setSubscribe(struct ble_gap_event *event);
|
void setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo);
|
||||||
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
void readEvent(NimBLEConnInfo& connInfo) override;
|
||||||
struct ble_gatt_access_ctxt *ctxt, void *arg);
|
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;
|
NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
|
||||||
uint16_t m_handle;
|
NimBLEService* m_pService{nullptr};
|
||||||
uint16_t m_properties;
|
std::vector<NimBLEDescriptor*> m_vDescriptors{};
|
||||||
NimBLECharacteristicCallbacks* m_pCallbacks;
|
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec{};
|
||||||
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;
|
|
||||||
}; // NimBLECharacteristic
|
}; // NimBLECharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
|
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
|
||||||
*
|
*
|
||||||
|
@ -200,14 +136,13 @@ private:
|
||||||
* sub-classed instance of this class and will be notified when such an event happens.
|
* sub-classed instance of this class and will be notified when such an event happens.
|
||||||
*/
|
*/
|
||||||
class NimBLECharacteristicCallbacks {
|
class NimBLECharacteristicCallbacks {
|
||||||
public:
|
public:
|
||||||
virtual ~NimBLECharacteristicCallbacks(){}
|
virtual ~NimBLECharacteristicCallbacks() {}
|
||||||
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
||||||
virtual void onWrite(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 onStatus(NimBLECharacteristic* pCharacteristic, int code);
|
||||||
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
|
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#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)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
#include "NimBLEClient.h"
|
#include "NimBLEClient.h"
|
||||||
|
#include "NimBLERemoteService.h"
|
||||||
|
#include "NimBLERemoteCharacteristic.h"
|
||||||
#include "NimBLEDevice.h"
|
#include "NimBLEDevice.h"
|
||||||
#include "NimBLELog.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.
|
* Called automatically when a characteristic or descriptor requires encryption or authentication to access it.
|
||||||
* @return True on success.
|
* @return True on success.
|
||||||
*/
|
*/
|
||||||
bool NimBLEClient::secureConnection() {
|
bool NimBLEClient::secureConnection() const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> secureConnection()");
|
NIMBLE_LOGD(LOG_TAG, ">> secureConnection()");
|
||||||
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
|
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;
|
int retryCount = 1;
|
||||||
|
|
||||||
|
@ -542,7 +544,7 @@ void NimBLEClient::setConnectTimeout(uint32_t time) {
|
||||||
* @brief Get the connection id for this client.
|
* @brief Get the connection id for this client.
|
||||||
* @return The connection id.
|
* @return The connection id.
|
||||||
*/
|
*/
|
||||||
uint16_t NimBLEClient::getConnId() {
|
uint16_t NimBLEClient::getConnId() const {
|
||||||
return m_conn_id;
|
return m_conn_id;
|
||||||
} // getConnId
|
} // getConnId
|
||||||
|
|
||||||
|
@ -610,7 +612,7 @@ bool NimBLEClient::setConnection(uint16_t conn_id) {
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the address of the peer.
|
* @brief Retrieve the address of the peer.
|
||||||
*/
|
*/
|
||||||
NimBLEAddress NimBLEClient::getPeerAddress() {
|
NimBLEAddress NimBLEClient::getPeerAddress() const {
|
||||||
return m_peerAddress;
|
return m_peerAddress;
|
||||||
} // getPeerAddress
|
} // getPeerAddress
|
||||||
|
|
||||||
|
@ -776,7 +778,7 @@ bool NimBLEClient::discoverAttributes() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto chr: svc->m_characteristicVector) {
|
for(auto chr: svc->m_vChars) {
|
||||||
if (!chr->retrieveDescriptors()) {
|
if (!chr->retrieveDescriptors()) {
|
||||||
return false;
|
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.
|
* 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
|
* @return true on success otherwise false if an error occurred
|
||||||
*/
|
*/
|
||||||
bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
|
bool NimBLEClient::retrieveServices(const NimBLEUUID *uuidFilter) {
|
||||||
/**
|
/**
|
||||||
* Design
|
* Design
|
||||||
* ------
|
* ------
|
||||||
|
@ -811,10 +813,10 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
|
||||||
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
|
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
|
||||||
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
|
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);
|
rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData);
|
||||||
} else {
|
} 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);
|
NimBLEClient::serviceDiscoveredCB, &taskData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,7 +974,7 @@ NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handl
|
||||||
* @brief Get the current mtu of this connection.
|
* @brief Get the current mtu of this connection.
|
||||||
* @returns The MTU value.
|
* @returns The MTU value.
|
||||||
*/
|
*/
|
||||||
uint16_t NimBLEClient::getMTU() {
|
uint16_t NimBLEClient::getMTU() const {
|
||||||
return ble_att_mtu(m_conn_id);
|
return ble_att_mtu(m_conn_id);
|
||||||
} // getMTU
|
} // getMTU
|
||||||
|
|
||||||
|
@ -1086,7 +1088,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cVector = &it->m_characteristicVector;
|
auto cVector = &it->m_vChars;
|
||||||
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
|
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
|
||||||
it->getUUID().toString().c_str(),
|
it->getUUID().toString().c_str(),
|
||||||
event->notify_rx.attr_handle);
|
event->notify_rx.attr_handle);
|
||||||
|
@ -1325,20 +1327,20 @@ bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, con
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NimBLEClientCallbacks::onPassKeyEntry(const NimBLEConnInfo& connInfo){
|
void NimBLEClientCallbacks::onPassKeyEntry(NimBLEConnInfo& connInfo){
|
||||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyEntry: default: 123456");
|
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyEntry: default: 123456");
|
||||||
NimBLEDevice::injectPassKey(connInfo, 123456);
|
NimBLEDevice::injectPassKey(connInfo, 123456);
|
||||||
} //onPassKeyEntry
|
} //onPassKeyEntry
|
||||||
|
|
||||||
void NimBLEClientCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
|
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NimBLEClientCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
|
void NimBLEClientCallbacks::onIdentity(NimBLEConnInfo& connInfo){
|
||||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onIdentity: default");
|
NIMBLE_LOGD("NimBLEClientCallbacks", "onIdentity: default");
|
||||||
} // onIdentity
|
} // onIdentity
|
||||||
|
|
||||||
void NimBLEClientCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
|
void NimBLEClientCallbacks::onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pin){
|
||||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true");
|
NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true");
|
||||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,14 @@
|
||||||
#include "NimBLEConnInfo.h"
|
#include "NimBLEConnInfo.h"
|
||||||
#include "NimBLEAttValue.h"
|
#include "NimBLEAttValue.h"
|
||||||
#include "NimBLEAdvertisedDevice.h"
|
#include "NimBLEAdvertisedDevice.h"
|
||||||
#include "NimBLERemoteService.h"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class NimBLERemoteService;
|
class NimBLERemoteService;
|
||||||
class NimBLERemoteCharacteristic;
|
class NimBLERemoteCharacteristic;
|
||||||
class NimBLEClientCallbacks;
|
|
||||||
class NimBLEAdvertisedDevice;
|
class NimBLEAdvertisedDevice;
|
||||||
|
class NimBLEClientCallbacks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A model of a %BLE client.
|
* @brief A model of a %BLE client.
|
||||||
|
@ -42,7 +41,7 @@ public:
|
||||||
bool connect(const NimBLEAddress &address, bool deleteAttributes = true);
|
bool connect(const NimBLEAddress &address, bool deleteAttributes = true);
|
||||||
bool connect(bool deleteAttributes = true);
|
bool connect(bool deleteAttributes = true);
|
||||||
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
|
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
|
||||||
NimBLEAddress getPeerAddress();
|
NimBLEAddress getPeerAddress() const;
|
||||||
void setPeerAddress(const NimBLEAddress &address);
|
void setPeerAddress(const NimBLEAddress &address);
|
||||||
int getRssi();
|
int getRssi();
|
||||||
std::vector<NimBLERemoteService*>* getServices(bool refresh = false);
|
std::vector<NimBLERemoteService*>* getServices(bool refresh = false);
|
||||||
|
@ -60,12 +59,12 @@ public:
|
||||||
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
|
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
|
||||||
bool deleteCallbacks = true);
|
bool deleteCallbacks = true);
|
||||||
std::string toString();
|
std::string toString();
|
||||||
uint16_t getConnId();
|
uint16_t getConnId() const;
|
||||||
void clearConnection();
|
void clearConnection();
|
||||||
bool setConnection(NimBLEConnInfo &conn_info);
|
bool setConnection(NimBLEConnInfo &conn_info);
|
||||||
bool setConnection(uint16_t conn_id);
|
bool setConnection(uint16_t conn_id);
|
||||||
uint16_t getMTU();
|
uint16_t getMTU() const;
|
||||||
bool secureConnection();
|
bool secureConnection() const;
|
||||||
void setConnectTimeout(uint32_t timeout);
|
void setConnectTimeout(uint32_t timeout);
|
||||||
void setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
|
void setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
|
||||||
uint16_t latency, uint16_t timeout,
|
uint16_t latency, uint16_t timeout,
|
||||||
|
@ -93,16 +92,16 @@ private:
|
||||||
const struct ble_gatt_svc *service,
|
const struct ble_gatt_svc *service,
|
||||||
void *arg);
|
void *arg);
|
||||||
static void dcTimerCb(ble_npl_event *event);
|
static void dcTimerCb(ble_npl_event *event);
|
||||||
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
|
bool retrieveServices(const NimBLEUUID *uuidFilter = nullptr);
|
||||||
|
|
||||||
NimBLEAddress m_peerAddress;
|
NimBLEAddress m_peerAddress;
|
||||||
int m_lastErr;
|
mutable int m_lastErr;
|
||||||
uint16_t m_conn_id;
|
uint16_t m_conn_id;
|
||||||
bool m_connEstablished;
|
bool m_connEstablished;
|
||||||
bool m_deleteCallbacks;
|
bool m_deleteCallbacks;
|
||||||
int32_t m_connectTimeout;
|
int32_t m_connectTimeout;
|
||||||
NimBLEClientCallbacks* m_pClientCallbacks;
|
NimBLEClientCallbacks* m_pClientCallbacks;
|
||||||
ble_task_data_t* m_pTaskData;
|
mutable ble_task_data_t* m_pTaskData;
|
||||||
ble_npl_callout m_dcTimer;
|
ble_npl_callout m_dcTimer;
|
||||||
#if CONFIG_BT_NIMBLE_EXT_ADV
|
#if CONFIG_BT_NIMBLE_EXT_ADV
|
||||||
uint8_t m_phyMask;
|
uint8_t m_phyMask;
|
||||||
|
@ -149,27 +148,27 @@ public:
|
||||||
* @brief Called when server requests a passkey for pairing.
|
* @brief Called when server requests a passkey for pairing.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
* @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.
|
* @brief Called when the pairing procedure is complete.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n
|
* @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.
|
* 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.
|
* @brief Called when using numeric comparision for pairing.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||||
* @param [in] pin The pin to compare with the server.
|
* @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.
|
* @brief Called when the peer identity address is resolved.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
* @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 */
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
#ifndef NIMBLECONNINFO_H_
|
#ifndef NIMBLECONNINFO_H_
|
||||||
#define 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"
|
#include "NimBLEAddress.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Connection information.
|
* @brief Connection information.
|
||||||
*/
|
*/
|
||||||
class NimBLEConnInfo {
|
class NimBLEConnInfo {
|
||||||
friend class NimBLEServer;
|
public:
|
||||||
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 */
|
/** @brief Gets the over-the-air address of the connected peer */
|
||||||
NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); }
|
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 */
|
/** @brief Gets the key size used to encrypt the connection */
|
||||||
uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; }
|
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
|
#endif
|
||||||
|
|
|
@ -15,17 +15,24 @@
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
#include "NimBLEService.h"
|
# include "NimBLEService.h"
|
||||||
#include "NimBLEDescriptor.h"
|
# include "NimBLEDescriptor.h"
|
||||||
#include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
#include <string>
|
# include <string>
|
||||||
|
|
||||||
#define NULL_HANDLE (0xffff)
|
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLEDescriptor";
|
static const char* LOG_TAG = "NimBLEDescriptor";
|
||||||
static NimBLEDescriptorCallbacks defaultCallbacks;
|
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
|
* @brief Construct a descriptor
|
||||||
|
@ -34,239 +41,64 @@ static NimBLEDescriptorCallbacks defaultCallbacks;
|
||||||
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
|
* @param [in] 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.
|
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
|
||||||
*/
|
*/
|
||||||
NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len,
|
NimBLEDescriptor::NimBLEDescriptor(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic)
|
||||||
NimBLECharacteristic* pCharacteristic)
|
: NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallbacks}, m_pCharacteristic{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;
|
|
||||||
|
|
||||||
// Check if this is the client configuration descriptor and set to removed if true.
|
// Check if this is the client configuration descriptor and set to removed if true.
|
||||||
if (uuid == NimBLEUUID((uint16_t)0x2902)) {
|
if (uuid == NimBLEUUID((uint16_t)0x2902)) {
|
||||||
NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove.");
|
NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove.");
|
||||||
m_removed = 1;
|
setRemoved(NIMBLE_ATT_REMOVE_HIDE);
|
||||||
} else {
|
|
||||||
m_removed = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
|
// convert uint16_t properties to uint8_t for descriptor properties
|
||||||
m_properties |= BLE_ATT_F_READ;
|
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)) {
|
if (properties & (NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::WRITE)) {
|
||||||
m_properties |= BLE_ATT_F_WRITE;
|
descProperties |= BLE_ATT_F_WRITE;
|
||||||
}
|
}
|
||||||
if (properties & BLE_GATT_CHR_F_READ_ENC) {
|
if (properties & NIMBLE_PROPERTY::READ_ENC) {
|
||||||
m_properties |= BLE_ATT_F_READ_ENC;
|
descProperties |= BLE_ATT_F_READ_ENC;
|
||||||
}
|
}
|
||||||
if (properties & BLE_GATT_CHR_F_READ_AUTHEN) {
|
if (properties & NIMBLE_PROPERTY::READ_AUTHEN) {
|
||||||
m_properties |= BLE_ATT_F_READ_AUTHEN;
|
descProperties |= BLE_ATT_F_READ_AUTHEN;
|
||||||
}
|
}
|
||||||
if (properties & BLE_GATT_CHR_F_READ_AUTHOR) {
|
if (properties & NIMBLE_PROPERTY::READ_AUTHOR) {
|
||||||
m_properties |= BLE_ATT_F_READ_AUTHOR;
|
descProperties |= BLE_ATT_F_READ_AUTHOR;
|
||||||
}
|
}
|
||||||
if (properties & BLE_GATT_CHR_F_WRITE_ENC) {
|
if (properties & NIMBLE_PROPERTY::WRITE_ENC) {
|
||||||
m_properties |= BLE_ATT_F_WRITE_ENC;
|
descProperties |= BLE_ATT_F_WRITE_ENC;
|
||||||
}
|
}
|
||||||
if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) {
|
if (properties & NIMBLE_PROPERTY::WRITE_AUTHEN) {
|
||||||
m_properties |= BLE_ATT_F_WRITE_AUTHEN;
|
descProperties |= BLE_ATT_F_WRITE_AUTHEN;
|
||||||
}
|
}
|
||||||
if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) {
|
if (properties & NIMBLE_PROPERTY::WRITE_AUTHOR) {
|
||||||
m_properties |= BLE_ATT_F_WRITE_AUTHOR;
|
descProperties |= BLE_ATT_F_WRITE_AUTHOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setProperties(descProperties);
|
||||||
} // NimBLEDescriptor
|
} // 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.
|
* @brief Get the characteristic this descriptor belongs to.
|
||||||
* @return A pointer to 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;
|
return m_pCharacteristic;
|
||||||
} // getCharacteristic
|
} // 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.
|
* @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.
|
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
|
||||||
*/
|
*/
|
||||||
void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) {
|
void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) {
|
||||||
if (pCallbacks != nullptr){
|
if (pCallbacks != nullptr) {
|
||||||
m_pCallbacks = pCallbacks;
|
m_pCallbacks = pCallbacks;
|
||||||
} else {
|
} else {
|
||||||
m_pCallbacks = &defaultCallbacks;
|
m_pCallbacks = &defaultCallbacks;
|
||||||
}
|
}
|
||||||
} // setCallbacks
|
} // 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.
|
* @brief Set the characteristic this descriptor belongs to.
|
||||||
* @param [in] pChar A pointer to 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;
|
m_pCharacteristic = pChar;
|
||||||
} // setCharacteristic
|
} // setCharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a string representation of the descriptor.
|
* @brief Return a string representation of the descriptor.
|
||||||
* @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];
|
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;
|
std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex;
|
||||||
return res;
|
return res;
|
||||||
} // toString
|
} // 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.
|
* @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.
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||||
*/
|
*/
|
||||||
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
||||||
(void)pDescriptor;
|
|
||||||
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default");
|
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default");
|
||||||
} // onRead
|
} // onRead
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function to support a write request.
|
* @brief Callback function to support a write request.
|
||||||
* @param [in] pDescriptor The descriptor that is the source of the event.
|
* @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.
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||||
*/
|
*/
|
||||||
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
|
||||||
(void)pDescriptor;
|
|
||||||
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
|
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
|
||||||
} // onWrite
|
} // onWrite
|
||||||
|
|
||||||
|
|
|
@ -12,94 +12,52 @@
|
||||||
* Author: kolban
|
* Author: kolban
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MAIN_NIMBLEDESCRIPTOR_H_
|
#ifndef NIMBLE_CPP_DESCRIPTOR_H_
|
||||||
#define MAIN_NIMBLEDESCRIPTOR_H_
|
#define NIMBLE_CPP_DESCRIPTOR_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
#include "NimBLECharacteristic.h"
|
class NimBLEDescriptor;
|
||||||
#include "NimBLEUUID.h"
|
|
||||||
#include "NimBLEAttValue.h"
|
|
||||||
#include "NimBLEConnInfo.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class NimBLEService;
|
|
||||||
class NimBLECharacteristic;
|
|
||||||
class NimBLEDescriptorCallbacks;
|
class NimBLEDescriptorCallbacks;
|
||||||
|
|
||||||
|
# include "NimBLELocalValueAttribute.h"
|
||||||
|
# include "NimBLECharacteristic.h"
|
||||||
|
# include "NimBLEUUID.h"
|
||||||
|
# include "NimBLEAttValue.h"
|
||||||
|
# include "NimBLEConnInfo.h"
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A model of a %BLE descriptor.
|
* @brief A model of a BLE descriptor.
|
||||||
*/
|
*/
|
||||||
class NimBLEDescriptor {
|
class NimBLEDescriptor : public NimBLELocalValueAttribute {
|
||||||
public:
|
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,
|
uint16_t max_len,
|
||||||
NimBLECharacteristic* pCharacteristic = nullptr);
|
NimBLECharacteristic* pCharacteristic = nullptr);
|
||||||
|
~NimBLEDescriptor() = default;
|
||||||
|
|
||||||
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
|
std::string toString() const;
|
||||||
uint16_t max_len,
|
|
||||||
NimBLECharacteristic* pCharacteristic = nullptr);
|
|
||||||
|
|
||||||
~NimBLEDescriptor();
|
|
||||||
|
|
||||||
uint16_t getHandle();
|
|
||||||
NimBLEUUID getUUID();
|
|
||||||
std::string toString();
|
|
||||||
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
|
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
|
||||||
NimBLECharacteristic* getCharacteristic();
|
NimBLECharacteristic* getCharacteristic() const;
|
||||||
|
|
||||||
size_t getLength();
|
private:
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class NimBLECharacteristic;
|
friend class NimBLECharacteristic;
|
||||||
friend class NimBLEService;
|
friend class NimBLEService;
|
||||||
friend class NimBLE2904;
|
|
||||||
|
|
||||||
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|
||||||
struct ble_gatt_access_ctxt *ctxt, void *arg);
|
|
||||||
void setHandle(uint16_t handle);
|
|
||||||
void setCharacteristic(NimBLECharacteristic* pChar);
|
void setCharacteristic(NimBLECharacteristic* pChar);
|
||||||
|
void readEvent(NimBLEConnInfo& connInfo) override;
|
||||||
|
void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override;
|
||||||
|
|
||||||
NimBLEUUID m_uuid;
|
NimBLEDescriptorCallbacks* m_pCallbacks{nullptr};
|
||||||
uint16_t m_handle;
|
NimBLECharacteristic* m_pCharacteristic{nullptr};
|
||||||
NimBLEDescriptorCallbacks* m_pCallbacks;
|
|
||||||
NimBLECharacteristic* m_pCharacteristic;
|
|
||||||
uint8_t m_properties;
|
|
||||||
NimBLEAttValue m_value;
|
|
||||||
uint8_t m_removed;
|
|
||||||
}; // NimBLEDescriptor
|
}; // NimBLEDescriptor
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
|
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
|
||||||
*
|
*
|
||||||
|
@ -108,13 +66,13 @@ private:
|
||||||
* sub-classed instance of this class and will be notified when such an event happens.
|
* sub-classed instance of this class and will be notified when such an event happens.
|
||||||
*/
|
*/
|
||||||
class NimBLEDescriptorCallbacks {
|
class NimBLEDescriptorCallbacks {
|
||||||
public:
|
public:
|
||||||
virtual ~NimBLEDescriptorCallbacks(){}
|
virtual ~NimBLEDescriptorCallbacks() = default;
|
||||||
virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
||||||
virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "NimBLE2904.h"
|
# include "NimBLE2904.h"
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif /* 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"
|
# include "esp32-hal-bt.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "NimBLELog.h"
|
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
#include "NimBLEClient.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "NimBLELog.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLEDevice";
|
static const char* LOG_TAG = "NimBLEDevice";
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
#include "NimBLEClient.h"
|
class NimBLEClient;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
@ -46,6 +46,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#define BLEDevice NimBLEDevice
|
#define BLEDevice NimBLEDevice
|
||||||
#define BLEClient NimBLEClient
|
#define BLEClient NimBLEClient
|
||||||
|
@ -236,6 +237,12 @@ private:
|
||||||
#endif
|
#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 // CONFIG_BT_ENABLED
|
||||||
#endif // MAIN_NIMBLEDEVICE_H_
|
#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_
|
|
@ -15,17 +15,25 @@
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
#include "NimBLERemoteCharacteristic.h"
|
# include "NimBLERemoteCharacteristic.h"
|
||||||
#include "NimBLEUtils.h"
|
# include "NimBLERemoteDescriptor.h"
|
||||||
#include "NimBLELog.h"
|
# include "NimBLERemoteService.h"
|
||||||
|
# include "NimBLEClient.h"
|
||||||
|
# include "NimBLEUtils.h"
|
||||||
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
#include <climits>
|
# include <climits>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const NimBLEUUID* uuid;
|
||||||
|
void* task_data;
|
||||||
|
} desc_filter_t;
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor.
|
* @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:
|
* @param [in] ble_gatt_chr struct defined as:
|
||||||
* struct ble_gatt_chr {
|
* struct ble_gatt_chr {
|
||||||
* uint16_t def_handle;
|
* uint16_t def_handle;
|
||||||
|
@ -34,34 +42,12 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
||||||
* ble_uuid_any_t uuid;
|
* ble_uuid_any_t uuid;
|
||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
|
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(const NimBLERemoteService* svc, const ble_gatt_chr* chr)
|
||||||
const struct ble_gatt_chr *chr)
|
: NimBLERemoteValueAttribute{chr->uuid, chr->val_handle},
|
||||||
{
|
m_pRemoteService{svc},
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
|
m_properties{chr->properties},
|
||||||
switch (chr->uuid.u.type) {
|
m_notifyCallback{},
|
||||||
case BLE_UUID_TYPE_16:
|
m_vDescriptors{} {} // NimBLERemoteCharacteristic
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*@brief Destructor.
|
*@brief Destructor.
|
||||||
|
@ -70,266 +56,114 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
|
||||||
deleteDescriptors();
|
deleteDescriptors();
|
||||||
} // ~NimBLERemoteCharacteristic
|
} // ~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.
|
* @brief Callback used by the API when a descriptor is discovered or search complete.
|
||||||
*/
|
*/
|
||||||
int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
|
int NimBLERemoteCharacteristic::descriptorDiscCB(
|
||||||
const struct ble_gatt_error *error,
|
uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg) {
|
||||||
uint16_t chr_val_handle,
|
|
||||||
const struct ble_gatt_dsc *dsc,
|
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
int rc = error->status;
|
int rc = error->status;
|
||||||
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d",
|
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1);
|
||||||
rc, (rc == 0) ? dsc->handle : -1);
|
|
||||||
|
|
||||||
desc_filter_t *filter = (desc_filter_t*)arg;
|
auto filter = (desc_filter_t*)arg;
|
||||||
const NimBLEUUID *uuid_filter = filter->uuid;
|
auto pTaskData = (ble_task_data_t*)filter->task_data;
|
||||||
ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data;
|
const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->pATT;
|
||||||
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
|
const NimBLEUUID* uuidFilter = filter->uuid;
|
||||||
|
|
||||||
if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
|
if (pChr->getHandle() != chr_val_handle) {
|
||||||
return 0;
|
rc = BLE_HS_EDONE; // descriptor not for this characteristic
|
||||||
}
|
|
||||||
|
|
||||||
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 (rc == 0) {
|
if (rc == 0) {
|
||||||
pChar->m_endHandle = chr->def_handle - 1;
|
if (uuidFilter != nullptr) {
|
||||||
rc = BLE_HS_EDONE;
|
if (ble_uuid_cmp(uuidFilter->getBase(), &dsc->uuid.u) == 0) {
|
||||||
} else if (rc == BLE_HS_EDONE) {
|
rc = BLE_HS_EDONE; // Found the descriptor, stop the search
|
||||||
pChar->m_endHandle = pChar->getRemoteService()->getEndHandle();
|
|
||||||
} else {
|
} 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);
|
xTaskNotifyGive(pTaskData->task);
|
||||||
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Populate the descriptors (if any) for this characteristic.
|
* @brief Populate the descriptors (if any) for this characteristic.
|
||||||
* @param [in] the end handle of the characteristic, or the service, whichever comes first.
|
* @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());
|
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();
|
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
|
int rc = ble_gattc_disc_all_dscs(getClient()->getConnId(),
|
||||||
// The end handle is the next characteristic definition handle -1.
|
getHandle(),
|
||||||
if (m_endHandle == 0) {
|
|
||||||
rc = ble_gattc_disc_all_chrs(getRemoteService()->getClient()->getConnId(),
|
|
||||||
m_handle,
|
|
||||||
getRemoteService()->getEndHandle(),
|
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,
|
NimBLERemoteCharacteristic::descriptorDiscCB,
|
||||||
&filter);
|
&filter);
|
||||||
|
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ulTaskNotifyValueClear
|
# ifdef ulTaskNotifyValueClear
|
||||||
// Clear the task notification value to ensure we block
|
// Clear the task notification value to ensure we block
|
||||||
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
|
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
|
||||||
#endif
|
# endif
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
if (taskData.rc != 0) {
|
if (taskData.rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d",
|
NIMBLE_LOGE(LOG_TAG,
|
||||||
m_handle, m_endHandle, taskData.rc);
|
"<< 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
|
} // retrieveDescriptors
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
|
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
|
||||||
* @param [in] uuid The UUID of the descriptor to find.
|
* @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());
|
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) {
|
if (it->getUUID() == uuid) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
|
pDsc = it;
|
||||||
return it;
|
goto Done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t prev_size = m_descriptorVector.size();
|
if (retrieveDescriptors(&uuid)) {
|
||||||
if(retrieveDescriptors(&uuid)) {
|
if (m_vDescriptors.size() > prev_size) {
|
||||||
if(m_descriptorVector.size() > prev_size) {
|
pDsc = m_vDescriptors.back();
|
||||||
return m_descriptorVector.back();
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the request was successful but 16/32 bit uuid not found
|
// If the request was successful but 16/32 bit uuid not found
|
||||||
// try again with the 128 bit uuid.
|
// try again with the 128 bit uuid.
|
||||||
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
|
if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
|
||||||
uuid.bitSize() == BLE_UUID_TYPE_32)
|
|
||||||
{
|
|
||||||
NimBLEUUID uuid128(uuid);
|
NimBLEUUID uuid128(uuid);
|
||||||
uuid128.to128();
|
uuid128.to128();
|
||||||
if(retrieveDescriptors(&uuid128)) {
|
if (retrieveDescriptors(&uuid128)) {
|
||||||
if(m_descriptorVector.size() > prev_size) {
|
if (m_vDescriptors.size() > prev_size) {
|
||||||
return m_descriptorVector.back();
|
pDsc = m_vDescriptors.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -339,20 +173,20 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
|
||||||
uuid16.to16();
|
uuid16.to16();
|
||||||
// if the uuid was 128 bit but not of the BLE base type this check will fail
|
// 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 (uuid16.bitSize() == BLE_UUID_TYPE_16) {
|
||||||
if(retrieveDescriptors(&uuid16)) {
|
if (retrieveDescriptors(&uuid16)) {
|
||||||
if(m_descriptorVector.size() > prev_size) {
|
if (m_vDescriptors.size() > prev_size) {
|
||||||
return m_descriptorVector.back();
|
pDsc = m_vDescriptors.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
|
Done:
|
||||||
return nullptr;
|
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", pDsc ? "" : "not ");
|
||||||
|
return pDsc;
|
||||||
} // getDescriptor
|
} // getDescriptor
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get a pointer to the vector of found descriptors.
|
* @brief Get a pointer to the vector of found descriptors.
|
||||||
* @param [in] refresh If true the current descriptor vector will be cleared and\n
|
* @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.
|
* of this characteristic.
|
||||||
* @return A pointer to the vector of descriptors for 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) {
|
if (refresh) {
|
||||||
deleteDescriptors();
|
deleteDescriptors();
|
||||||
|
retrieveDescriptors();
|
||||||
|
}
|
||||||
|
|
||||||
if (!retrieveDescriptors()) {
|
return m_vDescriptors;
|
||||||
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &m_descriptorVector;
|
|
||||||
} // getDescriptors
|
} // getDescriptors
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get iterator to the beginning of the vector of remote descriptor pointers.
|
* @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.
|
* @return An iterator to the beginning of the vector of remote descriptor pointers.
|
||||||
*/
|
*/
|
||||||
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::begin() {
|
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::begin() const {
|
||||||
return m_descriptorVector.begin();
|
return m_vDescriptors.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get iterator to the end of the vector of remote descriptor pointers.
|
* @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.
|
* @return An iterator to the end of the vector of remote descriptor pointers.
|
||||||
*/
|
*/
|
||||||
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end() {
|
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end() const {
|
||||||
return m_descriptorVector.end();
|
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.
|
* @brief Get the remote service associated with this characteristic.
|
||||||
* @return 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;
|
return m_pRemoteService;
|
||||||
} // getRemoteService
|
} // 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.
|
* @brief Subscribe or unsubscribe for notifications or indications.
|
||||||
* @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
|
* @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
|
||||||
|
@ -564,23 +236,20 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
|
||||||
* If NULL is provided then no callback is performed.
|
* If NULL is provided then no callback is performed.
|
||||||
* @return false if writing to the descriptor failed.
|
* @return false if writing to the descriptor failed.
|
||||||
*/
|
*/
|
||||||
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
|
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
|
NIMBLE_LOGD(LOG_TAG, ">> setNotify()");
|
||||||
|
|
||||||
m_notifyCallback = notifyCallback;
|
m_notifyCallback = notifyCallback;
|
||||||
|
|
||||||
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
|
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
|
||||||
if(desc == nullptr) {
|
if (desc == nullptr) {
|
||||||
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
|
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
|
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
|
||||||
|
return desc->writeValue(reinterpret_cast<uint8_t*>(&val), 2, response);
|
||||||
return desc->writeValue((uint8_t *)&val, 2, response);
|
|
||||||
} // setNotify
|
} // setNotify
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Subscribe for notifications or indications.
|
* @brief Subscribe for notifications or indications.
|
||||||
* @param [in] notifications If true, subscribe for notifications, false subscribe for 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.
|
* If NULL is provided then no callback is performed.
|
||||||
* @return false if writing to the descriptor failed.
|
* @return false if writing to the descriptor failed.
|
||||||
*/
|
*/
|
||||||
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
|
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) const {
|
||||||
if(notifications) {
|
return setNotify(notifications ? 0x01 : 0x02, notifyCallback, response);
|
||||||
return setNotify(0x01, notifyCallback, response);
|
|
||||||
} else {
|
|
||||||
return setNotify(0x02, notifyCallback, response);
|
|
||||||
}
|
|
||||||
} // subscribe
|
} // subscribe
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Unsubscribe for notifications or indications.
|
* @brief Unsubscribe for notifications or indications.
|
||||||
* @param [in] response bool if true, require a write response from the descriptor write operation.
|
* @param [in] response bool if true, require a write response from the descriptor write operation.
|
||||||
* @return false if writing to the descriptor failed.
|
* @return false if writing to the descriptor failed.
|
||||||
*/
|
*/
|
||||||
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
|
bool NimBLERemoteCharacteristic::unsubscribe(bool response) const {
|
||||||
return setNotify(0x00, nullptr, response);
|
return setNotify(0x00, nullptr, response);
|
||||||
} // unsubscribe
|
} // unsubscribe
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delete the descriptors in the descriptor vector.
|
* @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
|
* object references. Since we allocated these in this class, we are also responsible for deleting
|
||||||
* them. This method does just that.
|
* them. This method does just that.
|
||||||
*/
|
*/
|
||||||
void NimBLERemoteCharacteristic::deleteDescriptors() {
|
void NimBLERemoteCharacteristic::deleteDescriptors() const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
|
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
|
||||||
|
|
||||||
for(auto &it: m_descriptorVector) {
|
for (const auto& it : m_vDescriptors) {
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
m_descriptorVector.clear();
|
std::vector<NimBLERemoteDescriptor*>().swap(m_vDescriptors);
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors");
|
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors");
|
||||||
} // deleteDescriptors
|
} // deleteDescriptors
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delete descriptor by UUID
|
* @brief Delete descriptor by UUID
|
||||||
* @param [in] uuid The UUID of the descriptor to be deleted.
|
* @param [in] uuid The UUID of the descriptor to be deleted.
|
||||||
* @return Number of descriptors left in the vector.
|
* @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");
|
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) {
|
if ((*it)->getUUID() == uuid) {
|
||||||
delete *it;
|
delete (*it);
|
||||||
m_descriptorVector.erase(it);
|
m_vDescriptors.erase(it);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor");
|
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor");
|
||||||
|
return m_vDescriptors.size();
|
||||||
return m_descriptorVector.size();
|
|
||||||
} // deleteDescriptor
|
} // 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;
|
* @brief Convert a NimBLERemoteCharacteristic to a string representation;
|
||||||
* @return a String representation.
|
* @return a String representation.
|
||||||
*/
|
*/
|
||||||
std::string NimBLERemoteCharacteristic::toString() {
|
std::string NimBLERemoteCharacteristic::toString() const {
|
||||||
std::string res = "Characteristic: uuid: " + m_uuid.toString();
|
std::string res = "Characteristic: uuid: " + m_uuid.toString();
|
||||||
char val[6];
|
char val[6];
|
||||||
res += ", handle: ";
|
res += ", handle: ";
|
||||||
|
@ -662,145 +387,18 @@ std::string NimBLERemoteCharacteristic::toString() {
|
||||||
res += val;
|
res += val;
|
||||||
res += ", props: ";
|
res += ", props: ";
|
||||||
res += " 0x";
|
res += " 0x";
|
||||||
snprintf(val, sizeof(val), "%02x", m_charProp);
|
snprintf(val, sizeof(val), "%02x", m_properties);
|
||||||
res += val;
|
res += val;
|
||||||
|
|
||||||
for(auto &it: m_descriptorVector) {
|
for (const auto& it : m_vDescriptors) {
|
||||||
res += "\n" + it->toString();
|
res += "\n" + it->toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
|
NimBLEClient* NimBLERemoteCharacteristic::getClient() const {
|
||||||
/**
|
return getRemoteService()->getClient();
|
||||||
* @brief Write a new value to the remote characteristic from a std::vector<uint8_t>.
|
} // getClient
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||||
|
|
|
@ -12,169 +12,67 @@
|
||||||
* Author: kolban
|
* Author: kolban
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
|
#ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
|
||||||
#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
|
#define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
#include "NimBLERemoteService.h"
|
# include "NimBLERemoteValueAttribute.h"
|
||||||
#include "NimBLERemoteDescriptor.h"
|
# include <vector>
|
||||||
|
# include <functional>
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
#include "NimBLELog.h"
|
|
||||||
|
|
||||||
class NimBLERemoteService;
|
class NimBLERemoteService;
|
||||||
class NimBLERemoteDescriptor;
|
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:
|
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
|
typedef std::function<void(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)> notify_callback;
|
||||||
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);
|
|
||||||
|
|
||||||
|
bool subscribe(bool notifications = true, const notify_callback notifyCallback = nullptr, bool response = true) const;
|
||||||
|
bool unsubscribe(bool response = true) const;
|
||||||
|
|
||||||
/*********************** Template Functions ************************/
|
std::vector<NimBLERemoteDescriptor*>::iterator begin() const;
|
||||||
|
std::vector<NimBLERemoteDescriptor*>::iterator end() const;
|
||||||
/**
|
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID& uuid) const;
|
||||||
* @brief Template to set the remote characteristic value to <type\>val.
|
const std::vector<NimBLERemoteDescriptor*>& getDescriptors(bool refresh = false) const;
|
||||||
* @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));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr);
|
|
||||||
|
|
||||||
|
private:
|
||||||
friend class NimBLEClient;
|
friend class NimBLEClient;
|
||||||
friend class NimBLERemoteService;
|
friend class NimBLERemoteService;
|
||||||
friend class NimBLERemoteDescriptor;
|
|
||||||
|
|
||||||
// Private member functions
|
NimBLERemoteCharacteristic(const NimBLERemoteService* pRemoteService, const ble_gatt_chr* chr);
|
||||||
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true);
|
~NimBLERemoteCharacteristic();
|
||||||
bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr);
|
|
||||||
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
|
|
||||||
struct ble_gatt_attr *attr, void *arg);
|
|
||||||
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
|
|
||||||
struct ble_gatt_attr *attr, void *arg);
|
|
||||||
static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error,
|
|
||||||
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
|
|
||||||
void *arg);
|
|
||||||
static int nextCharCB(uint16_t conn_handle, const struct ble_gatt_error *error,
|
|
||||||
const struct ble_gatt_chr *chr, void *arg);
|
|
||||||
|
|
||||||
// Private properties
|
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const;
|
||||||
NimBLEUUID m_uuid;
|
bool retrieveDescriptors(const NimBLEUUID* uuidFilter = nullptr) const;
|
||||||
uint8_t m_charProp;
|
|
||||||
uint16_t m_handle;
|
static int descriptorDiscCB(
|
||||||
uint16_t m_defHandle;
|
uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg);
|
||||||
uint16_t m_endHandle;
|
|
||||||
NimBLERemoteService* m_pRemoteService;
|
const NimBLERemoteService* m_pRemoteService{nullptr};
|
||||||
NimBLEAttValue m_value;
|
uint8_t m_properties{0};
|
||||||
notify_callback m_notifyCallback;
|
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
|
}; // NimBLERemoteCharacteristic
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||||
#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */
|
#endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */
|
||||||
|
|
|
@ -15,181 +15,31 @@
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
#include "NimBLERemoteDescriptor.h"
|
# include "NimBLERemoteDescriptor.h"
|
||||||
#include "NimBLEUtils.h"
|
# include "NimBLERemoteCharacteristic.h"
|
||||||
#include "NimBLELog.h"
|
|
||||||
|
|
||||||
#include <climits>
|
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLERemoteDescriptor";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remote descriptor constructor.
|
* @brief Remote descriptor constructor.
|
||||||
* @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to.
|
* @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.
|
* @param [in] dsc A pointer to the struct that contains the descriptor information.
|
||||||
*/
|
*/
|
||||||
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
|
NimBLERemoteDescriptor::NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic,
|
||||||
const struct ble_gatt_dsc *dsc)
|
const ble_gatt_dsc* dsc)
|
||||||
{
|
: NimBLERemoteValueAttribute{dsc->uuid, dsc->handle}, m_pRemoteCharacteristic{pRemoteCharacteristic} {} // NimBLERemoteDescriptor
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the characteristic that owns this descriptor.
|
* @brief Get the characteristic that owns this descriptor.
|
||||||
* @return The characteristic that owns this descriptor.
|
* @return The characteristic that owns this descriptor.
|
||||||
*/
|
*/
|
||||||
NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() {
|
NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() const {
|
||||||
return m_pRemoteCharacteristic;
|
return const_cast<NimBLERemoteCharacteristic*>(m_pRemoteCharacteristic);
|
||||||
} // getRemoteCharacteristic
|
} // 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.
|
* @brief Return a string representation of this Remote Descriptor.
|
||||||
* @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();
|
std::string res = "Descriptor: uuid: " + getUUID().toString();
|
||||||
char val[6];
|
char val[6];
|
||||||
res += ", handle: ";
|
res += ", handle: ";
|
||||||
|
@ -199,137 +49,8 @@ std::string NimBLERemoteDescriptor::toString() {
|
||||||
return res;
|
return res;
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
|
NimBLEClient* NimBLERemoteDescriptor::getClient() const {
|
||||||
/**
|
return m_pRemoteCharacteristic->getClient();
|
||||||
* @brief Callback for descriptor write operation.
|
|
||||||
* @return success == 0 or error code.
|
|
||||||
*/
|
|
||||||
int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
|
|
||||||
const struct ble_gatt_error *error,
|
|
||||||
struct ble_gatt_attr *attr, void *arg)
|
|
||||||
{
|
|
||||||
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
|
|
||||||
NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)pTaskData->pATT;
|
|
||||||
|
|
||||||
if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
|
|
||||||
|
|
||||||
pTaskData->rc = error->status;
|
|
||||||
xTaskNotifyGive(pTaskData->task);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 */
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||||
|
|
|
@ -12,93 +12,34 @@
|
||||||
* Author: kolban
|
* Author: kolban
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
|
#ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
|
||||||
#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
|
#define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
#include "NimBLERemoteCharacteristic.h"
|
# include "NimBLERemoteValueAttribute.h"
|
||||||
|
|
||||||
class NimBLERemoteCharacteristic;
|
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:
|
public:
|
||||||
uint16_t getHandle();
|
NimBLERemoteCharacteristic* getRemoteCharacteristic() const;
|
||||||
NimBLERemoteCharacteristic* getRemoteCharacteristic();
|
std::string toString(void) const;
|
||||||
NimBLEUUID getUUID();
|
NimBLEClient* getClient() const override;
|
||||||
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);
|
|
||||||
|
|
||||||
|
private:
|
||||||
/*********************** 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class NimBLERemoteCharacteristic;
|
friend class NimBLERemoteCharacteristic;
|
||||||
|
|
||||||
NimBLERemoteDescriptor (NimBLERemoteCharacteristic* pRemoteCharacteristic,
|
NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic, const ble_gatt_dsc* dsc);
|
||||||
const struct ble_gatt_dsc *dsc);
|
~NimBLERemoteDescriptor() = default;
|
||||||
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
|
|
||||||
struct ble_gatt_attr *attr, void *arg);
|
|
||||||
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
|
|
||||||
struct ble_gatt_attr *attr, void *arg);
|
|
||||||
|
|
||||||
uint16_t m_handle;
|
const NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
|
||||||
NimBLEUUID m_uuid;
|
|
||||||
NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||||
#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */
|
#endif /* NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ */
|
||||||
|
|
|
@ -15,12 +15,14 @@
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
#include "NimBLERemoteService.h"
|
# include "NimBLERemoteService.h"
|
||||||
#include "NimBLEUtils.h"
|
# include "NimBLERemoteCharacteristic.h"
|
||||||
#include "NimBLEDevice.h"
|
# include "NimBLEClient.h"
|
||||||
#include "NimBLELog.h"
|
# include "NimBLEAttValue.h"
|
||||||
|
# include "NimBLEUtils.h"
|
||||||
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
#include <climits>
|
# include <climits>
|
||||||
|
|
||||||
static const char* LOG_TAG = "NimBLERemoteService";
|
static const char* LOG_TAG = "NimBLERemoteService";
|
||||||
|
|
||||||
|
@ -29,28 +31,8 @@ static const char* LOG_TAG = "NimBLERemoteService";
|
||||||
* @param [in] pClient A pointer to the client this belongs to.
|
* @param [in] pClient A pointer to the client this belongs to.
|
||||||
* @param [in] service A pointer to the structure with the service information.
|
* @param [in] service A pointer to the structure with the service information.
|
||||||
*/
|
*/
|
||||||
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) {
|
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const ble_gatt_svc* service)
|
||||||
|
: NimBLEAttribute{service->uuid, service->start_handle}, m_pClient{pClient}, m_endHandle{service->end_handle} {}
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief When deleting the service make sure we delete all characteristics and descriptors.
|
* @brief When deleting the service make sure we delete all characteristics and descriptors.
|
||||||
|
@ -59,66 +41,62 @@ NimBLERemoteService::~NimBLERemoteService() {
|
||||||
deleteCharacteristics();
|
deleteCharacteristics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get iterator to the beginning of the vector of remote characteristic pointers.
|
* @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.
|
* @return An iterator to the beginning of the vector of remote characteristic pointers.
|
||||||
*/
|
*/
|
||||||
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() {
|
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() const {
|
||||||
return m_characteristicVector.begin();
|
return m_vChars.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get iterator to the end of the vector of remote characteristic pointers.
|
* @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.
|
* @return An iterator to the end of the vector of remote characteristic pointers.
|
||||||
*/
|
*/
|
||||||
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() {
|
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() const {
|
||||||
return m_characteristicVector.end();
|
return m_vChars.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the remote characteristic object for the characteristic UUID.
|
* @brief Get the remote characteristic object for the characteristic UUID.
|
||||||
* @param [in] uuid Remote characteristic uuid.
|
* @param [in] uuid Remote characteristic uuid.
|
||||||
* @return A pointer to the remote characteristic object.
|
* @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));
|
return getCharacteristic(NimBLEUUID(uuid));
|
||||||
} // getCharacteristic
|
} // getCharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the characteristic object for the UUID.
|
* @brief Get the characteristic object for the UUID.
|
||||||
* @param [in] uuid Characteristic uuid.
|
* @param [in] uuid Characteristic uuid.
|
||||||
* @return A pointer to the characteristic object, or nullptr if not found.
|
* @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());
|
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) {
|
if (it->getUUID() == uuid) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
|
pChar = it;
|
||||||
return it;
|
goto Done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t prev_size = m_characteristicVector.size();
|
if (retrieveCharacteristics(&uuid)) {
|
||||||
if(retrieveCharacteristics(&uuid)) {
|
if (m_vChars.size() > prev_size) {
|
||||||
if(m_characteristicVector.size() > prev_size) {
|
pChar = m_vChars.back();
|
||||||
return m_characteristicVector.back();
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the request was successful but 16/32 bit uuid not found
|
// If the request was successful but 16/32 bit uuid not found
|
||||||
// try again with the 128 bit uuid.
|
// try again with the 128 bit uuid.
|
||||||
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
|
if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
|
||||||
uuid.bitSize() == BLE_UUID_TYPE_32)
|
|
||||||
{
|
|
||||||
NimBLEUUID uuid128(uuid);
|
NimBLEUUID uuid128(uuid);
|
||||||
uuid128.to128();
|
uuid128.to128();
|
||||||
if (retrieveCharacteristics(&uuid128)) {
|
if (retrieveCharacteristics(&uuid128)) {
|
||||||
if(m_characteristicVector.size() > prev_size) {
|
if (m_vChars.size() > prev_size) {
|
||||||
return m_characteristicVector.back();
|
pChar = m_vChars.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -128,283 +106,189 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
|
||||||
uuid16.to16();
|
uuid16.to16();
|
||||||
// if the uuid was 128 bit but not of the BLE base type this check will fail
|
// 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 (uuid16.bitSize() == BLE_UUID_TYPE_16) {
|
||||||
if(retrieveCharacteristics(&uuid16)) {
|
if (retrieveCharacteristics(&uuid16)) {
|
||||||
if(m_characteristicVector.size() > prev_size) {
|
if (m_vChars.size() > prev_size) {
|
||||||
return m_characteristicVector.back();
|
pChar = m_vChars.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
|
Done:
|
||||||
return nullptr;
|
NIMBLE_LOGD(LOG_TAG, "<< Characteristic %sfound", pChar ? "" : "not ");
|
||||||
|
return pChar;
|
||||||
} // getCharacteristic
|
} // getCharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get a pointer to the vector of found characteristics.
|
* @brief Get a pointer to the vector of found characteristics.
|
||||||
* @param [in] refresh If true the current characteristics vector will cleared and
|
* @param [in] refresh If true the current characteristics vector will cleared and
|
||||||
* all characteristics for this service retrieved from the peripheral.
|
* all characteristics for this service retrieved from the peripheral.
|
||||||
* If false the vector will be returned with the currently stored characteristics of this service.
|
* 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) {
|
if (refresh) {
|
||||||
deleteCharacteristics();
|
deleteCharacteristics();
|
||||||
|
retrieveCharacteristics();
|
||||||
|
}
|
||||||
|
|
||||||
if (!retrieveCharacteristics()) {
|
return m_vChars;
|
||||||
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &m_characteristicVector;
|
|
||||||
} // getCharacteristics
|
} // getCharacteristics
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback for Characteristic discovery.
|
* @brief Callback for Characteristic discovery.
|
||||||
* @return success == 0 or error code.
|
* @return success == 0 or error code.
|
||||||
*/
|
*/
|
||||||
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
|
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
|
||||||
const struct ble_gatt_error *error,
|
const ble_gatt_error* error,
|
||||||
const struct ble_gatt_chr *chr, void *arg)
|
const ble_gatt_chr* chr,
|
||||||
{
|
void* arg) {
|
||||||
NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d",
|
NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>");
|
||||||
error->status, (error->status == 0) ? chr->val_handle : -1);
|
auto pTaskData = (ble_task_data_t*)arg;
|
||||||
|
const auto pSvc = (NimBLERemoteService*)pTaskData->pATT;
|
||||||
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
|
|
||||||
NimBLERemoteService *service = (NimBLERemoteService*)pTaskData->pATT;
|
|
||||||
|
|
||||||
// Make sure the discovery is for this device
|
// Make sure the discovery is for this device
|
||||||
if(service->getClient()->getConnId() != conn_handle){
|
if (pSvc->getClient()->getConnId() != conn_handle) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(error->status == 0) {
|
if (error->status == 0) {
|
||||||
// Found a service - add it to the vector
|
pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr));
|
||||||
NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr);
|
|
||||||
service->m_characteristicVector.push_back(pRemoteCharacteristic);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(error->status == BLE_HS_EDONE) {
|
|
||||||
pTaskData->rc = 0;
|
|
||||||
} else {
|
} else {
|
||||||
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s",
|
pTaskData->rc = error->status == BLE_HS_EDONE ? 0 : error->status;
|
||||||
error->status,
|
NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery");
|
||||||
NimBLEUtils::returnCodeToString(error->status));
|
xTaskNotifyGive(pTaskData->task);
|
||||||
pTaskData->rc = error->status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xTaskNotifyGive(pTaskData->task);
|
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered");
|
|
||||||
return error->status;
|
return error->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve all the characteristics for this service.
|
* @brief Retrieve all the characteristics for this service.
|
||||||
* This function will not return until we have all the characteristics.
|
* This function will not return until we have all the characteristics.
|
||||||
* @return True if successful.
|
* @return True if successful.
|
||||||
*/
|
*/
|
||||||
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) {
|
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
|
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()");
|
||||||
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
|
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(),
|
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
|
||||||
m_startHandle,
|
getHandle(),
|
||||||
m_endHandle,
|
getEndHandle(),
|
||||||
NimBLERemoteService::characteristicDiscCB,
|
NimBLERemoteService::characteristicDiscCB,
|
||||||
&taskData);
|
&taskData);
|
||||||
} else {
|
} else {
|
||||||
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
|
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
|
||||||
m_startHandle,
|
getHandle(),
|
||||||
m_endHandle,
|
getEndHandle(),
|
||||||
uuid_filter->getBase(),
|
uuidFilter->getBase(),
|
||||||
NimBLERemoteService::characteristicDiscCB,
|
NimBLERemoteService::characteristicDiscCB,
|
||||||
&taskData);
|
&taskData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc != 0) {
|
if (rc == 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
# ifdef ulTaskNotifyValueClear
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ulTaskNotifyValueClear
|
|
||||||
// Clear the task notification value to ensure we block
|
// Clear the task notification value to ensure we block
|
||||||
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
|
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
|
||||||
#endif
|
# endif
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
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()");
|
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics");
|
return taskData.rc == 0;
|
||||||
return false;
|
|
||||||
|
|
||||||
} // retrieveCharacteristics
|
} // retrieveCharacteristics
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the client associated with this service.
|
* @brief Get the client associated with this service.
|
||||||
* @return A reference to 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;
|
return m_pClient;
|
||||||
} // getClient
|
} // 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.
|
* @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.
|
* @returns a string containing the value or an empty string if not found or error.
|
||||||
*/
|
*/
|
||||||
std::string NimBLERemoteService::getValue(const NimBLEUUID &characteristicUuid) {
|
NimBLEAttValue NimBLERemoteService::getValue(const NimBLEUUID& uuid) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str());
|
const auto pChar = getCharacteristic(uuid);
|
||||||
|
if (pChar) {
|
||||||
std::string ret = "";
|
return pChar->readValue();
|
||||||
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
|
|
||||||
|
|
||||||
if(pChar != nullptr) {
|
|
||||||
ret = pChar->readValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< readValue");
|
return NimBLEAttValue{};
|
||||||
return ret;
|
|
||||||
} // readValue
|
} // readValue
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the value of a characteristic.
|
* @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.
|
* @param [in] value The value to set.
|
||||||
* @returns true on success, false if not found or error
|
* @returns true on success, false if not found or error
|
||||||
*/
|
*/
|
||||||
bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const std::string &value) {
|
bool NimBLERemoteService::setValue(const NimBLEUUID& uuid, const NimBLEAttValue& value) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str());
|
const auto pChar = getCharacteristic(uuid);
|
||||||
|
if (pChar) {
|
||||||
bool ret = false;
|
return pChar->writeValue(value);
|
||||||
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
|
|
||||||
|
|
||||||
if(pChar != nullptr) {
|
|
||||||
ret = pChar->writeValue(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< setValue");
|
return false;
|
||||||
return ret;
|
|
||||||
} // setValue
|
} // setValue
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delete the characteristics in the characteristics vector.
|
* @brief Delete the characteristics in the characteristics vector.
|
||||||
* @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
|
* @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
|
* object references. Since we allocated these in this class, we are also responsible for deleting
|
||||||
* them. This method does just that.
|
* them. This method does just that.
|
||||||
*/
|
*/
|
||||||
void NimBLERemoteService::deleteCharacteristics() {
|
void NimBLERemoteService::deleteCharacteristics() const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics");
|
for (const auto& it : m_vChars) {
|
||||||
for(auto &it: m_characteristicVector) {
|
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
m_characteristicVector.clear();
|
std::vector<NimBLERemoteCharacteristic*>{}.swap(m_vChars);
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics");
|
|
||||||
} // deleteCharacteristics
|
} // deleteCharacteristics
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delete characteristic by UUID
|
* @brief Delete characteristic by UUID
|
||||||
* @param [in] uuid The UUID of the characteristic to be removed from the local database.
|
* @param [in] uuid The UUID of the characteristic to be removed from the local database.
|
||||||
* @return Number of characteristics left.
|
* @return Number of characteristics left.
|
||||||
*/
|
*/
|
||||||
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) {
|
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID& uuid) const {
|
||||||
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic");
|
for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) {
|
||||||
|
if ((*it)->getUUID() == uuid) {
|
||||||
for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) {
|
delete (*it);
|
||||||
if((*it)->getUUID() == uuid) {
|
m_vChars.erase(it);
|
||||||
delete *it;
|
|
||||||
m_characteristicVector.erase(it);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic");
|
return m_vChars.size();
|
||||||
|
|
||||||
return m_characteristicVector.size();
|
|
||||||
} // deleteCharacteristic
|
} // deleteCharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a string representation of this remote service.
|
* @brief Create a string representation of this remote service.
|
||||||
* @return A string representation of this remote service.
|
* @return A string representation of this remote service.
|
||||||
*/
|
*/
|
||||||
std::string NimBLERemoteService::toString() {
|
std::string NimBLERemoteService::toString() const {
|
||||||
std::string res = "Service: uuid: " + m_uuid.toString();
|
std::string res = "Service: uuid: " + m_uuid.toString() + ", start_handle: 0x";
|
||||||
char val[6];
|
char val[5];
|
||||||
res += ", start_handle: ";
|
snprintf(val, sizeof(val), "%04x", getHandle());
|
||||||
snprintf(val, sizeof(val), "%d", m_startHandle);
|
|
||||||
res += val;
|
res += val;
|
||||||
snprintf(val, sizeof(val), "%04x", m_startHandle);
|
res += ", end_handle: 0x";
|
||||||
res += " 0x";
|
snprintf(val, sizeof(val), "%04x", getEndHandle());
|
||||||
res += val;
|
|
||||||
res += ", end_handle: ";
|
|
||||||
snprintf(val, sizeof(val), "%d", m_endHandle);
|
|
||||||
res += val;
|
|
||||||
snprintf(val, sizeof(val), "%04x", m_endHandle);
|
|
||||||
res += " 0x";
|
|
||||||
res += val;
|
res += val;
|
||||||
|
|
||||||
for (auto &it: m_characteristicVector) {
|
for (const auto& chr : m_vChars) {
|
||||||
res += "\n" + it->toString();
|
res += "\n" + chr->toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -12,74 +12,54 @@
|
||||||
* Author: kolban
|
* Author: kolban
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_
|
#ifndef NIMBLE_CPP_REMOTE_SERVICE_H_
|
||||||
#define COMPONENTS_NIMBLEREMOTESERVICE_H_
|
#define NIMBLE_CPP_REMOTE_SERVICE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||||
|
|
||||||
#include "NimBLEClient.h"
|
# include "NimBLEAttribute.h"
|
||||||
#include "NimBLEUUID.h"
|
# include <vector>
|
||||||
#include "NimBLERemoteCharacteristic.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class NimBLEClient;
|
|
||||||
class NimBLERemoteCharacteristic;
|
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:
|
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
|
const std::vector<NimBLERemoteCharacteristic*>& getCharacteristics(bool refresh = false) const;
|
||||||
std::vector<NimBLERemoteCharacteristic*>::iterator begin();
|
std::vector<NimBLERemoteCharacteristic*>::iterator begin() const;
|
||||||
std::vector<NimBLERemoteCharacteristic*>::iterator end();
|
std::vector<NimBLERemoteCharacteristic*>::iterator end() const;
|
||||||
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);
|
|
||||||
|
|
||||||
private:
|
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 NimBLEClient;
|
||||||
friend class NimBLERemoteCharacteristic;
|
|
||||||
|
|
||||||
// Private methods
|
NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service);
|
||||||
bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr);
|
~NimBLERemoteService();
|
||||||
|
bool retrieveCharacteristics(const NimBLEUUID* uuidFilter = nullptr) const;
|
||||||
static int characteristicDiscCB(uint16_t conn_handle,
|
static int characteristicDiscCB(uint16_t conn_handle,
|
||||||
const struct ble_gatt_error *error,
|
const struct ble_gatt_error* error,
|
||||||
const struct ble_gatt_chr *chr,
|
const struct ble_gatt_chr* chr,
|
||||||
void *arg);
|
void* arg);
|
||||||
|
|
||||||
uint16_t getStartHandle();
|
mutable std::vector<NimBLERemoteCharacteristic*> m_vChars{};
|
||||||
uint16_t getEndHandle();
|
NimBLEClient* m_pClient{nullptr};
|
||||||
void releaseSemaphores();
|
uint16_t m_endHandle{0};
|
||||||
|
|
||||||
// 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;
|
|
||||||
}; // NimBLERemoteService
|
}; // NimBLERemoteService
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
#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.
|
* @brief Destructor: frees all resources / attributes created.
|
||||||
*/
|
*/
|
||||||
NimBLEServer::~NimBLEServer() {
|
NimBLEServer::~NimBLEServer() {
|
||||||
for(auto &it : m_svcVec) {
|
for(const auto &it : m_svcVec) {
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ void NimBLEServer::start() {
|
||||||
// Get the assigned service handles and build a vector of characteristics
|
// Get the assigned service handles and build a vector of characteristics
|
||||||
// with Notify / Indicate capabilities for event handling
|
// with Notify / Indicate capabilities for event handling
|
||||||
for(auto &svc : m_svcVec) {
|
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);
|
rc = ble_gatts_find_svc(svc->getUUID().getBase(), &svc->m_handle);
|
||||||
if(rc != 0) {
|
if(rc != 0) {
|
||||||
NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s",
|
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
|
// if Notify / Indicate is enabled but we didn't create the descriptor
|
||||||
// we do it now.
|
// we do it now.
|
||||||
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
|
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
|
||||||
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
|
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
|
||||||
m_notifyChrVec.push_back(chr);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -720,6 +727,68 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||||
} // handleGapEvent
|
} // 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.
|
* @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
|
// 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.
|
// is being called to delete the object and do so if requested.
|
||||||
// Otherwise, ignore the call and return.
|
// Otherwise, ignore the call and return.
|
||||||
if(service->m_removed > 0) {
|
if(service->getRemoved() > 0) {
|
||||||
if(deleteSvc) {
|
if(deleteSvc) {
|
||||||
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
|
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
|
||||||
if ((*it) == service) {
|
if ((*it) == service) {
|
||||||
|
@ -781,7 +850,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
|
||||||
return;
|
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();
|
serviceChanged();
|
||||||
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
||||||
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
|
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.
|
// If adding a service that was not removed add it and return.
|
||||||
// Else reset GATT and send service changed notification.
|
// Else reset GATT and send service changed notification.
|
||||||
if(service->m_removed == 0) {
|
if(service->getRemoved() == 0) {
|
||||||
m_svcVec.push_back(service);
|
m_svcVec.push_back(service);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
service->m_removed = 0;
|
service->setRemoved(0);
|
||||||
serviceChanged();
|
serviceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,8 +898,8 @@ void NimBLEServer::resetGATT() {
|
||||||
ble_svc_gatt_init();
|
ble_svc_gatt_init();
|
||||||
|
|
||||||
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
|
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
|
||||||
if ((*it)->m_removed > 0) {
|
if ((*it)->getRemoved() > 0) {
|
||||||
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
|
if ((*it)->getRemoved() == NIMBLE_ATT_REMOVE_DELETE) {
|
||||||
delete *it;
|
delete *it;
|
||||||
it = m_svcVec.erase(it);
|
it = m_svcVec.erase(it);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1004,20 +1073,20 @@ uint32_t NimBLEServerCallbacks::onPassKeyDisplay(){
|
||||||
return 123456;
|
return 123456;
|
||||||
} //onPassKeyDisplay
|
} //onPassKeyDisplay
|
||||||
|
|
||||||
void NimBLEServerCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
|
void NimBLEServerCallbacks::onConfirmPIN(NimBLEConnInfo& connInfo, uint32_t pin){
|
||||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
|
||||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||||
} // onConfirmPIN
|
} // onConfirmPIN
|
||||||
|
|
||||||
void NimBLEServerCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
|
void NimBLEServerCallbacks::onIdentity(NimBLEConnInfo& connInfo){
|
||||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onIdentity: default");
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onIdentity: default");
|
||||||
} // onIdentity
|
} // onIdentity
|
||||||
|
|
||||||
void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
|
||||||
} // onAuthenticationComplete
|
} // 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");
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
|
||||||
} // onAuthenticationComplete
|
} // onAuthenticationComplete
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,8 @@
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
#define NIMBLE_ATT_REMOVE_HIDE 1
|
class NimBLEServer;
|
||||||
#define NIMBLE_ATT_REMOVE_DELETE 2
|
class NimBLEServerCallbacks;
|
||||||
|
|
||||||
#define onMtuChanged onMTUChange
|
|
||||||
|
|
||||||
#include "NimBLEUtils.h"
|
#include "NimBLEUtils.h"
|
||||||
#include "NimBLEAddress.h"
|
#include "NimBLEAddress.h"
|
||||||
|
@ -31,13 +29,13 @@
|
||||||
#include "NimBLEAdvertising.h"
|
#include "NimBLEAdvertising.h"
|
||||||
#endif
|
#endif
|
||||||
#include "NimBLEService.h"
|
#include "NimBLEService.h"
|
||||||
|
#include "NimBLECharacteristic.h"
|
||||||
#include "NimBLEConnInfo.h"
|
#include "NimBLEConnInfo.h"
|
||||||
|
|
||||||
|
#define NIMBLE_ATT_REMOVE_HIDE 1
|
||||||
|
#define NIMBLE_ATT_REMOVE_DELETE 2
|
||||||
|
|
||||||
class NimBLEService;
|
#define onMtuChanged onMTUChange
|
||||||
class NimBLECharacteristic;
|
|
||||||
class NimBLEServerCallbacks;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The model of a %BLE server.
|
* @brief The model of a %BLE server.
|
||||||
|
@ -123,6 +121,9 @@ private:
|
||||||
bool setIndicateWait(uint16_t conn_handle);
|
bool setIndicateWait(uint16_t conn_handle);
|
||||||
void clearIndicateWait(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
|
}; // NimBLEServer
|
||||||
|
|
||||||
|
|
||||||
|
@ -182,14 +183,14 @@ public:
|
||||||
* Should be passed back to NimBLEDevice::injectConfirmPIN
|
* Should be passed back to NimBLEDevice::injectConfirmPIN
|
||||||
* @param [in] pin The pin to compare with the client.
|
* @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.
|
* @brief Called when the pairing procedure is complete.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||||
* about the peer connection parameters.
|
* about the peer connection parameters.
|
||||||
*/
|
*/
|
||||||
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
|
virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when the pairing procedure is complete.
|
* @brief Called when the pairing procedure is complete.
|
||||||
|
@ -197,13 +198,13 @@ public:
|
||||||
* @param [in] name The name of the connected peer device.
|
* @param [in] name The name of the connected peer device.
|
||||||
* about the peer connection parameters.
|
* 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.
|
* @brief Called when the peer identity address is resolved.
|
||||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||||
*/
|
*/
|
||||||
virtual void onIdentity(const NimBLEConnInfo& connInfo);
|
virtual void onIdentity(NimBLEConnInfo& connInfo);
|
||||||
}; // NimBLEServerCallbacks
|
}; // NimBLEServerCallbacks
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||||
|
|
|
@ -17,91 +17,67 @@
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
#include "NimBLEDevice.h"
|
# include "NimBLEDevice.h"
|
||||||
#include "NimBLEService.h"
|
# include "NimBLEService.h"
|
||||||
#include "NimBLEUtils.h"
|
# include "NimBLEUtils.h"
|
||||||
#include "NimBLELog.h"
|
# include "NimBLELog.h"
|
||||||
|
|
||||||
#include <string>
|
# 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
|
* @brief Construct an instance of the NimBLEService
|
||||||
* @param [in] uuid The UUID of the service.
|
* @param [in] uuid The UUID of the service.
|
||||||
*/
|
*/
|
||||||
NimBLEService::NimBLEService(const char* uuid)
|
NimBLEService::NimBLEService(const char* uuid) : NimBLEService(NimBLEUUID(uuid)) {}
|
||||||
: NimBLEService(NimBLEUUID(uuid)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct an instance of the BLEService
|
* @brief Construct an instance of the BLEService
|
||||||
* @param [in] uuid The UUID of the service.
|
* @param [in] uuid The UUID of the service.
|
||||||
*/
|
*/
|
||||||
NimBLEService::NimBLEService(const NimBLEUUID &uuid) {
|
NimBLEService::NimBLEService(const NimBLEUUID& uuid)
|
||||||
m_uuid = uuid;
|
: NimBLELocalAttribute{uuid, 0}, m_pSvcDef{{0, getUUID().getBase(), nullptr, nullptr},{}} {}
|
||||||
m_handle = NULL_HANDLE;
|
|
||||||
m_pSvcDef = nullptr;
|
|
||||||
m_removed = 0;
|
|
||||||
|
|
||||||
} // NimBLEService
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor, make sure we release the resources allocated for the service.
|
||||||
|
*/
|
||||||
NimBLEService::~NimBLEService() {
|
NimBLEService::~NimBLEService() {
|
||||||
if(m_pSvcDef != nullptr) {
|
if (m_pSvcDef->characteristics) {
|
||||||
if(m_pSvcDef->characteristics != nullptr) {
|
if (m_pSvcDef->characteristics->descriptors) {
|
||||||
for(int i=0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) {
|
delete[] m_pSvcDef->characteristics->descriptors;
|
||||||
if(m_pSvcDef->characteristics[i].descriptors) {
|
|
||||||
delete(m_pSvcDef->characteristics[i].descriptors);
|
|
||||||
}
|
}
|
||||||
}
|
delete[] m_pSvcDef->characteristics;
|
||||||
delete(m_pSvcDef->characteristics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(m_pSvcDef);
|
for (const auto& it : m_vChars) {
|
||||||
}
|
|
||||||
|
|
||||||
for(auto &it : m_chrVec) {
|
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
}
|
} // ~NimBLEService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Dump details of this BLE GATT service.
|
* @brief Dump details of this BLE GATT service.
|
||||||
*/
|
*/
|
||||||
void NimBLEService::dump() {
|
void NimBLEService::dump() const {
|
||||||
NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x",
|
NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x", getUUID().toString().c_str(), getHandle());
|
||||||
m_uuid.toString().c_str(),
|
|
||||||
m_handle);
|
|
||||||
|
|
||||||
std::string res;
|
std::string res;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
char hex[5];
|
char hex[5];
|
||||||
for (auto &it: m_chrVec) {
|
for (const auto& it : m_vChars) {
|
||||||
if (count > 0) {res += "\n";}
|
if (count > 0) {
|
||||||
|
res += "\n";
|
||||||
|
}
|
||||||
snprintf(hex, sizeof(hex), "%04x", it->getHandle());
|
snprintf(hex, sizeof(hex), "%04x", it->getHandle());
|
||||||
count++;
|
count++;
|
||||||
res += "handle: 0x";
|
res += "handle: 0x";
|
||||||
res += hex;
|
res += hex;
|
||||||
res += ", uuid: " + std::string(it->getUUID());
|
res += ", uuid: " + std::string(it->getUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str());
|
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str());
|
||||||
} // dump
|
} // 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
|
* @brief Builds the database of characteristics/descriptors for the service
|
||||||
* and registers it with the NimBLE stack.
|
* 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());
|
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
|
||||||
|
|
||||||
// Rebuild the service definition if the server attributes have changed.
|
// Rebuild the service definition if the server attributes have changed.
|
||||||
if(getServer()->m_svcChanged && m_pSvcDef != nullptr) {
|
if (getServer()->m_svcChanged) {
|
||||||
if(m_pSvcDef[0].characteristics) {
|
if (m_pSvcDef->characteristics) {
|
||||||
if(m_pSvcDef[0].characteristics[0].descriptors) {
|
if (m_pSvcDef->characteristics->descriptors) {
|
||||||
delete(m_pSvcDef[0].characteristics[0].descriptors);
|
delete[] m_pSvcDef->characteristics->descriptors;
|
||||||
}
|
}
|
||||||
delete(m_pSvcDef[0].characteristics);
|
delete[] m_pSvcDef->characteristics;
|
||||||
}
|
}
|
||||||
delete(m_pSvcDef);
|
m_pSvcDef->type = 0;
|
||||||
m_pSvcDef = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_pSvcDef == nullptr) {
|
if (!m_pSvcDef->type) {
|
||||||
// Nimble requires an array of services to be sent to the api
|
m_pSvcDef->type = BLE_GATT_SVC_TYPE_PRIMARY;
|
||||||
// Since we are adding 1 at a time we create an array of 2 and set the type
|
size_t numChrs = 0;
|
||||||
// of the second service to 0 to indicate the end of the array.
|
for (const auto& chr : m_vChars) {
|
||||||
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
|
if (chr->getRemoved()) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
++numChrs;
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t numChrs = m_chrVec.size() - removedCount;
|
NIMBLE_LOGD(LOG_TAG, "Adding %d characteristics for service %s", numChrs, toString().c_str());
|
||||||
NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
|
if (numChrs) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
if(!numChrs){
|
|
||||||
svc[0].characteristics = NULL;
|
|
||||||
}else{
|
|
||||||
// Nimble requires the last characteristic to have it's uuid = 0 to indicate the end
|
// 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
|
// of the characteristics for the service. We create 1 extra and set it to null
|
||||||
// for this purpose.
|
// for this purpose.
|
||||||
pChr_a = new ble_gatt_chr_def[numChrs + 1]{};
|
ble_gatt_chr_def* pChrs = new ble_gatt_chr_def[numChrs + 1]{};
|
||||||
int i = 0;
|
for (const auto& chr : m_vChars) {
|
||||||
for(auto chr_it = m_chrVec.begin(); chr_it != m_chrVec.end(); ++chr_it) {
|
if (chr->getRemoved()) {
|
||||||
if((*chr_it)->m_removed > 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
removedCount = 0;
|
size_t numDscs = 0;
|
||||||
for(auto it = (*chr_it)->m_dscVec.begin(); it != (*chr_it)->m_dscVec.end(); ) {
|
for (const auto& dsc : chr->m_vDescriptors) {
|
||||||
if ((*it)->m_removed > 0) {
|
if (dsc->getRemoved()) {
|
||||||
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
|
|
||||||
delete *it;
|
|
||||||
it = (*chr_it)->m_dscVec.erase(it);
|
|
||||||
} else {
|
|
||||||
++removedCount;
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
++numDscs;
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Must have last descriptor uuid = 0 so we have to create 1 extra
|
||||||
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
|
ble_gatt_dsc_def* pDscs = new ble_gatt_dsc_def[numDscs + 1]{};
|
||||||
int d = 0;
|
for (const auto& dsc : chr->m_vDescriptors) {
|
||||||
for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) {
|
if (dsc->getRemoved()) {
|
||||||
if((*dsc_it)->m_removed > 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pDsc_a[d].uuid = (*dsc_it)->m_uuid.getBase();
|
|
||||||
pDsc_a[d].att_flags = (*dsc_it)->m_properties;
|
printf("adding disc %s\n", dsc->toString().c_str());
|
||||||
pDsc_a[d].min_key_size = 0;
|
|
||||||
pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
|
pDscs[j].uuid = dsc->getUUID().getBase();
|
||||||
pDsc_a[d].arg = (*dsc_it);
|
pDscs[j].att_flags = dsc->getProperties();
|
||||||
++d;
|
pDscs[j].min_key_size = 0;
|
||||||
|
pDscs[j].access_cb = NimBLEServer::handleGattEvent;
|
||||||
|
pDscs[j].arg = dsc;
|
||||||
|
++j;
|
||||||
}
|
}
|
||||||
|
|
||||||
pDsc_a[numDscs].uuid = NULL;
|
pChrs[i].descriptors = pDscs;
|
||||||
pChr_a[i].descriptors = pDsc_a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pChr_a[i].uuid = (*chr_it)->m_uuid.getBase();
|
pChrs[i].uuid = chr->getUUID().getBase();
|
||||||
pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent;
|
pChrs[i].access_cb = NimBLEServer::handleGattEvent;
|
||||||
pChr_a[i].arg = (*chr_it);
|
pChrs[i].arg = chr;
|
||||||
pChr_a[i].flags = (*chr_it)->m_properties;
|
pChrs[i].flags = chr->getProperties();
|
||||||
pChr_a[i].min_key_size = 0;
|
pChrs[i].min_key_size = 0;
|
||||||
pChr_a[i].val_handle = &(*chr_it)->m_handle;
|
pChrs[i].val_handle = &chr->m_handle;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
pChr_a[numChrs].uuid = NULL;
|
m_pSvcDef->characteristics = pChrs;
|
||||||
svc[0].characteristics = pChr_a;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of services must indicate to api with type = 0
|
int rc = ble_gatts_count_cfg(m_pSvcDef);
|
||||||
svc[1].type = 0;
|
|
||||||
m_pSvcDef = svc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef);
|
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)m_pSvcDef);
|
rc = ble_gatts_add_svcs(m_pSvcDef);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< start()");
|
NIMBLE_LOGD(LOG_TAG, "<< start()");
|
||||||
return true;
|
return true;
|
||||||
} // start
|
} // 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.
|
* @brief Create a new BLE Characteristic associated with this service.
|
||||||
* @param [in] uuid - The UUID of the characteristic.
|
* @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) {
|
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) {
|
||||||
return createCharacteristic(NimBLEUUID(uuid), properties, max_len);
|
return createCharacteristic(NimBLEUUID(uuid), properties, max_len);
|
||||||
}
|
} // createCharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new BLE Characteristic associated with this service.
|
* @brief Create a new BLE Characteristic associated with this service.
|
||||||
|
@ -273,59 +198,62 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
|
||||||
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
|
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
|
||||||
* @return The new BLE characteristic.
|
* @return The new BLE characteristic.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
|
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) {
|
if (getCharacteristic(uuid) != nullptr) {
|
||||||
NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
|
NIMBLE_LOGD(LOG_TAG, "Adding a duplicate characteristic with UUID: %s", std::string(uuid).c_str());
|
||||||
std::string(uuid).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addCharacteristic(pCharacteristic);
|
addCharacteristic(pChar);
|
||||||
return pCharacteristic;
|
return pChar;
|
||||||
} // createCharacteristic
|
} // createCharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add a characteristic to the service.
|
* @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;
|
bool foundRemoved = false;
|
||||||
|
if (pChar->getRemoved() > 0) {
|
||||||
if(pCharacteristic->m_removed > 0) {
|
for (const auto& chr : m_vChars) {
|
||||||
for(auto& it : m_chrVec) {
|
if (chr == pChar) {
|
||||||
if(it == pCharacteristic) {
|
|
||||||
foundRemoved = true;
|
foundRemoved = true;
|
||||||
pCharacteristic->m_removed = 0;
|
pChar->setRemoved(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!foundRemoved) {
|
// Check if the characteristic is already in the service and if so, return.
|
||||||
m_chrVec.push_back(pCharacteristic);
|
for (const auto& chr : m_vChars) {
|
||||||
|
if (chr == pChar) {
|
||||||
|
pChar->setService(this); // Update the service pointer in the characteristic.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pCharacteristic->setService(this);
|
if (!foundRemoved) {
|
||||||
|
m_vChars.push_back(pChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
pChar->setService(this);
|
||||||
getServer()->serviceChanged();
|
getServer()->serviceChanged();
|
||||||
} // addCharacteristic
|
} // addCharacteristic
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove a characteristic from the service.
|
* @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.
|
* @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
|
// 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.
|
// is being called to delete the object and do so if requested.
|
||||||
// Otherwise, ignore the call and return.
|
// Otherwise, ignore the call and return.
|
||||||
if(pCharacteristic->m_removed > 0) {
|
if (pChar->getRemoved() > 0) {
|
||||||
if(deleteChr) {
|
if (deleteChr) {
|
||||||
for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) {
|
for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) {
|
||||||
if ((*it) == pCharacteristic) {
|
if ((*it) == pChar) {
|
||||||
m_chrVec.erase(it);
|
delete (*it);
|
||||||
delete *it;
|
m_vChars.erase(it);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,80 +262,82 @@ void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic,
|
||||||
return;
|
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();
|
getServer()->serviceChanged();
|
||||||
} // removeCharacteristic
|
} // 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.
|
* @brief Get a pointer to the characteristic object with the specified UUID.
|
||||||
* @param [in] uuid The UUID of the characteristic.
|
* @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.
|
* @return A pointer to the characteristic object or nullptr if not found.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) {
|
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID& uuid, uint16_t idx) const {
|
||||||
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) {
|
|
||||||
uint16_t position = 0;
|
uint16_t position = 0;
|
||||||
for (auto &it : m_chrVec) {
|
for (const auto& chr : m_vChars) {
|
||||||
if (it->getUUID() == uuid) {
|
if (chr->getUUID() == uuid) {
|
||||||
if (position == instanceId) {
|
if (position == idx) {
|
||||||
return it;
|
return chr;
|
||||||
}
|
}
|
||||||
position++;
|
position++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
} // getCharacteristic
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get a pointer to the characteristic object with the specified handle.
|
* @brief Get a pointer to the characteristic object with the specified handle.
|
||||||
* @param handle The handle of the characteristic.
|
* @param handle The handle of the characteristic.
|
||||||
* @return A pointer to the characteristic object or nullptr if not found.
|
* @return A pointer to the characteristic object or nullptr if not found.
|
||||||
*/
|
*/
|
||||||
NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) {
|
NimBLECharacteristic* NimBLEService::getCharacteristicByHandle(uint16_t handle) const {
|
||||||
for (auto &it : m_chrVec) {
|
for (const auto& chr : m_vChars) {
|
||||||
if (it->getHandle() == handle) {
|
if (chr->getHandle() == handle) {
|
||||||
return it;
|
return chr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
} // getCharacteristicByHandle
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A vector containing pointers to each characteristic associated with this service.
|
* @return A vector containing pointers to each characteristic associated with this service.
|
||||||
*/
|
*/
|
||||||
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics() {
|
const std::vector<NimBLECharacteristic*>& NimBLEService::getCharacteristics() const {
|
||||||
return m_chrVec;
|
return m_vChars;
|
||||||
}
|
} // getCharacteristics
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
|
* @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));
|
return getCharacteristics(NimBLEUUID(uuid));
|
||||||
}
|
} // getCharacteristics
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
|
* @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;
|
std::vector<NimBLECharacteristic*> result;
|
||||||
for (auto &it : m_chrVec) {
|
for (const auto& chr : m_vChars) {
|
||||||
if (it->getUUID() == uuid) {
|
if (chr->getUUID() == uuid) {
|
||||||
result.push_back(it);
|
result.push_back(chr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
} // getCharacteristics
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a string representation of this service.
|
* @brief Return a string representation of this service.
|
||||||
|
@ -416,7 +346,7 @@ std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const NimB
|
||||||
* * Its handle
|
* * Its handle
|
||||||
* @return A string representation of this service.
|
* @return A string representation of this service.
|
||||||
*/
|
*/
|
||||||
std::string NimBLEService::toString() {
|
std::string NimBLEService::toString() const {
|
||||||
std::string res = "UUID: " + getUUID().toString();
|
std::string res = "UUID: " + getUUID().toString();
|
||||||
char hex[5];
|
char hex[5];
|
||||||
snprintf(hex, sizeof(hex), "%04x", getHandle());
|
snprintf(hex, sizeof(hex), "%04x", getHandle());
|
||||||
|
@ -425,22 +355,20 @@ std::string NimBLEService::toString() {
|
||||||
return res;
|
return res;
|
||||||
} // toString
|
} // toString
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the BLE server associated with this service.
|
* @brief Get the BLE server associated with this service.
|
||||||
* @return The BLEServer associated with this service.
|
* @return The BLEServer associated with this service.
|
||||||
*/
|
*/
|
||||||
NimBLEServer* NimBLEService::getServer() {
|
NimBLEServer* NimBLEService::getServer() const {
|
||||||
return NimBLEDevice::getServer();
|
return NimBLEDevice::getServer();
|
||||||
}// getServer
|
} // getServer
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks if the service has been started.
|
* @brief Checks if the service has been started.
|
||||||
* @return True if the service has been started.
|
* @return True if the service has been started.
|
||||||
*/
|
*/
|
||||||
bool NimBLEService::isStarted() {
|
bool NimBLEService::isStarted() const {
|
||||||
return m_pSvcDef != nullptr;
|
return m_pSvcDef->type > 0;
|
||||||
}
|
} // isStarted
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||||
|
|
|
@ -12,76 +12,59 @@
|
||||||
* Author: kolban
|
* Author: kolban
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MAIN_NIMBLESERVICE_H_
|
#ifndef NIMBLE_CPP_SERVICE_H_
|
||||||
#define MAIN_NIMBLESERVICE_H_
|
#define NIMBLE_CPP_SERVICE_H_
|
||||||
|
|
||||||
#include "nimconfig.h"
|
#include "nimconfig.h"
|
||||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||||
|
|
||||||
#include "NimBLEServer.h"
|
class NimBLEService;
|
||||||
#include "NimBLECharacteristic.h"
|
|
||||||
#include "NimBLEUUID.h"
|
|
||||||
|
|
||||||
|
|
||||||
class NimBLEServer;
|
|
||||||
class NimBLECharacteristic;
|
|
||||||
|
|
||||||
|
# include "NimBLEAttribute.h"
|
||||||
|
# include "NimBLEServer.h"
|
||||||
|
# include "NimBLECharacteristic.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The model of a %BLE service.
|
* @brief The model of a BLE service.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class NimBLEService {
|
class NimBLEService : public NimBLELocalAttribute {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
NimBLEService(const char* uuid);
|
NimBLEService(const char* uuid);
|
||||||
NimBLEService(const NimBLEUUID &uuid);
|
NimBLEService(const NimBLEUUID& uuid);
|
||||||
~NimBLEService();
|
~NimBLEService();
|
||||||
|
|
||||||
NimBLEServer* getServer();
|
NimBLEServer* getServer() const;
|
||||||
|
std::string toString() const;
|
||||||
NimBLEUUID getUUID();
|
void dump() const;
|
||||||
uint16_t getHandle();
|
bool isStarted() const;
|
||||||
std::string toString();
|
|
||||||
void dump();
|
|
||||||
bool isStarted();
|
|
||||||
bool start();
|
bool start();
|
||||||
|
|
||||||
NimBLECharacteristic* createCharacteristic(const char* uuid,
|
NimBLECharacteristic* createCharacteristic(const char* uuid,
|
||||||
uint32_t properties =
|
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
|
||||||
NIMBLE_PROPERTY::READ |
|
|
||||||
NIMBLE_PROPERTY::WRITE,
|
|
||||||
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
|
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
|
||||||
|
|
||||||
NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
|
NimBLECharacteristic* createCharacteristic(const NimBLEUUID& uuid,
|
||||||
uint32_t properties =
|
uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
|
||||||
NIMBLE_PROPERTY::READ |
|
|
||||||
NIMBLE_PROPERTY::WRITE,
|
|
||||||
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
|
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
|
||||||
|
|
||||||
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
|
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
|
||||||
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);
|
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);
|
||||||
NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0);
|
NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0) const;
|
||||||
NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0);
|
NimBLECharacteristic* getCharacteristic(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
|
||||||
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);
|
NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle) const;
|
||||||
|
|
||||||
std::vector<NimBLECharacteristic*> getCharacteristics();
|
const std::vector<NimBLECharacteristic*>& getCharacteristics() const;
|
||||||
std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid);
|
std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid) const;
|
||||||
std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID &uuid);
|
std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID& uuid) const;
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
|
private:
|
||||||
friend class NimBLEServer;
|
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
|
}; // NimBLEService
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
#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[] = {
|
static const uint8_t ble_base_uuid[] = {
|
||||||
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
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.
|
* @brief Create a UUID from a string.
|
||||||
*
|
*
|
||||||
|
@ -323,4 +329,4 @@ NimBLEUUID::operator std::string() const {
|
||||||
return ble_uuid_to_str(&m_uuid.u, buf);
|
return ble_uuid_to_str(&m_uuid.u, buf);
|
||||||
} // operator std::string
|
} // operator std::string
|
||||||
|
|
||||||
#endif /* CONFIG_BT_ENABLED */
|
# endif /* CONFIG_BT_ENABLED */
|
||||||
|
|
|
@ -41,6 +41,7 @@ class NimBLEUUID {
|
||||||
* @brief Created a blank UUID.
|
* @brief Created a blank UUID.
|
||||||
*/
|
*/
|
||||||
NimBLEUUID() = default;
|
NimBLEUUID() = default;
|
||||||
|
NimBLEUUID(const ble_uuid_any_t& uuid);
|
||||||
NimBLEUUID(const std::string& uuid);
|
NimBLEUUID(const std::string& uuid);
|
||||||
NimBLEUUID(uint16_t uuid);
|
NimBLEUUID(uint16_t uuid);
|
||||||
NimBLEUUID(uint32_t uuid);
|
NimBLEUUID(uint32_t uuid);
|
||||||
|
|
Loading…
Reference in a new issue