diff --git a/API_DIFFERENCES.md b/API_DIFFERENCES.md index 26ad02c..32bf4a2 100644 --- a/API_DIFFERENCES.md +++ b/API_DIFFERENCES.md @@ -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. diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index cc9f6ab..a385126 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -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* NimBLEClient::getServices() { +std::vector* 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* 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 diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index a5db6b2..4bf2c33 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -27,6 +27,11 @@ #include #include +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* 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* getServices(bool refresh = false); std::vector::iterator begin(); std::vector::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"); diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index 0fc305d..85760b4 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -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* 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* 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::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; diff --git a/src/NimBLERemoteCharacteristic.h b/src/NimBLERemoteCharacteristic.h index d77b399..0b7d490 100644 --- a/src/NimBLERemoteCharacteristic.h +++ b/src/NimBLERemoteCharacteristic.h @@ -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 #include 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::iterator begin(); std::vector::iterator end(); NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid); - std::vector* getDescriptors(); + std::vector* 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 m_descriptorVector; -}; // BLERemoteCharacteristic +}; // NimBLERemoteCharacteristic #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED */ diff --git a/src/NimBLERemoteDescriptor.cpp b/src/NimBLERemoteDescriptor.cpp index 52743bb..057a68a 100644 --- a/src/NimBLERemoteDescriptor.cpp +++ b/src/NimBLERemoteDescriptor.cpp @@ -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: diff --git a/src/NimBLERemoteDescriptor.h b/src/NimBLERemoteDescriptor.h index 4763292..23fe13f 100644 --- a/src/NimBLERemoteDescriptor.h +++ b/src/NimBLERemoteDescriptor.h @@ -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"); diff --git a/src/NimBLERemoteService.cpp b/src/NimBLERemoteService.cpp index 79a5c9b..193a40b 100644 --- a/src/NimBLERemoteService.cpp +++ b/src/NimBLERemoteService.cpp @@ -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* 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* NimBLERemoteService::getCharacteristics() { - return &m_characteristicVector; -} // getCharacteristics - - /** * @brief Get the client associated with this service. * @return A reference to the client associated with this service. diff --git a/src/NimBLERemoteService.h b/src/NimBLERemoteService.h index b2a7a32..3803498 100644 --- a/src/NimBLERemoteService.h +++ b/src/NimBLERemoteService.h @@ -41,18 +41,16 @@ public: // Public methods std::vector::iterator begin(); std::vector::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* getCharacteristics(); -// void getCharacteristics(std::map* 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* 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 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 */