Remove automatic discovery in NimBLEClient::connect().

Instead of discovering the peripheral database on connection and consuming
the associated resources this will give the user more control over the
discovery operation.

* Adds void NimBLEClient::discoverAtrributes() for the user to discover all
the peripheral attributes as a replacement for the former functionality.

* getServices(), getCharacteristics(), getDescriptors() now take an
optional bool parameter (default false).
If true it will clear the respective vector and retrieve all the respective
attributes from the peripheral. If false it will retrieve the attributes
only if the vector is empty, otherwise the vector is returned with the
currently stored attributes.

* getService(NimBLEUUID), getCharacteristic(NimBLEUUID), getDescriptor(NimBLEUUID)
will now check the respective vectors for the attribute object and, if not
found, will retrieve (only) the specified attribute from the peripheral.
This commit is contained in:
h2zero 2020-05-23 10:27:32 -06:00
parent 10e50a8791
commit c5c9423893
9 changed files with 368 additions and 254 deletions

View file

@ -122,6 +122,35 @@ Has been removed from the API as it is no longer maintained in the library.
The last two above changes reduce the heap usage significantly with minimal application code adjustments.
**NEW** on May 23, 2020
> ```
> NimBLEClient::getServices(bool refresh = false)
> NimBLERemoteService::getCharacteristics(bool refresh = false)
> NimBLERemoteCharacteristic::getDecriptors(bool refresh = false)
>```
> These methods now take an optional (bool) parameter.
If true it will clear the respective vector and retrieve all the respective attributes from the peripheral.
If false it will retrieve the attributes only if the vector is empty, otherwise the vector is returned
with the currently stored attributes.
> Removed the automatic discovery of all peripheral attributes as they consumed time and resources for data
the user may not be interested in.
> Added `NimBLEClient::discoverAtrributes()` for the user to discover all the peripheral attributes
to replace the the former functionality.
> ```
>getService(NimBLEUUID)
>getCharacteristic(NimBLEUUID)
>getDescriptor(NimBLEUUID)
>```
>These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only)
the specified attribute from the peripheral.
> These changes allow more control for the user to manage the resources used for the attributes.
#### Client Security:
The client will automatically initiate security when the peripheral responds that it's required.
The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.

View file

@ -53,7 +53,6 @@ NimBLEClient::NimBLEClient()
{
m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_haveServices = false;
m_isConnected = false;
m_connectTimeout = 30000;
@ -95,7 +94,6 @@ void NimBLEClient::clearServices() {
}
m_servicesVector.clear();
m_haveServices = false;
NIMBLE_LOGD(LOG_TAG, "<< clearServices");
} // clearServices
@ -183,18 +181,6 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
clearServices();
}
if (!m_haveServices) {
if (!retrieveServices()) {
// error getting services, make sure we disconnect and release any resources before returning
disconnect();
clearServices();
return false;
}
else{
NIMBLE_LOGD(LOG_TAG, "Found %d services", getServices()->size());
}
}
m_pClientCallbacks->onConnect(this);
NIMBLE_LOGD(LOG_TAG, "<< connect()");
@ -384,10 +370,6 @@ NimBLERemoteService* NimBLEClient::getService(const char* uuid) {
NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str());
if (!m_haveServices) {
return nullptr;
}
for(auto &it: m_servicesVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str());
@ -395,6 +377,13 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
}
}
size_t prev_size = m_servicesVector.size();
if(retrieveServices(&uuid)) {
if(m_servicesVector.size() > prev_size) {
return m_servicesVector.back();
}
}
NIMBLE_LOGD(LOG_TAG, "<< getService: not found");
return nullptr;
} // getService
@ -402,12 +391,41 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
/**
* @Get a pointer to the vector of found services.
* @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all services retrieved from the peripheral.
* If false the vector will be returned with the currently stored services,
* if vector is empty it will retrieve all services from the peripheral.
* @return a pointer to the vector of available services.
*/
std::vector<NimBLERemoteService*>* NimBLEClient::getServices() {
std::vector<NimBLERemoteService*>* NimBLEClient::getServices(bool refresh) {
if(refresh) {
clearServices();
}
if(m_servicesVector.empty()) {
if (!retrieveServices()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get services");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d services", m_servicesVector.size());
}
}
return &m_servicesVector;
}
/**
* @ Retrieves the full database of attributes that the peripheral has available.
*/
void NimBLEClient::discoverAttributes() {
for(auto svc: *getServices(true)) {
for(auto chr: *svc->getCharacteristics(true)) {
chr->getDescriptors(true);
}
}
}
/**
* @brief Ask the remote %BLE server for its services.
* A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of
@ -415,7 +433,7 @@ std::vector<NimBLERemoteService*>* NimBLEClient::getServices() {
* We then ask for the characteristics for each service found and their desciptors.
* @return true on success otherwise false if an error occurred
*/
bool NimBLEClient::retrieveServices() {
bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
/**
* Design
* ------
@ -424,6 +442,7 @@ bool NimBLEClient::retrieveServices() {
*/
NIMBLE_LOGD(LOG_TAG, ">> retrieveServices");
int rc = 0;
if(!m_isConnected){
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting");
@ -432,26 +451,21 @@ bool NimBLEClient::retrieveServices() {
m_semaphoreSearchCmplEvt.take("retrieveServices");
int rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, this);
if(uuid_filter == nullptr) {
rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, this);
} else {
rc = ble_gattc_disc_svc_by_uuid(m_conn_id, &uuid_filter->getNative()->u,
NimBLEClient::serviceDiscoveredCB, this);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_haveServices = false;
m_semaphoreSearchCmplEvt.give();
return false;
}
// wait until we have all the services
// If sucessful, remember that we now have services.
m_haveServices = (m_semaphoreSearchCmplEvt.wait("retrieveServices") == 0);
if(m_haveServices){
for (auto &it: m_servicesVector) {
if(!m_isConnected || !it->retrieveCharacteristics()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve characteristics -aborting");
return false;
}
}
if(m_semaphoreSearchCmplEvt.wait("retrieveServices") == 0){
NIMBLE_LOGD(LOG_TAG, "<< retrieveServices");
return true;
}
@ -472,7 +486,9 @@ int NimBLEClient::serviceDiscoveredCB(
const struct ble_gatt_error *error,
const struct ble_gatt_svc *service, void *arg)
{
NIMBLE_LOGD(LOG_TAG,"Service Discovered >> status: %d handle: %d", error->status, conn_handle);
NIMBLE_LOGD(LOG_TAG,"Service Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? service->start_handle : -1);
NimBLEClient *peer = (NimBLEClient*)arg;
int rc=0;
@ -518,7 +534,8 @@ int NimBLEClient::serviceDiscoveredCB(
* @returns characteristic value or an empty string if not found
*/
std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) {
NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
std::string ret = "";
NimBLERemoteService* pService = getService(serviceUUID);
@ -541,8 +558,11 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @param [in] characteristicUUID The characteristic whose value we wish to write.
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, const std::string &value) {
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value)
{
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
bool ret = false;
NimBLERemoteService* pService = getService(serviceUUID);
@ -679,8 +699,6 @@ uint16_t NimBLEClient::getMTU() {
return 0;
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle);
if(!client->m_haveServices)
return 0;
for(auto &it: client->m_servicesVector) {
// Dont waste cycles searching services without this handle in their range
@ -688,19 +706,26 @@ uint16_t NimBLEClient::getMTU() {
continue;
}
auto cVector = it->getCharacteristics();
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d", it->getUUID().toString().c_str(),event->notify_rx.attr_handle);
auto cVector = &it->m_characteristicVector;
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
it->getUUID().toString().c_str(),
event->notify_rx.attr_handle);
auto characteristic = cVector->cbegin();
for(; characteristic != cVector->cend(); ++characteristic) {
if((*characteristic)->m_handle == event->notify_rx.attr_handle) break;
if((*characteristic)->m_handle == event->notify_rx.attr_handle)
break;
}
if(characteristic != cVector->cend()) {
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str());
if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", (*characteristic)->toString().c_str());
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data, event->notify_rx.om->om_len, !event->notify_rx.indication);
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
(*characteristic)->toString().c_str());
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data,
event->notify_rx.om->om_len,
!event->notify_rx.indication);
}
break;
@ -778,7 +803,6 @@ uint16_t NimBLEClient::getMTU() {
event->mtu.conn_handle,
event->mtu.value);
client->m_semaphoreOpenEvt.give(0);
//client->m_mtu = event->mtu.value;
return 0;
} // BLE_GAP_EVENT_MTU

View file

@ -27,6 +27,11 @@
#include <vector>
#include <string>
typedef struct {
const NimBLEUUID *uuid;
const void *attribute;
} disc_filter_t;
class NimBLERemoteService;
class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice;
@ -37,55 +42,56 @@ class NimBLEAdvertisedDevice;
class NimBLEClient {
public:
bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true);
bool connect(const NimBLEAddress &address, uint8_t type = BLE_ADDR_PUBLIC, bool refreshServices = true); // Connect to the remote BLE Server
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); // Disconnect from the remote BLE Server
NimBLEAddress getPeerAddress(); // Get the address of the remote BLE Server
int getRssi(); // Get the RSSI of the remote BLE Server
std::vector<NimBLERemoteService*>* getServices(); // Get a vector of the services offered by the remote BLE Server
bool connect(const NimBLEAddress &address, uint8_t type = BLE_ADDR_PUBLIC,
bool refreshServices = true);
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
NimBLEAddress getPeerAddress();
int getRssi();
std::vector<NimBLERemoteService*>* getServices(bool refresh = false);
std::vector<NimBLERemoteService*>::iterator begin();
std::vector<NimBLERemoteService*>::iterator end();
NimBLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server.
NimBLERemoteService* getService(const NimBLEUUID &uuid); // Get a reference to a specified service offered by the remote BLE server.
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID); // Get the value of a given characteristic at a given service.
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, const std::string &value); // Set the value of a given characteristic at a given service.
bool isConnected(); // Return true if we are connected.
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks, bool deleteCallbacks = true);
std::string toString(); // Return a string representation of this client.
NimBLERemoteService* getService(const char* uuid);
NimBLERemoteService* getService(const NimBLEUUID &uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value);
bool isConnected();
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true);
std::string toString();
uint16_t getConnId();
uint16_t getMTU();
bool secureConnection();
void setConnectTimeout(uint8_t timeout);
void setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
uint16_t scanInterval=16, uint16_t scanWindow=16); // NimBLE default scan settings
uint16_t latency, uint16_t timeout,
uint16_t scanInterval=16, uint16_t scanWindow=16);
void updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
uint16_t latency, uint16_t timeout);
void discoverAttributes();
private:
NimBLEClient();
~NimBLEClient();
friend class NimBLEDevice;
friend class NimBLERemoteService;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
static int serviceDiscoveredCB(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg);
void clearServices(); // Clear any existing services.
bool retrieveServices(); //Retrieve services from the server
// void onHostReset();
friend class NimBLEDevice;
friend class NimBLERemoteService;
NimBLEAddress m_peerAddress = NimBLEAddress(""); // The BD address of the remote server.
uint16_t m_conn_id;
bool m_haveServices = false; // Have we previously obtain the set of services from the remote server.
bool m_isConnected = false; // Are we currently connected.
bool m_waitingToConnect =false;
bool m_deleteCallbacks = true;
int32_t m_connectTimeout;
//uint16_t m_mtu = 23;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
static int serviceDiscoveredCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_svc *service,
void *arg);
void clearServices();
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress = NimBLEAddress("");
uint16_t m_conn_id;
bool m_isConnected = false;
bool m_waitingToConnect =false;
bool m_deleteCallbacks = true;
int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks = nullptr;
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt");
FreeRTOS::Semaphore m_semeaphoreSecEvt = FreeRTOS::Semaphore("Security");

View file

@ -35,7 +35,9 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
* ble_uuid_any_t uuid;
* };
*/
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService, const struct ble_gatt_chr *chr) {
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
const struct ble_gatt_chr *chr)
{
switch (chr->uuid.u.type) {
case BLE_UUID_TYPE_16:
@ -51,13 +53,13 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_uuid = nullptr;
break;
}
m_handle = chr->val_handle;
m_defHandle = chr->def_handle;
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
m_rawData = nullptr;
m_dataLen = 0;
m_handle = chr->val_handle;
m_defHandle = chr->def_handle;
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
m_rawData = nullptr;
m_dataLen = 0;
} // NimBLERemoteCharacteristic
@ -145,9 +147,12 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
const struct ble_gatt_dsc *dsc,
void *arg)
{
NIMBLE_LOGD(LOG_TAG,"Descriptor Discovered >> status: %d handle: %d", error->status, conn_handle);
NIMBLE_LOGD(LOG_TAG,"Descriptor Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? dsc->handle : -1);
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)arg;
disc_filter_t *filter = (disc_filter_t*)arg;
NimBLEUUID *uuid_filter = (NimBLEUUID*)filter->uuid;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)filter->attribute;
int rc=0;
// Make sure the discovery is for this device
@ -157,56 +162,71 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
switch (error->status) {
case 0: {
if(dsc->uuid.u.type == BLE_UUID_TYPE_16 && dsc->uuid.u16.value == uint16_t(0x2803)) {
NIMBLE_LOGD(LOG_TAG,"Descriptor NOT found - end of Characteristic definintion");
rc = BLE_HS_EDONE;
break;
}
if(uuid_filter != nullptr) {
if(ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) {
return 0;
} else {
NIMBLE_LOGD(LOG_TAG,"Descriptor Found");
rc = BLE_HS_EDONE;
}
}
// Found a descriptor - add it to the vector
NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc);
characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor);
break;
}
case BLE_HS_EDONE:{
/* All descriptors in this characteristic discovered; */
characteristic->m_semaphoreGetDescEvt.give(0);
rc = 0;
break;
}
default:
rc = error->status;
break;
}
if (rc != 0) {
/** If rc == BLE_HS_EDONE, release the semaphore 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 semaphore rc.
*/
if (rc == BLE_HS_EDONE) {
characteristic->m_semaphoreGetDescEvt.give(0);
} else if(rc != 0) {
/* Error; abort discovery. */
// pass non-zero to semaphore on error to indicate an error finding descriptors
characteristic->m_semaphoreGetDescEvt.give(1);
// pass error code to semaphore waiting
characteristic->m_semaphoreGetDescEvt.give(rc);
}
NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", rc);
return rc;
}
/**
* @brief Populate the descriptors (if any) for this characteristic.
* @param [in] the end handle of the characteristic, or the service, whichever comes first.
*/
bool NimBLERemoteCharacteristic::retrieveDescriptors(uint16_t endHdl) {
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
int rc = 0;
//removeDescriptors(); // Remove any existing descriptors.
disc_filter_t filter;
filter.uuid = uuid_filter;
filter.attribute = this;
m_semaphoreGetDescEvt.take("retrieveDescriptors");
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
endHdl,
getRemoteService()->getEndHandle(),
NimBLERemoteCharacteristic::descriptorDiscCB,
this);
&filter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreGetDescEvt.give();
return false;
}
if(m_semaphoreGetDescEvt.wait("retrieveCharacteristics") != 0) {
// if there was an error release the resources
//removeDescriptors();
if(m_semaphoreGetDescEvt.wait("retrieveDescriptors") != 0) {
return false;
}
@ -216,30 +236,57 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(uint16_t endHdl) {
/**
* @brief Retrieve the vector of descriptors.
*/
std::vector<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors() {
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
* @param [in] uuid The UUID of the descriptor to find.
* @return The Remote descriptor (if present) or null if not present.
*/
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
for(auto &it: m_descriptorVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
return it;
}
}
size_t prev_size = m_descriptorVector.size();
if(retrieveDescriptors(&uuid)) {
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
}
}
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
/**
* @Get a pointer to the vector of found descriptors.
* @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all descriptors for this characteristic retrieved from the peripheral.
* If false the vector will be returned with the currently stored descriptors,
* if the vector is empty it will retrieve all descriptors for this characteristic
* from the peripheral.
* @return a pointer to the vector of descriptors for this characteristic.
*/
std::vector<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors(bool refresh) {
if(refresh) {
removeDescriptors();
}
if(m_descriptorVector.empty()) {
if (!retrieveDescriptors()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size());
}
}
return &m_descriptorVector;
} // getDescriptors
/**
* @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 iterator to the beginning of the vector of remote descriptor pointers.
* @return An iterator to the beginning of the vector of remote descriptor pointers.
@ -259,22 +306,20 @@ std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end()
/**
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
* @param [in] uuid The UUID of the descriptor to find.
* @return The Remote descriptor (if present) or null if not present.
* @brief Get the handle for this characteristic.
* @return The handle for this characteristic.
*/
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
uint16_t NimBLERemoteCharacteristic::getHandle() {
return m_handle;
} // getHandle
for(auto &it: m_descriptorVector) {
if(it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
return it;
}
}
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
/**
* @brief Get the handle for this characteristics definition.
* @return The handle for this characteristic definition.
*/
uint16_t NimBLERemoteCharacteristic::getDefHandle() {
return m_defHandle;
} // getDefHandle
/**
@ -339,7 +384,8 @@ uint8_t NimBLERemoteCharacteristic::readUInt8() {
* @return The value of the remote characteristic.
*/
std::string NimBLERemoteCharacteristic::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle());
NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x",
getUUID().toString().c_str(), getHandle(), getHandle());
int rc = 0;
int retryCount = 1;

View file

@ -20,19 +20,17 @@
#include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
//#include "NimBLEUUID.h"
//#include "FreeRTOS.h"
#include "NimBLERemoteService.h"
#include "NimBLERemoteDescriptor.h"
//#include <string>
#include <vector>
class NimBLERemoteService;
class NimBLERemoteDescriptor;
typedef void (*notify_callback)(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify);
typedef void (*notify_callback)(NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify);
/**
* @brief A model of a remote %BLE characteristic.
@ -51,7 +49,7 @@ public:
std::vector<NimBLERemoteDescriptor*>::iterator begin();
std::vector<NimBLERemoteDescriptor*>::iterator end();
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid);
std::vector<NimBLERemoteDescriptor*>* getDescriptors();
std::vector<NimBLERemoteDescriptor*>* getDescriptors(bool refresh = false);
uint16_t getHandle();
uint16_t getDefHandle();
NimBLEUUID getUUID();
@ -59,10 +57,16 @@ public:
uint8_t readUInt8();
uint16_t readUInt16();
uint32_t readUInt32();
bool registerForNotify(notify_callback _callback, bool notifications = true, bool response = true);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
bool writeValue(uint8_t newValue, bool response = false);
bool registerForNotify(notify_callback _callback,
bool notifications = true,
bool response = true);
bool writeValue(const uint8_t* data,
size_t length,
bool response = false);
bool writeValue(const std::string &newValue,
bool response = false);
bool writeValue(uint8_t newValue,
bool response = false);
std::string toString();
uint8_t* readRawData();
size_t getDataLength();
@ -72,19 +76,21 @@ private:
NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr);
friend class NimBLEClient;
friend class NimBLERemoteService;
friend class NimBLERemoteDescriptor;
friend class NimBLEClient;
friend class NimBLERemoteService;
friend class NimBLERemoteDescriptor;
// Private member functions
void removeDescriptors();
bool retrieveDescriptors(uint16_t endHdl);
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);
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);
void releaseSemaphores();
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);
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg);
// Private properties
NimBLEUUID m_uuid;
@ -102,7 +108,7 @@ private:
// We maintain a vector of descriptors owned by this characteristic.
std::vector<NimBLERemoteDescriptor*> m_descriptorVector;
}; // BLERemoteCharacteristic
}; // NimBLERemoteCharacteristic
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -29,7 +29,7 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
* @param [in] Reference to the struct that contains the descriptor information.
*/
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
const struct ble_gatt_dsc *dsc)
{
switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16:

View file

@ -28,30 +28,34 @@ class NimBLERemoteCharacteristic;
*/
class NimBLERemoteDescriptor {
public:
uint16_t getHandle();
uint16_t getHandle();
NimBLERemoteCharacteristic* getRemoteCharacteristic();
NimBLEUUID getUUID();
std::string readValue(void);
uint8_t readUInt8(void);
uint16_t readUInt16(void);
uint32_t readUInt32(void);
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
bool writeValue(uint8_t newValue, bool response = false);
NimBLEUUID getUUID();
std::string readValue(void);
uint8_t readUInt8(void);
uint16_t readUInt16(void);
uint32_t readUInt32(void);
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
bool writeValue(uint8_t newValue, bool response = false);
private:
friend class NimBLERemoteCharacteristic;
NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic, const struct ble_gatt_dsc *dsc);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
void releaseSemaphores();
friend class NimBLERemoteCharacteristic;
uint16_t m_handle; // Server handle of this descriptor.
NimBLEUUID m_uuid; // UUID of this descriptor.
std::string m_value; // Last received value of the descriptor.
NimBLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated.
NimBLERemoteDescriptor (NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
void releaseSemaphores();
uint16_t m_handle;
NimBLEUUID m_uuid;
std::string m_value;
NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt");
FreeRTOS::Semaphore m_semaphoreDescWrite = FreeRTOS::Semaphore("WriteDescEvt");

View file

@ -31,7 +31,7 @@ static const char* LOG_TAG = "NimBLERemoteService";
*/
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) {
NIMBLE_LOGD(LOG_TAG, ">> BLERemoteService()");
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()");
m_pClient = pClient;
switch (service->uuid.u.type) {
case BLE_UUID_TYPE_16:
@ -49,9 +49,7 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
m_haveCharacteristics = false;
NIMBLE_LOGD(LOG_TAG, "<< BLERemoteService()");
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()");
}
@ -98,11 +96,16 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
* @return Reference to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
if (m_haveCharacteristics) {
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
return it;
}
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
return it;
}
}
size_t prev_size = m_characteristicVector.size();
if(retrieveCharacteristics(&uuid)) {
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
}
}
@ -110,6 +113,33 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
} // getCharacteristic
/**
* @Get a pointer to the vector of found characteristics.
* @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics,
* if the vector is empty it will retrieve all characteristics of this service
* from the peripheral.
* @return a pointer to the vector of descriptors for this characteristic.
*/
std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) {
if(refresh) {
removeCharacteristics();
}
if(m_characteristicVector.empty()) {
if (!retrieveCharacteristics()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size());
}
}
return &m_characteristicVector;
} // getCharacteristics
/**
* @brief Callback for Characterisic discovery.
*/
@ -117,7 +147,8 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d", error->status, conn_handle);
NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? chr->val_handle : -1);
NimBLERemoteService *service = (NimBLERemoteService*)arg;
int rc=0;
@ -164,7 +195,7 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
* This function will not return until we have all the characteristics.
* @return N/A
*/
bool NimBLERemoteService::retrieveCharacteristics() {
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
int rc = 0;
@ -172,49 +203,28 @@ bool NimBLERemoteService::retrieveCharacteristics() {
m_semaphoreGetCharEvt.take("retrieveCharacteristics");
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
m_startHandle,
m_endHandle,
NimBLERemoteService::characteristicDiscCB,
this);
if(uuid_filter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
m_startHandle,
m_endHandle,
NimBLERemoteService::characteristicDiscCB,
this);
} else {
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
m_startHandle,
m_endHandle,
&uuid_filter->getNative()->u,
NimBLERemoteService::characteristicDiscCB,
this);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_haveCharacteristics = false;
m_semaphoreGetCharEvt.give();
return false;
}
m_haveCharacteristics = (m_semaphoreGetCharEvt.wait("retrieveCharacteristics") == 0);
if(m_haveCharacteristics){
uint16_t endHdl = 0xFFFF;
NIMBLE_LOGD(LOG_TAG, "Found %d Characteristics", m_characteristicVector.size());
for(auto it = m_characteristicVector.cbegin(); it != m_characteristicVector.cend(); ++it) {
NIMBLE_LOGD(LOG_TAG, "Found UUID: %s Handle: %d Def Handle: %d", (*it)->getUUID().toString().c_str(), (*it)->getHandle(), (*it)->getDefHandle());
// The descriptor handle is between this characteristic val_handle and the next ones def_handle
// so make the end of the scan at the handle before the next characteristic def_handle
// Make sure we don't go past the service end handle
if(++it != m_characteristicVector.cend()){
NIMBLE_LOGD(LOG_TAG, "Next UUID: %s Handle: %d Def Handle: %d", (*it)->getUUID().toString().c_str(), (*it)->getHandle(),(*it)->getDefHandle());
endHdl = (*it)->getDefHandle()-1;
}
else{
NIMBLE_LOGD(LOG_TAG, "END CHARS");
endHdl = m_endHandle;
}
--it;
//If there is no handles between this characteristic and the next there is no descriptor so skip to the next
if((*it)->getHandle() != endHdl){
if(!m_pClient->m_isConnected || !(*it)->retrieveDescriptors(endHdl)) {
return false;
}
}
//NIMBLE_LOGD(LOG_TAG, "Found %d Characteristics in service UUID: %s", chars->size(), myPair.first.c_str());
}
if(m_semaphoreGetCharEvt.wait("retrieveCharacteristics") == 0){
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true;
}
@ -225,15 +235,6 @@ bool NimBLERemoteService::retrieveCharacteristics() {
} // retrieveCharacteristics
/**
* @brief Retrieve a vector of all the characteristics of this service.
* @return A vector of all the characteristics of this service.
*/
std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics() {
return &m_characteristicVector;
} // getCharacteristics
/**
* @brief Get the client associated with this service.
* @return A reference to the client associated with this service.

View file

@ -41,18 +41,16 @@ public:
// Public methods
std::vector<NimBLERemoteCharacteristic*>::iterator begin();
std::vector<NimBLERemoteCharacteristic*>::iterator end();
NimBLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference.
NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid); // Get the specified characteristic reference.
// BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference.
std::vector<NimBLERemoteCharacteristic*>* getCharacteristics();
// void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* pCharacteristicMap);
NimBLEClient* getClient(void); // Get a reference to the client associated with this service.
uint16_t getHandle(); // Get the handle of this service.
NimBLEUUID getUUID(void); // Get the UUID of this service.
std::string getValue(const NimBLEUUID &characteristicUuid); // Get the value of a characteristic.
bool setValue(const NimBLEUUID &characteristicUuid, const std::string &value); // Set the value of a characteristic.
NimBLERemoteCharacteristic* getCharacteristic(const char* uuid);
NimBLERemoteCharacteristic* getCharacteristic(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 constructor ... never meant to be created by a user application.
@ -63,13 +61,14 @@ private:
friend class NimBLERemoteCharacteristic;
// Private methods
bool retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server.
bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr);
static int characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg);
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr,
void *arg);
uint16_t getStartHandle(); // Get the start handle for this service.
uint16_t getEndHandle(); // Get the end handle for this service.
uint16_t getStartHandle();
uint16_t getEndHandle();
void releaseSemaphores();
void removeCharacteristics();
@ -78,13 +77,12 @@ private:
// We maintain a vector of characteristics owned by this service.
std::vector<NimBLERemoteCharacteristic*> m_characteristicVector;
bool m_haveCharacteristics; // Have we previously obtained the characteristics.
NimBLEClient* m_pClient;
FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt");
NimBLEUUID m_uuid; // The UUID of this service.
uint16_t m_startHandle; // The starting handle of this service.
uint16_t m_endHandle; // The ending handle of this service.
}; // BLERemoteService
NimBLEUUID m_uuid;
uint16_t m_startHandle;
uint16_t m_endHandle;
}; // NimBLERemoteService
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */