diff --git a/CMakeLists.txt b/CMakeLists.txt index 60df8115..3cccafa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ idf_component_register( "src/NimBLEHIDDevice.cpp" "src/NimBLERemoteCharacteristic.cpp" "src/NimBLERemoteDescriptor.cpp" + "src/NimBLERemoteGattUtils.cpp" "src/NimBLERemoteService.cpp" "src/NimBLERemoteValueAttribute.cpp" "src/NimBLEScan.cpp" diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index e5b1383c..a3700dbc 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -21,6 +21,7 @@ # include "NimBLEClient.h" # include "NimBLERemoteService.h" # include "NimBLERemoteCharacteristic.h" +# include "NimBLERemoteGattUtils.h" # include "NimBLEDevice.h" # include "NimBLELog.h" @@ -101,8 +102,8 @@ NimBLEClient::~NimBLEClient() { */ void NimBLEClient::deleteServices() { // Delete all the services. - for (auto& it : m_svcVec) { - delete it; + for (auto& svc : m_svcVec) { + delete svc; } std::vector().swap(m_svcVec); @@ -243,8 +244,7 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes, break; default: - NIMBLE_LOGE(LOG_TAG, - "Failed to connect to %s, rc=%d; %s", + NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s", std::string(m_peerAddress).c_str(), rc, NimBLEUtils::returnCodeToString(rc)); @@ -632,47 +632,13 @@ NimBLERemoteService* NimBLEClient::getService(const char* uuid) { NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID& uuid) { NIMBLE_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str()); - for (auto& it : m_svcVec) { - if (it->getUUID() == uuid) { - NIMBLE_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str()); - return it; - } - } - - size_t prevSize = m_svcVec.size(); - if (retrieveServices(&uuid)) { - if (m_svcVec.size() > prevSize) { - return m_svcVec.back(); - } + auto pSvc = NimBLERemoteGattUtils::getAttr + (uuid, m_svcVec, [this](const NimBLEUUID* u) { + return retrieveServices(u); + }); - // If the request was successful but 16/32 bit uuid not found - // try again with the 128 bit uuid. - if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) { - NimBLEUUID uuid128(uuid); - uuid128.to128(); - if (retrieveServices(&uuid128)) { - if (m_svcVec.size() > prevSize) { - return m_svcVec.back(); - } - } - } else { - // If the request was successful but the 128 bit uuid not found - // try again with the 16 bit uuid. - NimBLEUUID uuid16(uuid); - uuid16.to16(); - // if the uuid was 128 bit but not of the BLE base type this check will fail - if (uuid16.bitSize() == BLE_UUID_TYPE_16) { - if (retrieveServices(&uuid16)) { - if (m_svcVec.size() > prevSize) { - return m_svcVec.back(); - } - } - } - } - } - - NIMBLE_LOGD(LOG_TAG, "<< getService: not found"); - return nullptr; + NIMBLE_LOGD(LOG_TAG, "<< getService: %sfound", !pSvc ? "not " : ""); + return pSvc; } // getService /** @@ -685,11 +651,7 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID& uuid) { const std::vector& NimBLEClient::getServices(bool refresh) { if (refresh) { deleteServices(); - if (!retrieveServices()) { - NIMBLE_LOGE(LOG_TAG, "Error: Failed to get services"); - } else { - NIMBLE_LOGI(LOG_TAG, "Found %d services", m_svcVec.size()); - } + retrieveServices(); } return m_svcVec; @@ -725,36 +687,34 @@ bool NimBLEClient::discoverAttributes() { * * Here we ask the server for its set of services and wait until we have received them all. * @return true on success otherwise false if an error occurred */ -bool NimBLEClient::retrieveServices(const NimBLEUUID* uuidFilter) { +bool NimBLEClient::retrieveServices(const NimBLEUUID* uuid) { + NIMBLE_LOGD(LOG_TAG, ">> retrieveServices()"); + NimBLETaskData taskData(this); if (!isConnected()) { NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting"); return false; } - int rc = 0; - NimBLETaskData taskData(this); - - if (uuidFilter == nullptr) { - rc = ble_gattc_disc_all_svcs(m_connHandle, NimBLEClient::serviceDiscoveredCB, &taskData); - } else { - rc = ble_gattc_disc_svc_by_uuid(m_connHandle, uuidFilter->getBase(), NimBLEClient::serviceDiscoveredCB, &taskData); - } + int rc = (uuid == nullptr) + ? ble_gattc_disc_all_svcs(m_connHandle, svcDiscCB, &taskData) + : ble_gattc_disc_svc_by_uuid(m_connHandle, uuid->getBase(), svcDiscCB, &taskData); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE_RC(rc, LOG_TAG, "ble_gattc_disc_all_svcs"); m_lastErr = rc; return false; } NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); rc = taskData.m_flags; - if (rc == 0 || rc == BLE_HS_EDONE) { - return true; + if (rc != BLE_HS_EDONE) { + NIMBLE_LOGE_RC(rc, LOG_TAG, "Could not retrieve services"); + m_lastErr = rc; + return false; } - m_lastErr = rc; - NIMBLE_LOGE(LOG_TAG, "Could not retrieve services, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); - return false; + NIMBLE_LOGD(LOG_TAG, "<< retrieveServices(): found %d services.", m_svcVec.size()); + return true; } // getServices /** @@ -762,39 +722,29 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID* uuidFilter) { * @details When a service is found or there is none left or there was an error * the API will call this and report findings. */ -int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle, - 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, - (error->status == 0) ? service->start_handle : -1); - - NimBLETaskData* pTaskData = (NimBLETaskData*)arg; - NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance; - - if (error->status == BLE_HS_ENOTCONN) { - NIMBLE_LOGE(LOG_TAG, "<< Service Discovered; Disconnected"); - NimBLEUtils::taskRelease(*pTaskData, error->status); - return error->status; - } +int NimBLEClient::svcDiscCB(uint16_t connHandle, + const struct ble_gatt_error* error, + const struct ble_gatt_svc* service, + void* arg) { + const int rc = error->status; + auto pTaskData = (NimBLETaskData*)arg; + auto pClient = (NimBLEClient*)pTaskData->m_pInstance; + NIMBLE_LOGD(LOG_TAG, "Service Discovered >> status: %d handle: %d", rc, (rc == 0) ? service->start_handle : -1); // Make sure the service discovery is for this device if (pClient->getConnHandle() != connHandle) { return 0; } - if (error->status == 0) { - // Found a service - add it to the vector + if (rc == 0) { // Found a service - add it to the vector pClient->m_svcVec.push_back(new NimBLERemoteService(pClient, service)); return 0; } NimBLEUtils::taskRelease(*pTaskData, error->status); - NIMBLE_LOGD(LOG_TAG, "<< Service Discovered"); + NIMBLE_LOGD(LOG_TAG, "<< Service Discovered%s", (rc == BLE_HS_ENOTCONN) ? "; Not connected" : ""); return error->status; -} // serviceDiscoveredCB +} // serviceDiscCB /** * @brief Get the value of a specific characteristic associated with a specific service. @@ -803,8 +753,7 @@ int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle, * @returns characteristic value or an empty value if not found. */ NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID& serviceUUID, const NimBLEUUID& characteristicUUID) { - NIMBLE_LOGD(LOG_TAG, - ">> getValue: serviceUUID: %s, characteristicUUID: %s", + NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); @@ -833,15 +782,14 @@ bool NimBLEClient::setValue(const NimBLEUUID& serviceUUID, const NimBLEUUID& characteristicUUID, const NimBLEAttValue& value, bool response) { - NIMBLE_LOGD(LOG_TAG, - ">> setValue: serviceUUID: %s, characteristicUUID: %s", + NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); bool ret = false; auto pService = getService(serviceUUID); if (pService != nullptr) { - NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID); + auto pChar = pService->getCharacteristic(characteristicUUID); if (pChar != nullptr) { ret = pChar->writeValue(value, response); } @@ -858,11 +806,13 @@ bool NimBLEClient::setValue(const NimBLEUUID& serviceUUID, */ NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(uint16_t handle) { for (const auto& svc : m_svcVec) { - if (svc->getStartHandle() <= handle && handle <= svc->getEndHandle()) { - for (const auto& chr : svc->m_vChars) { - if (chr->getHandle() == handle) { - return chr; - } + if (svc->getStartHandle() > handle && handle > svc->getEndHandle()) { + continue; + } + + for (const auto& chr : svc->m_vChars) { + if (chr->getHandle() == handle) { + return chr; } } } @@ -882,28 +832,28 @@ uint16_t NimBLEClient::getMTU() const { * @brief Callback for the MTU exchange API function. * @details When the MTU exchange is complete the API will call this and report the new MTU. */ -int NimBLEClient::exchangeMTUCb(uint16_t conn_handle, const ble_gatt_error* error, uint16_t mtu, void* arg) { - NIMBLE_LOGD(LOG_TAG, "exchangeMTUCb: status=%d, mtu=%d", error->status, mtu); +int NimBLEClient::exchangeMTUCB(uint16_t connHandle, const ble_gatt_error* error, uint16_t mtu, void* arg) { + NIMBLE_LOGD(LOG_TAG, "exchangeMTUCB: status=%d, mtu=%d", error->status, mtu); NimBLEClient* pClient = (NimBLEClient*)arg; - if (pClient->getConnHandle() != conn_handle) { + if (pClient->getConnHandle() != connHandle) { return 0; } if (error->status != 0) { - NIMBLE_LOGE(LOG_TAG, "exchangeMTUCb() rc=%d %s", error->status, NimBLEUtils::returnCodeToString(error->status)); + NIMBLE_LOGE(LOG_TAG, "exchangeMTUCB() rc=%d %s", error->status, NimBLEUtils::returnCodeToString(error->status)); pClient->m_lastErr = error->status; } return 0; -} // exchangeMTUCb +} // exchangeMTUCB /** * @brief Begin the MTU exchange process with the server. * @returns true if the request was sent successfully. */ bool NimBLEClient::exchangeMTU() { - int rc = ble_gattc_exchange_mtu(m_connHandle, NimBLEClient::exchangeMTUCb, this); + int rc = ble_gattc_exchange_mtu(m_connHandle, NimBLEClient::exchangeMTUCB, this); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); m_lastErr = rc; @@ -989,25 +939,25 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) { pClient->m_pClientCallbacks->onConnect(pClient); } - if (pClient->m_config.exchangeMTU) { - if (!pClient->exchangeMTU()) { - rc = pClient->m_lastErr; // sets the error in the task data - break; - } - - return 0; // return as we may have a task waiting for the MTU before releasing it. + if (!pClient->m_config.exchangeMTU) { + break; } - } else { - pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE; - if (pClient->m_config.asyncConnect) { - pClient->m_pClientCallbacks->onConnectFail(pClient, rc); - if (pClient->m_config.deleteOnConnectFail) { - NimBLEDevice::deleteClient(pClient); - } + if (!pClient->exchangeMTU()) { + rc = pClient->m_lastErr; // sets the error in the task data + break; } + + return 0; // return as we may have a task waiting for the MTU before releasing it. } + pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE; + if (pClient->m_config.asyncConnect) { + pClient->m_pClientCallbacks->onConnectFail(pClient, rc); + if (pClient->m_config.deleteOnConnectFail) { + NimBLEDevice::deleteClient(pClient); + } + } break; } // BLE_GAP_EVENT_CONNECT @@ -1035,23 +985,23 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) { continue; } - NIMBLE_LOGD(LOG_TAG, - "checking service %s for handle: %d", + NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d", svc->getUUID().toString().c_str(), event->notify_rx.attr_handle); for (const auto& chr : svc->m_vChars) { - if (chr->getHandle() == event->notify_rx.attr_handle) { - NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", chr->toString().c_str()); + if (chr->getHandle() != event->notify_rx.attr_handle) { + continue; + } - uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om); - chr->m_value.setValue(event->notify_rx.om->om_data, data_len); + NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", chr->toString().c_str()); + uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om); + chr->m_value.setValue(event->notify_rx.om->om_data, data_len); - if (chr->m_notifyCallback != nullptr) { - chr->m_notifyCallback(chr, event->notify_rx.om->om_data, data_len, !event->notify_rx.indication); - } - break; + if (chr->m_notifyCallback != nullptr) { + chr->m_notifyCallback(chr, event->notify_rx.om->om_data, data_len, !event->notify_rx.indication); } + break; } } @@ -1064,8 +1014,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) { return 0; } NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); - NIMBLE_LOGD(LOG_TAG, - "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", + NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", event->conn_update_req.peer_params->itvl_min, event->conn_update_req.peer_params->itvl_max, event->conn_update_req.peer_params->latency, diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index eba7165f..66967922 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -116,13 +116,13 @@ class NimBLEClient { NimBLEClient(const NimBLEClient&) = delete; NimBLEClient& operator=(const NimBLEClient&) = delete; - bool retrieveServices(const NimBLEUUID* uuidFilter = nullptr); + bool retrieveServices(const NimBLEUUID* uuid = nullptr); + static int exchangeMTUCB(uint16_t connHandle, const ble_gatt_error* error, uint16_t mtu, void* arg); static int handleGapEvent(struct ble_gap_event* event, void* arg); - static int exchangeMTUCb(uint16_t conn_handle, const ble_gatt_error* error, uint16_t mtu, void* arg); - static int serviceDiscoveredCB(uint16_t connHandle, - const struct ble_gatt_error* error, - const struct ble_gatt_svc* service, - void* arg); + static int svcDiscCB(uint16_t connHandle, + const struct ble_gatt_error* error, + const struct ble_gatt_svc* service, + void* arg); NimBLEAddress m_peerAddress; mutable int m_lastErr; diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index bac85747..d3b20d1e 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -20,6 +20,7 @@ # include "NimBLERemoteCharacteristic.h" # include "NimBLERemoteDescriptor.h" +# include "NimBLERemoteGattUtils.h" # include "NimBLERemoteService.h" # include "NimBLEClient.h" # include "NimBLEUtils.h" @@ -29,7 +30,7 @@ typedef struct { const NimBLEUUID* uuid; - void* task_data; + void* taskData; } desc_filter_t; static const char* LOG_TAG = "NimBLERemoteCharacteristic"; @@ -37,7 +38,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic"; /** * @brief Constructor. * @param [in] svc A pointer to the service this characteristic belongs to. - * @param [in] ble_gatt_chr struct defined as: + * @param [in] chr struct defined as: * struct ble_gatt_chr { * uint16_t def_handle; * uint16_t val_handle; @@ -62,73 +63,68 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() { /** * @brief Callback used by the API when a descriptor is discovered or search complete. */ -int NimBLERemoteCharacteristic::descriptorDiscCB( - uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg) { - int rc = error->status; +int NimBLERemoteCharacteristic::dscDiscCB(uint16_t connHandle, + const ble_gatt_error* error, + uint16_t chrHandle, + const ble_gatt_dsc* dsc, + void* arg) { + int rc = error->status; + auto filter = (desc_filter_t*)arg; + auto pTaskData = (NimBLETaskData*)filter->taskData; + const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance; + const auto uuid = filter->uuid; // UUID to filter for NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1); - auto filter = (desc_filter_t*)arg; - auto pTaskData = (NimBLETaskData*)filter->task_data; - const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance; - const NimBLEUUID* uuidFilter = filter->uuid; - - if (pChr->getHandle() != chr_val_handle) { - return 0; // Descriptor not for this characteristic - } - - if (rc == 0) { - if (uuidFilter != nullptr) { - if (ble_uuid_cmp(uuidFilter->getBase(), &dsc->uuid.u) == 0) { - rc = BLE_HS_EDONE; // Found the descriptor, stop the search - } else { - return 0; // Not the descriptor we are looking for - } - } - + // Results for chrHandle added until rc != 0 + // Must find specified UUID if filter is used + if (rc == 0 && pChr->getHandle() == chrHandle + && (!uuid || 0 == ble_uuid_cmp(uuid->getBase(), &dsc->uuid.u))) { + // Return BLE_HS_EDONE if the descriptor was found, stop the search pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc)); - return 0; + rc = !!uuid * BLE_HS_EDONE; } - NimBLEUtils::taskRelease(*pTaskData, rc); - NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery"); + if (rc != 0) { + NimBLEUtils::taskRelease(*pTaskData, rc); + NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery"); + } 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. + * @param [in] filter Structure containing pointers to descriptor, UUID, and task data. + * @param out Pointer to hold result. + * @return True if successfully retrieved, success = BLE_HS_EDONE. */ -bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilter) const { +bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuid) const { NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); - - // If this is the last handle then there are no descriptors - if (getHandle() == getRemoteService()->getEndHandle()) { - NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found 0 descriptors."); - return true; - } - NimBLETaskData taskData(const_cast(this)); - desc_filter_t filter = {uuidFilter, &taskData}; + desc_filter_t filter = {uuid, &taskData}; + const uint16_t endHandle = getRemoteService()->getEndHandle(); + const uint16_t connHandle = getClient()->getConnHandle(); + const uint16_t starthandle = getHandle(); + // If this is the last handle then there are no more descriptors + if (starthandle == endHandle) { + NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size()); + return true; + } - int rc = ble_gattc_disc_all_dscs(getClient()->getConnHandle(), - getHandle(), - getRemoteService()->getEndHandle(), - NimBLERemoteCharacteristic::descriptorDiscCB, - &filter); + int rc = ble_gattc_disc_all_dscs(connHandle, starthandle, endHandle, dscDiscCB, &filter); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE_RC(rc, LOG_TAG, "ble_gattc_disc_all_dscs"); return false; } NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); rc = taskData.m_flags; - if (rc == 0 || rc == BLE_HS_EDONE) { - NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size()); - return true; + if (rc != BLE_HS_EDONE) { + NIMBLE_LOGE_RC(rc, LOG_TAG, "<< retrieveDescriptors(): failed"); + return false; } - NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); - return false; + NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size()); + return true; } // retrieveDescriptors /** @@ -138,50 +134,13 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilte */ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const { NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); - NimBLERemoteDescriptor* pDsc = nullptr; - size_t prev_size = m_vDescriptors.size(); - - for (const auto& it : m_vDescriptors) { - if (it->getUUID() == uuid) { - pDsc = it; - goto Done; - } - } - - if (retrieveDescriptors(&uuid)) { - if (m_vDescriptors.size() > prev_size) { - pDsc = m_vDescriptors.back(); - goto Done; - } - // If the request was successful but 16/32 bit uuid not found - // try again with the 128 bit uuid. - if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) { - NimBLEUUID uuid128(uuid); - uuid128.to128(); - if (retrieveDescriptors(&uuid128)) { - if (m_vDescriptors.size() > prev_size) { - pDsc = m_vDescriptors.back(); - } - } - } else { - // If the request was successful but the 128 bit uuid not found - // try again with the 16 bit uuid. - NimBLEUUID uuid16(uuid); - uuid16.to16(); - // if the uuid was 128 bit but not of the BLE base type this check will fail - if (uuid16.bitSize() == BLE_UUID_TYPE_16) { - if (retrieveDescriptors(&uuid16)) { - if (m_vDescriptors.size() > prev_size) { - pDsc = m_vDescriptors.back(); - } - } - } - } - } + auto pDsc = NimBLERemoteGattUtils::getAttr + (uuid, m_vDescriptors, [this](const NimBLEUUID* u) { + return retrieveDescriptors(u); + }); -Done: - NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", pDsc ? "" : "not "); + NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", !pDsc ? "not " : ""); return pDsc; } // getDescriptor @@ -311,7 +270,7 @@ size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID& uuid) cons * @return True if supported. */ bool NimBLERemoteCharacteristic::canBroadcast() const { - return (m_properties & BLE_GATT_CHR_PROP_BROADCAST) != 0; + return (m_properties & BLE_GATT_CHR_PROP_BROADCAST); }; /** diff --git a/src/NimBLERemoteCharacteristic.h b/src/NimBLERemoteCharacteristic.h index 532d814f..6f86bb22 100644 --- a/src/NimBLERemoteCharacteristic.h +++ b/src/NimBLERemoteCharacteristic.h @@ -65,10 +65,10 @@ class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute { ~NimBLERemoteCharacteristic(); bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const; - bool retrieveDescriptors(const NimBLEUUID* uuidFilter = nullptr) const; + bool retrieveDescriptors(const NimBLEUUID* uuid = nullptr) const; - static int descriptorDiscCB( - uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg); + static int dscDiscCB( + uint16_t connHandle, const ble_gatt_error* error, uint16_t chrHandle, const ble_gatt_dsc* dsc, void* arg); const NimBLERemoteService* m_pRemoteService{nullptr}; uint8_t m_properties{0}; diff --git a/src/NimBLERemoteGattUtils.cpp b/src/NimBLERemoteGattUtils.cpp new file mode 100644 index 00000000..041da11a --- /dev/null +++ b/src/NimBLERemoteGattUtils.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2020-2024 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +# include "NimBLERemoteGattUtils.h" +# include "NimBLEAddress.h" +# include "NimBLERemoteService.h" +# include "NimBLERemoteDescriptor.h" +# include "NimBLERemoteCharacteristic.h" + +/** + * @brief Get an attribute matching a uuid. + * @param [in] uuid Search for this uuid. + * @param [in] vec Vector to search through before trying to get attribute. + * @param [in] getter Attribute getter function to call. + */ +template +T* NimBLERemoteGattUtils::getAttr(const NimBLEUUID& uuid, const std::vector& vec, const std::function& getter) { + // Check if already exists. + for (const auto& v : vec) { + if (v->getUUID() == uuid) { + return v; + } + } + // Exit if request failed or uuid was found. + if (!getter(&uuid)) { + return nullptr; + } + // Try again with 128 bit uuid if request succeeded with no uuid found. + if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) { + NimBLEUUID uuid128 = NimBLEUUID(uuid).to128(); + bool found = getter(&uuid128); + return found ? vec.back() : nullptr; + } + // Try again with 16 bit uuid if request succeeded with no uuid found. + // If the uuid was 128 bit but not of the BLE base type this check will fail. + NimBLEUUID uuid16 = NimBLEUUID(uuid).to16(); + if (uuid16.bitSize() == BLE_UUID_TYPE_16) { + bool found = getter(&uuid16); + return found ? vec.back() : nullptr; + } + + return nullptr; +} + +using svc = NimBLERemoteService; using chr = NimBLERemoteCharacteristic; using dsc = NimBLERemoteDescriptor; +template svc* NimBLERemoteGattUtils::getAttr(const NimBLEUUID&, const std::vector&, const std::function&); +template chr* NimBLERemoteGattUtils::getAttr(const NimBLEUUID&, const std::vector&, const std::function&); +template dsc* NimBLERemoteGattUtils::getAttr(const NimBLEUUID&, const std::vector&, const std::function&); + +#endif // CONFIG_BT_ENABLED diff --git a/src/NimBLERemoteGattUtils.h b/src/NimBLERemoteGattUtils.h new file mode 100644 index 00000000..285929f3 --- /dev/null +++ b/src/NimBLERemoteGattUtils.h @@ -0,0 +1,39 @@ +/* + * Copyright 2020-2024 Ryan Powell and + * esp-nimble-cpp, NimBLE-Arduino contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NIMBLE_CPP_GATT_UTILS_H_ +#define NIMBLE_CPP_GATT_UTILS_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) +# include "NimBLEUUID.h" +# include +# include +# include + +/** + * @brief A BLE Remote GATT Utility class for getting an attribute. + */ +class NimBLERemoteGattUtils { + public: + template + static T* getAttr(const NimBLEUUID& uuid, const std::vector& vec, + const std::function& getter); +}; // NimBLERemoteGattUtils + +#endif /* CONFIG_BT_ENABLED */ +#endif /* NIMBLE_CPP_GATT_UTILS_H_ */ diff --git a/src/NimBLERemoteService.cpp b/src/NimBLERemoteService.cpp index c131af74..422cfdda 100644 --- a/src/NimBLERemoteService.cpp +++ b/src/NimBLERemoteService.cpp @@ -20,6 +20,7 @@ # include "NimBLERemoteService.h" # include "NimBLERemoteCharacteristic.h" +# include "NimBLERemoteGattUtils.h" # include "NimBLEClient.h" # include "NimBLEAttValue.h" # include "NimBLEUtils.h" @@ -76,51 +77,14 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u */ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID& uuid) const { NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str()); - NimBLERemoteCharacteristic* pChar = nullptr; - size_t prev_size = m_vChars.size(); - for (const auto& it : m_vChars) { - if (it->getUUID() == uuid) { - pChar = it; - goto Done; - } - } - - if (retrieveCharacteristics(&uuid)) { - if (m_vChars.size() > prev_size) { - pChar = m_vChars.back(); - goto Done; - } - - // If the request was successful but 16/32 bit uuid not found - // try again with the 128 bit uuid. - if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) { - NimBLEUUID uuid128(uuid); - uuid128.to128(); - if (retrieveCharacteristics(&uuid128)) { - if (m_vChars.size() > prev_size) { - pChar = m_vChars.back(); - } - } - } else { - // If the request was successful but the 128 bit uuid not found - // try again with the 16 bit uuid. - NimBLEUUID uuid16(uuid); - uuid16.to16(); - // if the uuid was 128 bit but not of the BLE base type this check will fail - if (uuid16.bitSize() == BLE_UUID_TYPE_16) { - if (retrieveCharacteristics(&uuid16)) { - if (m_vChars.size() > prev_size) { - pChar = m_vChars.back(); - } - } - } - } - } + auto pChr = NimBLERemoteGattUtils::getAttr + (uuid, m_vChars, [this](const NimBLEUUID* u) { + return retrieveCharacteristics(u); + }); -Done: - NIMBLE_LOGD(LOG_TAG, "<< Characteristic %sfound", pChar ? "" : "not "); - return pChar; + NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: %sfound", !pChr ? "not " : ""); + return pChr; } // getCharacteristic /** @@ -143,77 +107,65 @@ const std::vector& NimBLERemoteService::getCharacte * @brief Callback for Characteristic discovery. * @return success == 0 or error code. */ -int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, - const ble_gatt_error* error, - const ble_gatt_chr* chr, - void* arg) { - NIMBLE_LOGD(LOG_TAG, - "Characteristic Discovery >> status: %d handle: %d", - error->status, - (error->status == 0) ? chr->def_handle : -1); +int NimBLERemoteService::chrDiscCB(uint16_t connHandle, + const ble_gatt_error* error, + const ble_gatt_chr* chr, + void* arg) { + const int rc = error->status; auto pTaskData = (NimBLETaskData*)arg; const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance; - - if (error->status == BLE_HS_ENOTCONN) { - NIMBLE_LOGE(LOG_TAG, "<< Characteristic Discovery; Not connected"); - NimBLEUtils::taskRelease(*pTaskData, error->status); - return error->status; - } + NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >> status: %d handle: %d", rc, (rc == 0) ? chr->def_handle : -1); // Make sure the discovery is for this device - if (pSvc->getClient()->getConnHandle() != conn_handle) { + if (pSvc->getClient()->getConnHandle() != connHandle) { return 0; } - if (error->status == 0) { + if (rc == 0) { pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr)); return 0; } - NimBLEUtils::taskRelease(*pTaskData, error->status); - NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery"); - return error->status; + NimBLEUtils::taskRelease(*pTaskData, rc); + NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery%s", (rc == BLE_HS_ENOTCONN) ? "; Not connected" : ""); + return rc; } /** * @brief Retrieve all the characteristics for this service. * This function will not return until we have all the characteristics. - * @return True if successful. + * @return True if successfully retrieved, success = BLE_HS_EDONE. */ -bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const { - NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()"); - int rc = 0; +bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuid) const { + NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); NimBLETaskData taskData(const_cast(this)); - - if (uuidFilter == nullptr) { - rc = ble_gattc_disc_all_chrs(m_pClient->getConnHandle(), - getHandle(), - getEndHandle(), - NimBLERemoteService::characteristicDiscCB, - &taskData); - } else { - rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnHandle(), - getHandle(), - getEndHandle(), - uuidFilter->getBase(), - NimBLERemoteService::characteristicDiscCB, - &taskData); + const uint16_t hdlConn = m_pClient->getConnHandle(); + const uint16_t hdlEnd = getEndHandle(); + const uint16_t hdlStart = getHandle(); + // If this is the last handle then there are no more characteristics + if (hdlStart == hdlEnd) { + NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics(): found %d characteristics.", m_vChars.size()); + return true; } + int rc = (uuid == nullptr) + ? ble_gattc_disc_all_chrs(hdlConn, hdlStart, hdlEnd, chrDiscCB, &taskData) + : ble_gattc_disc_chrs_by_uuid(hdlConn, hdlStart, hdlEnd, uuid->getBase(), chrDiscCB, &taskData); + if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_chrs rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE_RC(rc, LOG_TAG, "ble_gattc_disc_chrs rc"); return false; } NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); rc = taskData.m_flags; - if (rc == 0 || rc == BLE_HS_EDONE) { - NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); - return true; + if (rc != BLE_HS_EDONE) { + NIMBLE_LOGE_RC(rc, LOG_TAG, "<< retrieveCharacteristics(): failed"); + return false; } - NIMBLE_LOGE(LOG_TAG, "<< retrieveCharacteristics() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); - return false; + NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics(): found %d characteristics.", m_vChars.size()); + return true; } // retrieveCharacteristics /** @@ -231,11 +183,8 @@ NimBLEClient* NimBLERemoteService::getClient() const { */ NimBLEAttValue NimBLERemoteService::getValue(const NimBLEUUID& uuid) const { const auto pChar = getCharacteristic(uuid); - if (pChar) { - return pChar->readValue(); - } - - return NimBLEAttValue{}; + return pChar ? pChar->readValue() + : NimBLEAttValue{}; } // readValue /** @@ -246,11 +195,8 @@ NimBLEAttValue NimBLERemoteService::getValue(const NimBLEUUID& uuid) const { */ bool NimBLERemoteService::setValue(const NimBLEUUID& uuid, const NimBLEAttValue& value) const { const auto pChar = getCharacteristic(uuid); - if (pChar) { - return pChar->writeValue(value); - } - - return false; + return pChar ? pChar->writeValue(value) + : false; } // setValue /** @@ -260,8 +206,8 @@ bool NimBLERemoteService::setValue(const NimBLEUUID& uuid, const NimBLEAttValue& * them. This method does just that. */ void NimBLERemoteService::deleteCharacteristics() const { - for (const auto& it : m_vChars) { - delete it; + for (const auto& chr : m_vChars) { + delete chr; } std::vector{}.swap(m_vChars); } // deleteCharacteristics diff --git a/src/NimBLERemoteService.h b/src/NimBLERemoteService.h index 8045788b..605a6d52 100644 --- a/src/NimBLERemoteService.h +++ b/src/NimBLERemoteService.h @@ -53,11 +53,11 @@ class NimBLERemoteService : public NimBLEAttribute { NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service); ~NimBLERemoteService(); - bool retrieveCharacteristics(const NimBLEUUID* uuidFilter = nullptr) const; - static int characteristicDiscCB(uint16_t conn_handle, - const struct ble_gatt_error* error, - const struct ble_gatt_chr* chr, - void* arg); + bool retrieveCharacteristics(const NimBLEUUID* uuid = nullptr) const; + static int chrDiscCB(uint16_t connHandle, + const struct ble_gatt_error* error, + const struct ble_gatt_chr* chr, + void* arg); mutable std::vector m_vChars{}; NimBLEClient* m_pClient{nullptr};