diff options
39 files changed, 1986 insertions, 618 deletions
diff --git a/examples/BLE_client/BLE_client.ino b/examples/BLE_client/BLE_client.ino index 3fc5efa..c0b6163 100644 --- a/examples/BLE_client/BLE_client.ino +++ b/examples/BLE_client/BLE_client.ino @@ -26,7 +26,7 @@ static void notifyCallback( Serial.println(length); } -void connectToServer(BLEAddress pAddress) { +bool connectToServer(BLEAddress pAddress) { Serial.print("Forming a connection to "); Serial.println(pAddress.toString().c_str()); @@ -42,8 +42,9 @@ void connectToServer(BLEAddress pAddress) { if (pRemoteService == nullptr) { Serial.print("Failed to find our service UUID: "); Serial.println(serviceUUID.toString().c_str()); - return; + return false; } + Serial.println(" - Found our service"); // Obtain a reference to the characteristic in the service of the remote BLE server. @@ -51,8 +52,9 @@ void connectToServer(BLEAddress pAddress) { if (pRemoteCharacteristic == nullptr) { Serial.print("Failed to find our characteristic UUID: "); Serial.println(charUUID.toString().c_str()); - return; + return false; } + Serial.println(" - Found our characteristic"); // Read the value of the characteristic. std::string value = pRemoteCharacteristic->readValue(); @@ -109,9 +111,13 @@ void loop() { // BLE Server with which we wish to connect. Now we connect to it. Once we are // connected we set the connected flag to be true. if (doConnect == true) { - connectToServer(*pServerAddress); + if (connectToServer(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } doConnect = false; - connected = true; } // If we are connected to a peer BLE Server, update the characteristic each time we are reached diff --git a/examples/BLE_notify/BLE_notify.ino b/examples/BLE_notify/BLE_notify.ino index f2539f7..44506c2 100644 --- a/examples/BLE_notify/BLE_notify.ino +++ b/examples/BLE_notify/BLE_notify.ino @@ -1,6 +1,6 @@ /* Video: https://www.youtube.com/watch?v=oCMOYS71NIU - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleNotify.cpp + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp Ported to Arduino ESP32 by Evandro Copercini Create a BLE server that, once we receive a connection, will send periodic notifications. diff --git a/examples/BLE_scan/BLE_scan.ino b/examples/BLE_scan/BLE_scan.ino index a884062..ef7d892 100644 --- a/examples/BLE_scan/BLE_scan.ino +++ b/examples/BLE_scan/BLE_scan.ino @@ -1,5 +1,5 @@ /* - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleScan.cpp + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp Ported to Arduino ESP32 by Evandro Copercini */ diff --git a/examples/BLE_server/BLE_server.ino b/examples/BLE_server/BLE_server.ino index c70c935..45ebf99 100644 --- a/examples/BLE_server/BLE_server.ino +++ b/examples/BLE_server/BLE_server.ino @@ -1,5 +1,5 @@ /* - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleServer.cpp + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp Ported to Arduino ESP32 by Evandro Copercini */ diff --git a/examples/BLE_uart/BLE_uart.ino b/examples/BLE_uart/BLE_uart.ino index 444ba6b..a8ab2d7 100644 --- a/examples/BLE_uart/BLE_uart.ino +++ b/examples/BLE_uart/BLE_uart.ino @@ -1,6 +1,6 @@ /* Video: https://www.youtube.com/watch?v=oCMOYS71NIU - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleNotify.cpp + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp Ported to Arduino ESP32 by Evandro Copercini Create a BLE server that, once we receive a connection, will send periodic notifications. diff --git a/examples/BLE_write/BLE_write.ino b/examples/BLE_write/BLE_write.ino index 0573669..ed5ebc6 100644 --- a/examples/BLE_write/BLE_write.ino +++ b/examples/BLE_write/BLE_write.ino @@ -1,5 +1,5 @@ /* - Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLETests/SampleWrite.cpp + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp Ported to Arduino ESP32 by Evandro Copercini */ diff --git a/library.properties b/library.properties index afb6d27..fdb01e2 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ESP32 BLE Arduino -version=0.4.2 +version=0.4.3 author=Neil Kolban <kolban1@kolban.com> maintainer=Neil Kolban <kolban1@kolban.com> sentence=BLE functions for ESP32 @@ -7,4 +7,4 @@ paragraph=This library provides an implementation Bluetooth Low Energy support f category=Communication url=https://github.com/nkolban/ESP32_BLE_Arduino architectures=esp32 -includes=BLEDevice.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h
\ No newline at end of file +includes=BLE.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h
\ No newline at end of file diff --git a/src/BLEAdvertisedDevice.cpp b/src/BLEAdvertisedDevice.cpp index 97ec1e3..5074b81 100644 --- a/src/BLEAdvertisedDevice.cpp +++ b/src/BLEAdvertisedDevice.cpp @@ -60,7 +60,7 @@ BLEAddress BLEAdvertisedDevice::getAddress() { * * @return The appearance of the advertised device. */ -uint16_t BLEAdvertisedDevice::getApperance() { +uint16_t BLEAdvertisedDevice::getAppearance() { return m_appearance; } @@ -265,7 +265,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) { } // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE default: { - ESP_LOGD(LOG_TAG, "Unhandled type"); + ESP_LOGD(LOG_TAG, "Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type); break; } } // switch @@ -329,7 +329,7 @@ void BLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) { void BLEAdvertisedDevice::setName(std::string name) { m_name = name; m_haveName = true; - ESP_LOGD(LOG_TAG, "- name: %s", m_name.c_str()); + ESP_LOGD(LOG_TAG, "- setName(): name: %s", m_name.c_str()); } // setName @@ -340,7 +340,7 @@ void BLEAdvertisedDevice::setName(std::string name) { void BLEAdvertisedDevice::setRSSI(int rssi) { m_rssi = rssi; m_haveRSSI = true; - ESP_LOGD(LOG_TAG, "- rssi: %d", m_rssi); + ESP_LOGD(LOG_TAG, "- setRSSI(): rssi: %d", m_rssi); } // setRSSI @@ -367,7 +367,7 @@ void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) { void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) { m_serviceUUID = serviceUUID; m_haveServiceUUID = true; - ESP_LOGD(LOG_TAG, "- serviceUUID: %s", serviceUUID.toString().c_str()); + ESP_LOGD(LOG_TAG, "- setServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str()); } // setRSSI @@ -390,7 +390,7 @@ std::string BLEAdvertisedDevice::toString() { std::stringstream ss; ss << "Name: " << getName() << ", Address: " << getAddress().toString(); if (haveAppearance()) { - ss << ", appearance: " << getApperance(); + ss << ", appearance: " << getAppearance(); } if (haveManufacturerData()) { char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length()); diff --git a/src/BLEAdvertisedDevice.h b/src/BLEAdvertisedDevice.h index 2fb2652..fbdeeec 100644 --- a/src/BLEAdvertisedDevice.h +++ b/src/BLEAdvertisedDevice.h @@ -30,7 +30,7 @@ public: BLEAdvertisedDevice(); BLEAddress getAddress(); - uint16_t getApperance(); + uint16_t getAppearance(); std::string getManufacturerData(); std::string getName(); int getRSSI(); diff --git a/src/BLEAdvertising.cpp b/src/BLEAdvertising.cpp index ad076dd..2647966 100644 --- a/src/BLEAdvertising.cpp +++ b/src/BLEAdvertising.cpp @@ -45,6 +45,24 @@ BLEAdvertising::BLEAdvertising() { /** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); +} // addServiceUUID + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The string representation of the service to expose. + */ +void BLEAdvertising::addServiceUUID(const char* serviceUUID) { + addServiceUUID(BLEUUID(serviceUUID)); +} // addServiceUUID + + +/** * @brief Set the device appearance in the advertising data. * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here: * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml. @@ -57,52 +75,6 @@ void BLEAdvertising::setAppearance(uint16_t appearance) { /** - * @brief Set the service UUID. - * We maintain a class member called m_advData (esp_ble_adv_data_t) that is passed to the - * ESP-IDF advertising functions. In this method, we see two fields within that structure - * namely service_uuid_len and p_service_uuid to be the information supplied in the passed - * in service uuid. - * @param [in] uuid The UUID of the service. - * @return N/A. - */ -void BLEAdvertising::setServiceUUID(const char* serviceUUID) { - return setServiceUUID(BLEUUID(serviceUUID)); -} -/** - * @brief Set the service UUID. - * We maintain a class member called m_advData (esp_ble_adv_data_t) that is passed to the - * ESP-IDF advertising functions. In this method, we see two fields within that structure - * namely service_uuid_len and p_service_uuid to be the information supplied in the passed - * in service uuid. - * @param [in] uuid The UUID of the service. - * @return N/A. - */ -void BLEAdvertising::setServiceUUID(BLEUUID serviceUUID) { - ESP_LOGD(LOG_TAG, ">> setServiceUUID - %s", serviceUUID.toString().c_str()); - m_serviceUUID = serviceUUID; // Save the new service UUID - esp_bt_uuid_t* espUUID = m_serviceUUID.getNative(); - switch(espUUID->len) { - case ESP_UUID_LEN_16: { - m_advData.service_uuid_len = 2; - m_advData.p_service_uuid = reinterpret_cast<uint8_t*>(&espUUID->uuid.uuid16); - break; - } - case ESP_UUID_LEN_32: { - m_advData.service_uuid_len = 4; - m_advData.p_service_uuid = reinterpret_cast<uint8_t*>(&espUUID->uuid.uuid32); - break; - } - case ESP_UUID_LEN_128: { - m_advData.service_uuid_len = 16; - m_advData.p_service_uuid = reinterpret_cast<uint8_t*>(&espUUID->uuid.uuid128); - break; - } - } // switch - ESP_LOGD(LOG_TAG, "<< setServiceUUID"); -} // setServiceUUID - - -/** * @brief Start advertising. * Start advertising. * @return N/A. @@ -110,24 +82,49 @@ void BLEAdvertising::setServiceUUID(BLEUUID serviceUUID) { void BLEAdvertising::start() { ESP_LOGD(LOG_TAG, ">> start"); - if (m_advData.service_uuid_len > 0) { - uint8_t hexData[16*2+1]; - BLEUtils::buildHexData(hexData, m_advData.p_service_uuid, m_advData.service_uuid_len); - ESP_LOGD(LOG_TAG, " - Service: service_uuid_len=%d, p_service_uuid=0x%x (data=%s)", - m_advData.service_uuid_len, - (uint32_t)m_advData.p_service_uuid, - (m_advData.service_uuid_len > 0?(char *)hexData:"N/A") - ); - } // We have a service to advertise + // We have a vector of service UUIDs that we wish to advertise. In order to use the + // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) + // representations. If we have 1 or more services to advertise then we allocate enough + // storage to host them and then copy them in one at a time into the contiguous storage. + int numServices = m_serviceUUIDs.size(); + if (numServices > 0) { + m_advData.service_uuid_len = 16*numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + uint8_t* p = m_advData.p_service_uuid; + for (int i=0; i<numServices; i++) { + ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str()); + BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128(); + memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16); + p+=16; + } + } else { + m_advData.service_uuid_len = 0; + ESP_LOGD(LOG_TAG, "- no services advertised"); + } // Set the configuration for advertising. + m_advData.set_scan_rsp = false; esp_err_t errRc = ::esp_ble_gap_config_adv_data(&m_advData); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } + m_advData.set_scan_rsp = true; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + // If we had services to advertise then we previously allocated some storage for them. + // Here we release that storage. + if (m_advData.service_uuid_len > 0) { + delete[] m_advData.p_service_uuid; + m_advData.p_service_uuid = nullptr; + } + // Start advertising. errRc = ::esp_ble_gap_start_advertising(&m_advParams); if (errRc != ESP_OK) { @@ -152,4 +149,6 @@ void BLEAdvertising::stop() { } ESP_LOGD(LOG_TAG, "<< stop"); } // stop + + #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLEAdvertising.h b/src/BLEAdvertising.h index 2d0b51c..6f315b8 100644 --- a/src/BLEAdvertising.h +++ b/src/BLEAdvertising.h @@ -11,6 +11,7 @@ #if defined(CONFIG_BT_ENABLED) #include <esp_gap_ble_api.h> #include "BLEUUID.h" +#include <vector> /** * @brief Perform and manage %BLE advertising. @@ -20,15 +21,15 @@ class BLEAdvertising { public: BLEAdvertising(); + void addServiceUUID(BLEUUID serviceUUID); + void addServiceUUID(const char* serviceUUID); void start(); void stop(); void setAppearance(uint16_t appearance); - void setServiceUUID(const char* serviceUUID); - void setServiceUUID(BLEUUID serviceUUID); private: esp_ble_adv_data_t m_advData; esp_ble_adv_params_t m_advParams; - BLEUUID m_serviceUUID; + std::vector<BLEUUID> m_serviceUUIDs; }; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ diff --git a/src/BLECharacteristic.cpp b/src/BLECharacteristic.cpp index f5b8200..284032e 100644 --- a/src/BLECharacteristic.cpp +++ b/src/BLECharacteristic.cpp @@ -94,19 +94,20 @@ void BLECharacteristic::executeCreate(BLEService* pService) { m_semaphoreCreateEvt.take("executeCreate"); - std::string strValue = m_value.getValue(); - + /* esp_attr_value_t value; - value.attr_len = strValue.length(); + value.attr_len = m_value.getLength(); value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; - value.attr_value = (uint8_t*)strValue.data(); + value.attr_value = m_value.getData(); + */ esp_err_t errRc = ::esp_ble_gatts_add_char( m_pService->getHandle(), getUUID().getNative(), static_cast<esp_gatt_perm_t>(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE), getProperties(), - &value, + //&value, + nullptr, &control); // Whether to auto respond or not. if (errRc != ESP_OK) { @@ -131,7 +132,6 @@ void BLECharacteristic::executeCreate(BLEService* pService) { } // executeCreate - /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. @@ -141,6 +141,7 @@ BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID)); } // getDescriptorByUUID + /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. @@ -274,7 +275,7 @@ void BLECharacteristic::handleGATTServerEvent( ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s", getHandle(), getUUID().toString().c_str()); - char *pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len); + char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len); ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData); free(pHexData); @@ -422,6 +423,7 @@ void BLECharacteristic::handleGATTServerEvent( } // handleGATTServerEvent + /** * @brief Send an indication. * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication diff --git a/src/BLECharacteristic.h b/src/BLECharacteristic.h index b39c022..24977c0 100644 --- a/src/BLECharacteristic.h +++ b/src/BLECharacteristic.h @@ -29,16 +29,16 @@ public: void setByUUID(const char* uuid, BLEDescriptor *pDescriptor); void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor); void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor); - BLEDescriptor *getByUUID(const char* uuid); - BLEDescriptor *getByUUID(BLEUUID uuid); - BLEDescriptor *getByHandle(uint16_t handle); - std::string toString(); + BLEDescriptor* getByUUID(const char* uuid); + BLEDescriptor* getByUUID(BLEUUID uuid); + BLEDescriptor* getByHandle(uint16_t handle); + std::string toString(); void handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param); - BLEDescriptor *getFirst(); - BLEDescriptor *getNext(); + esp_ble_gatts_cb_param_t* param); + BLEDescriptor* getFirst(); + BLEDescriptor* getNext(); private: std::map<std::string, BLEDescriptor *> m_uuidMap; std::map<uint16_t, BLEDescriptor *> m_handleMap; diff --git a/src/BLECharacteristicMap.cpp b/src/BLECharacteristicMap.cpp index f475e83..6ded0a6 100644 --- a/src/BLECharacteristicMap.cpp +++ b/src/BLECharacteristicMap.cpp @@ -10,6 +10,17 @@ #include <iomanip> #include "BLEService.h" + +/** + * @brief Return the characteristic by handle. + * @param [in] handle The handle to look up the characteristic. + * @return The characteristic. + */ +BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + /** * @brief Return the characteristic by UUID. * @param [in] UUID The UUID to look up the characteristic. @@ -19,6 +30,7 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) { return getByUUID(BLEUUID(uuid)); } + /** * @brief Return the characteristic by UUID. * @param [in] UUID The UUID to look up the characteristic. @@ -36,26 +48,49 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { /** - * @brief Return the characteristic by handle. - * @param [in] handle The handle to look up the characteristic. - * @return The characteristic. + * @brief Get the first characteristic in the map. + * @return The first characteristic in the map. */ -BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) { - return m_handleMap.at(handle); -} // getByHandle +BLECharacteristic* BLECharacteristicMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLECharacteristic* pRet = m_iterator->second; + m_iterator++; + return pRet; +} // getFirst /** - * @brief Set the characteristic by UUID. - * @param [in] uuid The uuid of the characteristic. - * @param [in] characteristic The characteristic to cache. - * @return N/A. + * @brief Get the next characteristic in the map. + * @return The next characteristic in the map. */ -void BLECharacteristicMap::setByUUID( - BLEUUID uuid, - BLECharacteristic *pCharacteristic) { - m_uuidMap.insert(std::pair<std::string, BLECharacteristic *>(uuid.toString(), pCharacteristic)); -} // setByUUID +BLECharacteristic* BLECharacteristicMap::getNext() { + if (m_iterator == m_uuidMap.end()) { + return nullptr; + } + BLECharacteristic* pRet = m_iterator->second; + m_iterator++; + return pRet; +} // getNext + + +/** + * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ +void BLECharacteristicMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every Service we have. + for (auto &myPair : m_uuidMap) { + myPair.second->handleGATTServerEvent(event, gatts_if, param); + } +} // handleGATTServerEvent /** @@ -71,6 +106,19 @@ void BLECharacteristicMap::setByHandle(uint16_t handle, /** + * @brief Set the characteristic by UUID. + * @param [in] uuid The uuid of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void BLECharacteristicMap::setByUUID( + BLEUUID uuid, + BLECharacteristic *pCharacteristic) { + m_uuidMap.insert(std::pair<std::string, BLECharacteristic *>(uuid.toString(), pCharacteristic)); +} // setByUUID + + +/** * @brief Return a string representation of the characteristic map. * @return A string representation of the characteristic map. */ @@ -89,48 +137,4 @@ std::string BLECharacteristicMap::toString() { } // toString -/** - * @breif Pass the GATT server event onwards to each of the characteristics found in the mapping - * @param [in] event - * @param [in] gatts_if - * @param [in] param - */ -void BLECharacteristicMap::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - // Invoke the handler for every Service we have. - for (auto &myPair : m_uuidMap) { - myPair.second->handleGATTServerEvent(event, gatts_if, param); - } -} // handleGATTServerEvent - - -/** - * @brief Get the first characteristic in the map. - * @return The first characteristic in the map. - */ -BLECharacteristic* BLECharacteristicMap::getFirst() { - m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLECharacteristic *pRet = m_iterator->second; - m_iterator++; - return pRet; -} // getFirst - - -/** - * @brief Get the next characteristic in the map. - * @return The next characteristic in the map. - */ -BLECharacteristic* BLECharacteristicMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLECharacteristic *pRet = m_iterator->second; - m_iterator++; - return pRet; -} // getNext #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLEClient.cpp b/src/BLEClient.cpp index f9b38c3..645c4ae 100644 --- a/src/BLEClient.cpp +++ b/src/BLEClient.cpp @@ -48,12 +48,28 @@ BLEClient::BLEClient() { m_haveServices = false; } // BLEClient + /** - * @brief Connect to the partner. + * @brief Destructor. + */ +BLEClient::~BLEClient() { + // We may have allocated service references associated with this client. Before we are finished + // with the client, we must release resources. + for (auto &myPair : m_servicesMap) { + delete myPair.second; + } + m_servicesMap.clear(); +} // ~BLEClient + + +/** + * @brief Connect to the partner (BLE Server). * @param [in] address The address of the partner. + * @return True on success. */ bool BLEClient::connect(BLEAddress address) { ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); + // We need the connection handle that we get from registering the application. We register the app // and then block on its completion. When the event has arrived, we will have the handle. m_semaphoreRegEvt.take("connect"); @@ -62,10 +78,12 @@ bool BLEClient::connect(BLEAddress address) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return false; } + m_semaphoreRegEvt.wait("connect"); m_peerAddress = address; + // Perform the open connection request against the target BLE Server. m_semaphoreOpenEvt.take("connect"); errRc = ::esp_ble_gattc_open( getGattcIf(), @@ -110,36 +128,6 @@ void BLEClient::gattClientEventHandler( // Execute handler code based on the type of event received. switch(event) { // - // ESP_GATTC_NOTIFY_EVT - // - // notify - // uint16_t conn_id - // esp_bd_addr_t remote_bda - // esp_gatt_srvc_id_t srvc_id - // esp_gatt_id_t char_id - // esp_gatt_id_t descr_id - // uint16_t value_len - // uint8_t* value - // bool is_notify - // - case ESP_GATTC_NOTIFY_EVT: { - BLERemoteService *pBLERemoteService = getService(BLEUUID(evtParam->notify.srvc_id.id.uuid)); - if (pBLERemoteService == nullptr) { - ESP_LOGE(LOG_TAG, "Could not find service with UUID %s for notification", BLEUUID(evtParam->notify.srvc_id.id.uuid).toString().c_str()); - break; - } - BLERemoteCharacteristic* pBLERemoteCharacteristic = pBLERemoteService->getCharacteristic(BLEUUID(evtParam->notify.char_id.uuid)); - if (pBLERemoteCharacteristic == nullptr) { - ESP_LOGE(LOG_TAG, "Could not find characteristic with UUID %s for notification", BLEUUID(evtParam->notify.char_id.uuid).toString().c_str()); - break; - } - if (pBLERemoteCharacteristic->m_notifyCallback != nullptr) { - pBLERemoteCharacteristic->m_notifyCallback(pBLERemoteCharacteristic, evtParam->notify.value, evtParam->notify.value_len, evtParam->notify.is_notify); - } - break; - } // ESP_GATTC_NOTIFY_EVT - - // // ESP_GATTC_OPEN_EVT // // open: @@ -189,12 +177,19 @@ void BLEClient::gattClientEventHandler( // ESP_GATTC_SEARCH_RES_EVT // // search_res: - // - uint16_t conn_id - // - esp_gatt_srvc_id_t srvc_id + // - uint16_t conn_id + // - uint16_t start_handle + // - uint16_t end_handle + // - esp_gatt_id_t srvc_id // case ESP_GATTC_SEARCH_RES_EVT: { BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id); - BLERemoteService* pRemoteService = new BLERemoteService(evtParam->search_res.srvc_id, this); + BLERemoteService* pRemoteService = new BLERemoteService( + evtParam->search_res.srvc_id, + this, + evtParam->search_res.start_handle, + evtParam->search_res.end_handle + ); m_servicesMap.insert(std::pair<std::string, BLERemoteService *>(uuid.toString(), pRemoteService)); break; } // ESP_GATTC_SEARCH_RES_EVT @@ -205,6 +200,7 @@ void BLEClient::gattClientEventHandler( } } // Switch + // Pass the request on to all services. for (auto &myPair : m_servicesMap) { myPair.second->gattClientEventHandler(event, gattc_if, evtParam); } @@ -215,7 +211,7 @@ void BLEClient::gattClientEventHandler( /** * @brief Retrieve the address of the peer. * - * Returns the address of the %BLE peer to which this client is connected. + * Returns the Bluetooth device address of the %BLE peer to which this client is connected. */ BLEAddress BLEClient::getPeerAddress() { return m_peerAddress; @@ -232,15 +228,14 @@ esp_gatt_if_t BLEClient::getGattcIf() { } // getGattcIf - /** - * @brief Get the service object corresponding to the uuid. + * @brief Get the service BLE Remote Service instance corresponding to the uuid. * @param [in] uuid The UUID of the service being sought. * @return A reference to the Service or nullptr if don't know about it. */ BLERemoteService* BLEClient::getService(const char* uuid) { return getService(BLEUUID(uuid)); -} +} // getService /** @@ -249,6 +244,7 @@ BLERemoteService* BLEClient::getService(const char* uuid) { * @return A reference to the Service or nullptr if don't know about it. */ BLERemoteService* BLEClient::getService(BLEUUID uuid) { + ESP_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str()); // Design // ------ // We wish to retrieve the service given its UUID. It is possible that we have not yet asked the @@ -258,12 +254,14 @@ BLERemoteService* BLEClient::getService(BLEUUID uuid) { if (!m_haveServices) { getServices(); } - std::string v = uuid.toString(); + std::string uuidStr = uuid.toString(); for (auto &myPair : m_servicesMap) { - if (myPair.first == v) { + if (myPair.first == uuidStr) { + ESP_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str()); return myPair.second; } - } + } // End of each of the services. + ESP_LOGD(LOG_TAG, "<< getService: not found"); return nullptr; } // getService @@ -287,7 +285,7 @@ std::map<std::string, BLERemoteService*>* BLEClient::getServices() { esp_err_t errRc = esp_ble_gattc_search_service( getGattcIf(), getConnId(), - NULL // Filter UUID + nullptr // Filter UUID ); m_semaphoreSearchCmplEvt.take("getServices"); if (errRc != ESP_OK) { @@ -319,7 +317,7 @@ std::string BLEClient::toString() { ss << "\nServices:\n"; for (auto &myPair : m_servicesMap) { ss << myPair.second->toString() << "\n"; - // myPair.second is the value + // myPair.second is the value } return ss.str(); } // toString diff --git a/src/BLEClient.h b/src/BLEClient.h index 898f98c..494f51d 100644 --- a/src/BLEClient.h +++ b/src/BLEClient.h @@ -28,6 +28,7 @@ class BLEClientCallbacks; class BLEClient { public: BLEClient(); + ~BLEClient(); bool connect(BLEAddress address); void disconnect(); BLEAddress getPeerAddress(); @@ -39,8 +40,9 @@ public: private: friend class BLEDevice; - friend class BLERemoteCharacteristic; friend class BLERemoteService; + friend class BLERemoteCharacteristic; + friend class BLERemoteDescriptor; void gattClientEventHandler( esp_gattc_cb_event_t event, diff --git a/src/BLEDevice.cpp b/src/BLEDevice.cpp index 04cae14..b2777e5 100644 --- a/src/BLEDevice.cpp +++ b/src/BLEDevice.cpp @@ -7,21 +7,20 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include <freertos/FreeRTOS.h> +#include <freertos/event_groups.h> #include <freertos/task.h> #include <esp_err.h> #include <nvs_flash.h> -#include <freertos/FreeRTOS.h> -#include <freertos/event_groups.h> -#include <bt.h> // ESP32 BLE -#include <esp_bt_main.h> // ESP32 BLE -#include <esp_gap_ble_api.h> // ESP32 BLE -// ESP32 BLE -#include <esp_gatts_api.h> // ESP32 BLE -#include <esp_err.h> // ESP32 ESP-IDF -#include <esp_log.h> // ESP32 ESP-IDF -#include <map> // Part of C++ STL -#include <sstream> -#include <iomanip> +#include <bt.h> // ESP32 BLE +#include <esp_bt_main.h> // ESP32 BLE +#include <esp_gap_ble_api.h> // ESP32 BLE +#include <esp_gatts_api.h> // ESP32 BLE +#include <esp_gattc_api.h> // ESP32 BLE +#include <esp_err.h> // ESP32 ESP-IDF +#include <esp_log.h> // ESP32 ESP-IDF +#include <map> // Part of C++ Standard library +#include <sstream> // Part of C++ Standard library +#include <iomanip> // Part of C++ Standard library #include "BLEDevice.h" #include "BLEClient.h" @@ -30,13 +29,18 @@ static const char* LOG_TAG = "BLEDevice"; -BLEServer *BLEDevice::m_bleServer = nullptr; -BLEScan *BLEDevice::m_pScan = nullptr; -BLEClient *BLEDevice::m_pClient = nullptr; - -#include <esp_gattc_api.h> +/** + * Singletons for the BLEDevice. + */ +BLEServer* BLEDevice::m_pServer = nullptr; +BLEScan* BLEDevice::m_pScan = nullptr; +BLEClient* BLEDevice::m_pClient = nullptr; +/** + * @brief Create a new instance of a client. + * @return A new instance of the client. + */ BLEClient* BLEDevice::createClient() { m_pClient = new BLEClient(); return m_pClient; @@ -44,23 +48,38 @@ BLEClient* BLEDevice::createClient() { /** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ +BLEServer* BLEDevice::createServer() { + ESP_LOGD(LOG_TAG, ">> createServer"); + m_pServer = new BLEServer(); + m_pServer->createApp(0); + ESP_LOGD(LOG_TAG, "<< createServer"); + return m_pServer; +} // createServer + + +/** * @brief Handle GATT server events. * - * @param [in] event - * @param [in] gatts_if - * @param [in] param + * @param [in] event The event that has been newly received. + * @param [in] gatts_if The connection to the GATT interface. + * @param [in] param Parameters for the event. */ void BLEDevice::gattServerEventHandler( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param + esp_ble_gatts_cb_param_t* param ) { ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s", gatts_if, BLEUtils::gattServerEventTypeToString(event).c_str()); + BLEUtils::dumpGattServerEvent(event, gatts_if, param); - if (BLEDevice::m_bleServer != nullptr) { - BLEDevice::m_bleServer->handleGATTServerEvent(event, gatts_if, param); + + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); } } // gattServerEventHandler @@ -69,29 +88,26 @@ void BLEDevice::gattServerEventHandler( * @brief Handle GATT client events. * * Handler for the GATT client events. - * * `ESP_GATTC_OPEN_EVT` – Invoked when a connection is opened. - * * `ESP_GATTC_PREP_WRITE_EVT` – Response to write a characteristic. - * * `ESP_GATTC_READ_CHAR_EVT` – Response to read a characteristic. - * * `ESP_GATTC_REG_EVT` – Invoked when a GATT client has been registered. * * @param [in] event * @param [in] gattc_if * @param [in] param */ void BLEDevice::gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param) { ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); BLEUtils::dumpGattClientEvent(event, gattc_if, param); - +/* switch(event) { default: { break; } } // switch + */ // If we have a client registered, call it. if (BLEDevice::m_pClient != nullptr) { @@ -124,8 +140,8 @@ void BLEDevice::gapEventHandler( } } // switch - if (BLEDevice::m_bleServer != nullptr) { - BLEDevice::m_bleServer->handleGAPEvent(event, param); + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGAPEvent(event, param); } if (BLEDevice::m_pScan != nullptr) { @@ -135,6 +151,18 @@ void BLEDevice::gapEventHandler( /** + * @brief Retrieve the Scan object that we use for scanning. + * @return The scanning object reference. + */ +BLEScan* BLEDevice::getScan() { + if (m_pScan == nullptr) { + m_pScan = new BLEScan(); + } + return m_pScan; +} // getScan + + +/** * @brief Initialize the %BLE environment. * @param deviceName The device name of the device. */ @@ -205,18 +233,4 @@ void BLEDevice::init(std::string deviceName) { } // init - -/** - * @brief Retrieve the Scan object that we use for scanning. - * @return The scanning object reference. - */ -BLEScan* BLEDevice::getScan() { - if (m_pScan == nullptr) { - m_pScan = new BLEScan(); - } - return m_pScan; -} // getScan - - - #endif // CONFIG_BT_ENABLED diff --git a/src/BLEDevice.h b/src/BLEDevice.h index 9d767c1..eb9fdaa 100644 --- a/src/BLEDevice.h +++ b/src/BLEDevice.h @@ -24,30 +24,34 @@ */ class BLEDevice { public: - static void dumpDevices(); - static BLEClient *createClient(); - static void init(std::string deviceName); - //static void scan(int duration, esp_ble_scan_type_t scan_type = BLE_SCAN_TYPE_PASSIVE); - static BLEScan *getScan(); - static BLEServer *m_bleServer; + static BLEClient* createClient(); + static BLEServer* createServer(); + static void dumpDevices(); + static BLEScan* getScan(); + static void init(std::string deviceName); + +private: + static BLEServer *m_pServer; static BLEScan *m_pScan; static BLEClient *m_pClient; -private: static esp_gatt_if_t getGattcIF(); static void gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param); + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param); + static void gattServerEventHandler( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param); + esp_ble_gatts_cb_param_t* param); + static void gapEventHandler( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t *param); + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + }; // class BLE #endif // CONFIG_BT_ENABLED diff --git a/src/BLERemoteCharacteristic.cpp b/src/BLERemoteCharacteristic.cpp index 5227bf6..2e0fbb0 100644 --- a/src/BLERemoteCharacteristic.cpp +++ b/src/BLERemoteCharacteristic.cpp @@ -17,21 +17,98 @@ #include <sstream> #include "BLEUtils.h" #include "GeneralUtils.h" +#include "BLERemoteDescriptor.h" -static const char* LOG_TAG = "BLERemoteCharacteristic"; +static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class. +/** + * @brief Constructor. + * @param [in] handle The BLE server side handle of this characteristic. + * @param [in] uuid The UUID of this characteristic. + * @param [in] charProp The properties of this characteristic. + * @param [in] pRemoteService A reference to the remote service to which this remote characteristic pertains. + */ BLERemoteCharacteristic::BLERemoteCharacteristic( - esp_gatt_id_t charId, + uint16_t handle, + BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService) { - m_charId = charId; + ESP_LOGD(LOG_TAG, ">> BLERemoteCharacteristic: handle: %d 0x%d, uuid: %s", handle, handle, uuid.toString().c_str()); + m_handle = handle; + m_uuid = uuid; m_charProp = charProp; m_pRemoteService = pRemoteService; m_notifyCallback = nullptr; + + retrieveDescriptors(); // Get the descriptors for this characteristic + ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic"); } // BLERemoteCharacteristic +/** + *@brief Destructor. + */ +BLERemoteCharacteristic::~BLERemoteCharacteristic() { + removeDescriptors(); // Release resources for any descriptor information we may have allocated. +} // ~BLERemoteCharacteristic + + +/** + * @brief Does the characteristic support broadcasting? + * @return True if the characteristic supports broadcasting. + */ +bool BLERemoteCharacteristic::canBroadcast() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_BROADCAST) != 0; +} // canBroadcast + + +/** + * @brief Does the characteristic support indications? + * @return True if the characteristic supports indications. + */ +bool BLERemoteCharacteristic::canIndicate() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_INDICATE) != 0; +} // canIndicate + + +/** + * @brief Does the characteristic support notifications? + * @return True if the characteristic supports notifications. + */ +bool BLERemoteCharacteristic::canNotify() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0; +} // canNotify + + +/** + * @brief Does the characteristic support reading? + * @return True if the characteristic supports reading. + */ +bool BLERemoteCharacteristic::canRead() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_READ) != 0; +} // canRead + + +/** + * @brief Does the characteristic support writing? + * @return True if the characteristic supports writing. + */ +bool BLERemoteCharacteristic::canWrite() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE) != 0; +} // canWrite + + +/** + * @brief Does the characteristic support writing with no response? + * @return True if the characteristic supports writing with no response. + */ +bool BLERemoteCharacteristic::canWriteNoResponse() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) != 0; +} // canWriteNoResponse + + +/* static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { if (id1.id.inst_id != id2.id.inst_id) { return false; @@ -41,8 +118,9 @@ static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { } return true; } // compareSrvcId +*/ - +/* static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { if (id1.inst_id != id2.inst_id) { return false; @@ -52,6 +130,7 @@ static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { } return true; } // compareCharId +*/ /** @@ -69,31 +148,56 @@ void BLERemoteCharacteristic::gattClientEventHandler( esp_ble_gattc_cb_param_t* evtParam) { switch(event) { // - // ESP_GATTC_READ_CHAR_EVT - // This event indicates that the server has responded to the read request. + // ESP_GATTC_NOTIFY_EVT // - // read: - // esp_gatt_status_t status - // uint16_t conn_id - // esp_gatt_srvc_id_t srvc_id - // esp_gatt_id_t char_id - // esp_gatt_id_t descr_id - // uint8_t* value - // uint16_t value_type - // uint16_t value_len - case ESP_GATTC_READ_CHAR_EVT: { - if (compareSrvcId(evtParam->read.srvc_id, *m_pRemoteService->getSrvcId()) == false) { + // notify + // - uint16_t conn_id - The connection identifier of the server. + // - esp_bd_addr_t remote_bda - The device address of the BLE server. + // - uint16_t handle - The handle of the characteristic for which the event is being received. + // - uint16_t value_len - The length of the received data. + // - uint8_t* value - The received data. + // - bool is_notify - True if this is a notify, false if it is an indicate. + // + // We have received a notification event which means that the server wishes us to know about a notification + // piece of data. What we must now do is find the characteristic with the associated handle and then + // invoke its notification callback (if it has one). + // + case ESP_GATTC_NOTIFY_EVT: { + if (evtParam->notify.handle != getHandle()) { break; } + if (m_notifyCallback != nullptr) { + ESP_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", toString().c_str()); + m_notifyCallback( + this, + evtParam->notify.value, + evtParam->notify.value_len, + evtParam->notify.is_notify + ); + } // End we have a callback function ... + break; + } // ESP_GATTC_NOTIFY_EVT - if (evtParam->read.conn_id != m_pRemoteService->getClient()->getConnId()) { - break; - } - if (compareGattId(evtParam->read.char_id, m_charId) == false) { + // + // ESP_GATTC_READ_CHAR_EVT + // This event indicates that the server has responded to the read request. + // + // read: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - uint16_t handle + // - uint8_t* value + // - uint16_t value_len + // + case ESP_GATTC_READ_CHAR_EVT: { + // If this event is not for us, then nothing further to do. + if (evtParam->read.handle != getHandle()) { break; } + // At this point, we have determined that the event is for us, so now we save the value + // and unlock the semaphore to ensure that the requestor of the data can continue. if (evtParam->read.status == ESP_GATT_OK) { m_value = std::string((char*)evtParam->read.value, evtParam->read.value_len); } else { @@ -110,39 +214,53 @@ void BLERemoteCharacteristic::gattClientEventHandler( // // reg_for_notify: // - esp_gatt_status_t status - // - esp_gatt_srvc_id_t srvc_id - // - esp_gatt_id_t char_id + // - uint16_t handle + // case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - if (compareSrvcId(evtParam->reg_for_notify.srvc_id, *m_pRemoteService->getSrvcId()) == false) { + // If the request is not for this BLERemoteCharacteristic then move on to the next. + if (evtParam->reg_for_notify.handle != getHandle()) { break; } - if (compareGattId(evtParam->reg_for_notify.char_id, m_charId) == false) { + + // We have processed the notify registration and can unlock the semaphore. + m_semaphoreRegForNotifyEvt.give(); + break; + } // ESP_GATTC_REG_FOR_NOTIFY_EVT + + + // + // ESP_GATTC_UNREG_FOR_NOTIFY_EVT + // + // unreg_for_notify: + // - esp_gatt_status_t status + // - uint16_t handle + // + case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { + if (evtParam->unreg_for_notify.handle != getHandle()) { break; } + // We have processed the notify un-registration and can unlock the semaphore. m_semaphoreRegForNotifyEvt.give(); break; - } // ESP_GATTC_REG_FOR_NOTIFY_EVT + } // ESP_GATTC_UNREG_FOR_NOTIFY_EVT: // // ESP_GATTC_WRITE_CHAR_EVT // // write: - // esp_gatt_status_t status - // uint16_t conn_id - // esp_gatt_srvc_id_t srvc_id - // esp_gatt_id_t char_id - // esp_gatt_id_t descr_id + // - esp_gatt_status_t status + // - uint16_t conn_id + // - uint16_t handle + // case ESP_GATTC_WRITE_CHAR_EVT: { - if (compareSrvcId(evtParam->write.srvc_id, *m_pRemoteService->getSrvcId()) == false) { - break; - } - if (evtParam->write.conn_id != m_pRemoteService->getClient()->getConnId()) { - break; - } - if (compareGattId(evtParam->write.char_id, m_charId) == false) { + // Determine if this event is for us and, if not, pass onwards. + if (evtParam->write.handle != getHandle()) { break; } + + // There is nothing further we need to do here. This is merely an indication + // that the write has completed and we can unlock the caller. m_semaphoreWriteCharEvt.give(); break; } // ESP_GATTC_WRITE_CHAR_EVT @@ -151,13 +269,118 @@ void BLERemoteCharacteristic::gattClientEventHandler( default: { break; } - } + } // End switch }; // gattClientEventHandler +/** + * @brief Populate the descriptors (if any) for this characteristic. + */ +void BLERemoteCharacteristic::retrieveDescriptors() { + ESP_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); + + removeDescriptors(); // Remove any existing descriptors. + + // Loop over each of the descriptors within the service associated with this characteristic. + // For each descriptor we find, create a BLERemoteDescriptor instance. + uint16_t offset = 0; + esp_gattc_descr_elem_t result; + while(1) { + uint16_t count = 1; + esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr( + getRemoteService()->getClient()->getGattcIf(), + getRemoteService()->getClient()->getConnId(), + getHandle(), + &result, + &count, + offset + ); + + if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + break; + } + + if (status != ESP_GATT_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_descr: %s", BLEUtils::gattStatusToString(status).c_str()); + break; + } + + if (count == 0) { + break; + } + ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str()); + + // We now have a new characteristic ... let us add that to our set of known characteristics + BLERemoteDescriptor *pNewRemoteDescriptor = new BLERemoteDescriptor( + result.handle, + BLEUUID(result.uuid), + this + ); + + m_descriptorMap.insert(std::pair<std::string, BLERemoteDescriptor*>(pNewRemoteDescriptor->getUUID().toString(), pNewRemoteDescriptor)); + + offset++; + } // while true + //m_haveCharacteristics = true; // Remember that we have received the characteristics. + ESP_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", offset); +} // getDescriptors + + +/** + * @brief Retrieve the map of descriptors keyed by UUID. + */ +std::map<std::string, BLERemoteDescriptor *>* BLERemoteCharacteristic::getDescriptors() { + return &m_descriptorMap; +} // getDescriptors + + +/** + * @brief Get the handle for this characteristic. + * @return The handle for this characteristic. + */ +uint16_t BLERemoteCharacteristic::getHandle() { + //ESP_LOGD(LOG_TAG, ">> getHandle: Characteristic: %s", getUUID().toString().c_str()); + //ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", m_handle, m_handle); + return m_handle; +} // getHandle + + +/** + * @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. + */ +BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) { + ESP_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); + std::string v = uuid.toString(); + for (auto &myPair : m_descriptorMap) { + if (myPair.first == v) { + ESP_LOGD(LOG_TAG, "<< getDescriptor: found"); + return myPair.second; + } + } + ESP_LOGD(LOG_TAG, "<< getDescriptor: Not found"); + return nullptr; +} // getDescriptor + + +/** + * @brief Get the remote service associated with this characteristic. + * @return The remote service associated with this characteristic. + */ +BLERemoteService* BLERemoteCharacteristic::getRemoteService() { + return m_pRemoteService; +} // getRemoteService + + +/** + * @brief Get the UUID for this characteristic. + * @return The UUID for this characteristic. + */ BLEUUID BLERemoteCharacteristic::getUUID() { - return BLEUUID(m_charId.uuid); -} + return m_uuid; +} // getUUID + /** * @brief Read an unsigned 16 bit value @@ -203,17 +426,18 @@ uint8_t BLERemoteCharacteristic::readUInt8(void) { * @return The value of the remote characteristic. */ std::string BLERemoteCharacteristic::readValue() { - ESP_LOGD(LOG_TAG, ">> readValue()"); + ESP_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle()); m_semaphoreReadCharEvt.take("readValue"); // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. + // This is an asynchronous request which means that we must block waiting for the response + // to become available. esp_err_t errRc = ::esp_ble_gattc_read_char( m_pRemoteService->getClient()->getGattcIf(), - m_pRemoteService->getClient()->getConnId(), - m_pRemoteService->getSrvcId(), - &m_charId, - ESP_GATT_AUTH_REQ_NONE); + m_pRemoteService->getClient()->getConnId(), // The connection ID to the BLE server + getHandle(), // The handle of this characteristic + ESP_GATT_AUTH_REQ_NONE); // Security if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -224,38 +448,51 @@ std::string BLERemoteCharacteristic::readValue() { // in m_value will contain our data. m_semaphoreReadCharEvt.wait("readValue"); - ESP_LOGD(LOG_TAG, "<< readValue()"); + ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); return m_value; } // readValue /** * @brief Register for notifications. - * @param [in] notifyCallback A callback to be invoked for a notification. + * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are + * unregistering a notification. * @return N/A. */ void BLERemoteCharacteristic::registerForNotify( void (*notifyCallback)( BLERemoteCharacteristic* pBLERemoteCharacteristic, - uint8_t* pData, - size_t length, - bool isNotify)) { - ESP_LOGD(LOG_TAG, ">> registerForNotify()"); + uint8_t* pData, + size_t length, + bool isNotify)) { + ESP_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); - m_notifyCallback = notifyCallback; // Save the notification callback. + m_notifyCallback = notifyCallback; // Save the notification callback. m_semaphoreRegForNotifyEvt.take("registerForNotify"); - esp_err_t errRc = ::esp_ble_gattc_register_for_notify( - m_pRemoteService->getClient()->getGattcIf(), - *m_pRemoteService->getClient()->getPeerAddress().getNative(), - m_pRemoteService->getSrvcId(), - &m_charId); + if (notifyCallback != nullptr) { // If we have a callback function, then this is a registration. + esp_err_t errRc = ::esp_ble_gattc_register_for_notify( + m_pRemoteService->getClient()->getGattcIf(), + *m_pRemoteService->getClient()->getPeerAddress().getNative(), + getHandle() + ); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } // End Register + else { // If we weren't passed a callback function, then this is an unregistration. + esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify( + m_pRemoteService->getClient()->getGattcIf(), + *m_pRemoteService->getClient()->getPeerAddress().getNative(), + getHandle() + ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } // End Unregister m_semaphoreRegForNotifyEvt.wait("registerForNotify"); @@ -264,14 +501,31 @@ void BLERemoteCharacteristic::registerForNotify( /** + * @brief Delete the descriptors in the descriptor map. + * We maintain a map called m_descriptorMap that contains pointers to BLERemoteDescriptors + * object references. Since we allocated these in this class, we are also responsible for deleteing + * them. This method does just that. + * @return N/A. + */ +void BLERemoteCharacteristic::removeDescriptors() { + // Iterate through all the descriptors releasing their storage and erasing them from the map. + for (auto &myPair : m_descriptorMap) { + m_descriptorMap.erase(myPair.first); + delete myPair.second; + } + m_descriptorMap.clear(); // Technically not neeeded, but just to be sure. +} // removeCharacteristics + + +/** * @brief Convert a BLERemoteCharacteristic to a string representation; * @return a String representation. */ std::string BLERemoteCharacteristic::toString() { std::ostringstream ss; - ss << "Characteristic: uuid: " << BLEUUID(m_charId.uuid).toString() << - ", props: " << BLEUtils::characteristicPropertiesToString(m_charProp) << - ", inst_id: " << (int)m_charId.inst_id; + ss << "Characteristic: uuid: " << m_uuid.toString() << + ", handle: " << getHandle() << " 0x" << std::hex << getHandle() << + ", props: " << BLEUtils::characteristicPropertiesToString(m_charProp); return ss.str(); } // toString @@ -287,11 +541,11 @@ void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { m_semaphoreWriteCharEvt.take("writeValue"); + // Invoke the ESP-IDF API to perform the write. esp_err_t errRc = ::esp_ble_gattc_write_char( m_pRemoteService->getClient()->getGattcIf(), m_pRemoteService->getClient()->getConnId(), - m_pRemoteService->getSrvcId(), - &m_charId, + getHandle(), newValue.length(), (uint8_t*)newValue.data(), response?ESP_GATT_WRITE_TYPE_RSP:ESP_GATT_WRITE_TYPE_NO_RSP, @@ -332,5 +586,4 @@ void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool resp writeValue(std::string((char *)data, length), response); } // writeValue - #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLERemoteCharacteristic.h b/src/BLERemoteCharacteristic.h index b5b22b6..6f23f49 100644 --- a/src/BLERemoteCharacteristic.h +++ b/src/BLERemoteCharacteristic.h @@ -15,49 +15,71 @@ #include <esp_gattc_api.h> #include "BLERemoteService.h" +#include "BLERemoteDescriptor.h" #include "BLEUUID.h" #include "FreeRTOS.h" class BLERemoteService; +class BLERemoteDescriptor; /** * @brief A model of a remote %BLE characteristic. */ class BLERemoteCharacteristic { public: - BLERemoteCharacteristic(esp_gatt_id_t charId, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); + ~BLERemoteCharacteristic(); // Public member functions + bool canBroadcast(); + bool canIndicate(); + bool canNotify(); + bool canRead(); + bool canWrite(); + bool canWriteNoResponse(); + BLERemoteDescriptor* getDescriptor(BLEUUID uuid); + std::map<std::string, BLERemoteDescriptor *>* getDescriptors(); + uint16_t getHandle(); BLEUUID getUUID(); std::string readValue(void); uint8_t readUInt8(void); uint16_t readUInt16(void); uint32_t readUInt32(void); - void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)); + void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)); void writeValue(uint8_t* data, size_t length, bool response = false); void writeValue(std::string newValue, bool response = false); void writeValue(uint8_t newValue, bool response = false); std::string toString(void); private: + BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); friend class BLEClient; friend class BLERemoteService; + friend class BLERemoteDescriptor; // Private member functions void gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *evtParam); + esp_ble_gattc_cb_param_t* evtParam); + + + BLERemoteService* getRemoteService(); + void removeDescriptors(); + void retrieveDescriptors(); // Private properties - esp_gatt_id_t m_charId; + BLEUUID m_uuid; esp_gatt_char_prop_t m_charProp; + uint16_t m_handle; BLERemoteService* m_pRemoteService; FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); std::string m_value; - void (*m_notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify); + void (*m_notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + + // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. + std::map<std::string, BLERemoteDescriptor*> m_descriptorMap; }; // BLERemoteCharacteristic #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */ diff --git a/src/BLERemoteDescriptor.cpp b/src/BLERemoteDescriptor.cpp index 2be312a..a606929 100644 --- a/src/BLERemoteDescriptor.cpp +++ b/src/BLERemoteDescriptor.cpp @@ -6,6 +6,154 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include <sstream> #include "BLERemoteDescriptor.h" +#include "GeneralUtils.h" +#include <esp_log.h> + +static const char* LOG_TAG = "BLERemoteDescriptor"; + + +BLERemoteDescriptor::BLERemoteDescriptor( + uint16_t handle, + BLEUUID uuid, + BLERemoteCharacteristic* pRemoteCharacteristic) { + + m_handle = handle; + m_uuid = uuid; + m_pRemoteCharacteristic = pRemoteCharacteristic; +} + +/** + * @brief Retrieve the handle associated with this remote descriptor. + * @return The handle associated with this remote descriptor. + */ +uint16_t BLERemoteDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Retrieve the UUID associated this remote descriptor. + * @return The UUID associated this remote descriptor. + */ +BLEUUID BLERemoteDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +std::string BLERemoteDescriptor::readValue(void) { + m_semaphoreReadDescrEvt.take("readValue"); + + // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. + esp_err_t errRc = ::esp_ble_gattc_read_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server + getHandle(), // The handle of this characteristic + ESP_GATT_AUTH_REQ_NONE); // Security + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return ""; + } + + // Block waiting for the event that indicates that the read has completed. When it has, the std::string found + // in m_value will contain our data. + m_semaphoreReadDescrEvt.wait("readValue"); + + ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); + return m_value; + return ""; +} // readValue + + +uint8_t BLERemoteDescriptor::readUInt8(void) { + std::string value = readValue(); + if (value.length() >= 1) { + return (uint8_t)value[0]; + } + return 0; +} // readUInt8 + + +uint16_t BLERemoteDescriptor::readUInt16(void) { + std::string value = readValue(); + if (value.length() >= 2) { + return *(uint16_t*)(value.data()); + } + return 0; +} // readUInt16 + + +uint32_t BLERemoteDescriptor::readUInt32(void) { + std::string value = readValue(); + if (value.length() >= 4) { + return *(uint32_t*)(value.data()); + } + return 0; +} // readUInt32 + + +/** + * @brief Return a string representation of this BLE Remote Descriptor. + * @retun A string representation of this BLE Remote Descriptor. + */ +std::string BLERemoteDescriptor::toString(void) { + std::stringstream ss; + ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); + return ss.str(); +} // toString + + +/** + * @brief Write data to the BLE Remote Descriptor. + * @param [in] data The data to send to the remote descriptor. + * @param [in] length The length of the data to send. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + uint8_t* data, + size_t length, + bool response) { + ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); + esp_err_t errRc = ::esp_ble_gattc_write_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), + getHandle(), + length, // Data length + data, // Data + ESP_GATT_WRITE_TYPE_NO_RSP, + ESP_GATT_AUTH_REQ_NONE + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc); + } + ESP_LOGD(LOG_TAG, "<< writeValue"); +} // writeValue + + +/** + * @brief Write data represented as a string to the BLE Remote Descriptor. + * @param [in] newValue The data to send to the remote descriptor. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + std::string newValue, + bool response) { + writeValue(newValue.data(), newValue.length()); +} // writeValue + + +/** + * @brief Write a byte value to the Descriptor. + * @param [in] The single byte to write. + * @param [in] True if we expect a response. + */ +void BLERemoteDescriptor::writeValue( + uint8_t newValue, + bool response) { + writeValue(&newValue, 1, response); +} // writeValue + #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLERemoteDescriptor.h b/src/BLERemoteDescriptor.h index a8d944d..c2cf383 100644 --- a/src/BLERemoteDescriptor.h +++ b/src/BLERemoteDescriptor.h @@ -9,12 +9,47 @@ #define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include <string> +#include <esp_gattc_api.h> + +#include "BLERemoteCharacteristic.h" +#include "BLEUUID.h" +#include "FreeRTOS.h" + +class BLERemoteCharacteristic; /** * @brief A model of remote %BLE descriptor. */ class BLERemoteDescriptor { public: + uint16_t getHandle(); + BLEUUID getUUID(); + std::string readValue(void); + uint8_t readUInt8(void); + uint16_t readUInt16(void); + uint32_t readUInt32(void); + std::string toString(void); + //void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)); + void writeValue(uint8_t* data, size_t length, bool response = false); + void writeValue(std::string newValue, bool response = false); + void writeValue(uint8_t newValue, bool response = false); + + +private: + friend class BLERemoteCharacteristic; + BLERemoteDescriptor( + uint16_t handle, + BLEUUID uuid, + BLERemoteCharacteristic* pRemoteCharacteristic + ); + uint16_t m_handle; // Server handle of this descriptor. + BLEUUID m_uuid; // UUID of this descriptor. + std::string m_value; // Last received value of the descriptor. + BLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated. + FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt"); + + }; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */ diff --git a/src/BLERemoteService.cpp b/src/BLERemoteService.cpp index 4d18625..c312e94 100644 --- a/src/BLERemoteService.cpp +++ b/src/BLERemoteService.cpp @@ -17,13 +17,21 @@ static const char* LOG_TAG = "BLERemoteService"; BLERemoteService::BLERemoteService( - esp_gatt_srvc_id_t srvcId, - BLEClient *pClient) { + esp_gatt_id_t srvcId, + BLEClient* pClient, + uint16_t startHandle, + uint16_t endHandle + ) { + ESP_LOGD(LOG_TAG, ">> BLERemoteService()"); m_srvcId = srvcId; m_pClient = pClient; m_uuid = BLEUUID(m_srvcId); m_haveCharacteristics = false; + m_startHandle = startHandle; + m_endHandle = endHandle; + + ESP_LOGD(LOG_TAG, "<< BLERemoteService()"); } @@ -31,6 +39,7 @@ BLERemoteService::~BLERemoteService() { removeCharacteristics(); } +/* static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { if (id1.id.inst_id != id2.id.inst_id) { return false; @@ -40,7 +49,7 @@ static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { } return true; } // compareSrvcId - +*/ /** * @brief Handle GATT Client events @@ -60,6 +69,7 @@ void BLERemoteService::gattClientEventHandler( // - esp_gatt_id_t char_id // - esp_gatt_char_prop_t char_prop // + /* case ESP_GATTC_GET_CHAR_EVT: { // Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be // the same. @@ -94,7 +104,7 @@ void BLERemoteService::gattClientEventHandler( //m_semaphoreGetCharEvt.give(); break; } // ESP_GATTC_GET_CHAR_EVT - +*/ default: { break; } @@ -108,13 +118,13 @@ void BLERemoteService::gattClientEventHandler( /** - * @brief Get the characteristic object for the UUID. - * @param [in] uuid Characteristic uuid. - * @return Reference to the characteristic object. + * @brief Get the remote characteristic object for the characteristic UUID. + * @param [in] uuid Remote characteristic uuid. + * @return Reference to the remote characteristic object. */ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { return getCharacteristic(BLEUUID(uuid)); -} +} // getCharacteristic /** @@ -130,7 +140,7 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { // asked the device about its characteristics, then we do that now. Once we get the results we can then // examine the characteristics map to see if it has the characteristic we are looking for. if (!m_haveCharacteristics) { - getCharacteristics(); + retrieveCharacteristics(); } std::string v = uuid.toString(); for (auto &myPair : m_characteristicMap) { @@ -144,14 +154,15 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { /** * @brief Retrieve all the characteristics for this service. + * This function will not return until we have all the characteristics. * @return N/A */ -void BLERemoteService::getCharacteristics() { +void BLERemoteService::retrieveCharacteristics() { ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); removeCharacteristics(); // Forget any previous characteristics. - + /* m_semaphoreGetCharEvt.take("getCharacteristics"); esp_err_t errRc = ::esp_ble_gattc_get_characteristic( @@ -168,18 +179,135 @@ void BLERemoteService::getCharacteristics() { m_semaphoreGetCharEvt.wait("getCharacteristics"); // Wait for the characteristics to become available. m_haveCharacteristics = true; // Remember that we have received the characteristics. + */ + //ESP_LOGE(LOG_TAG, "!!! NOT IMPLEMENTED !!!"); + //ESP_LOGD(LOG_TAG, "--- test code ---"); + /* + uint16_t count; + esp_gatt_status_t status = ::esp_ble_gattc_get_attr_count( + getClient()->getGattcIf(), + getClient()->getConnId(), + ESP_GATT_DB_CHARACTERISTIC, + m_startHandle, + m_endHandle, + 0, // Characteristic handle ... only used for ESP_GATT_DB_DESCRIPTOR + &count + ); + if (status != ESP_GATT_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_attr_count: %s", BLEUtils::gattStatusToString(status).c_str()); + } else { + ESP_LOGD(LOG_TAG, "Number of characteristics associated with service is %d", count); + } + + count = 1; + esp_gattc_service_elem_t srvcElem; + status = ::esp_ble_gattc_get_service( + getClient()->getGattcIf(), + getClient()->getConnId(), + &m_srvcId.uuid, // UUID of service + &srvcElem, // Records + &count, // records retrieved + 0 // offset + ); + if (status != ESP_GATT_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_service: %s", BLEUtils::gattStatusToString(status).c_str()); + } + else { + ESP_LOGD(LOG_TAG, "%s", BLEUtils::gattcServiceElementToString(&srvcElem).c_str()); + } + */ + + uint16_t offset = 0; + esp_gattc_char_elem_t result; + while(1) { + uint16_t count = 1; + esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( + getClient()->getGattcIf(), + getClient()->getConnId(), + m_startHandle, + m_endHandle, + &result, + &count, + offset + ); + + if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + break; + } + + if (status != ESP_GATT_OK) { // If we got an error, end. + ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str()); + break; + } + + if (count == 0) { // If we failed to get any new records, end. + break; + } + + ESP_LOGD(LOG_TAG, "Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str()); + + // We now have a new characteristic ... let us add that to our set of known characteristics + BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic( + result.char_handle, + BLEUUID(result.uuid), + result.properties, + this + ); + + m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); + + offset++; // Increment our count of number of descriptors found. + } // Loop forever (until we break inside the loop). + + m_haveCharacteristics = true; // Remember that we have received the characteristics. ESP_LOGD(LOG_TAG, "<< getCharacteristics()"); } // getCharacteristics +/** + * @brief Retrieve a map of all the characteristics of this service. + * @return A map of all the characteristics of this service. + */ +std::map<std::string, BLERemoteCharacteristic*>* BLERemoteService::getCharacteristics() { + ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); + // If is possible that we have not read the characteristics associated with the service so do that + // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking + // call and does not return until all the characteristics are available. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } + ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str()); + return &m_characteristicMap; +} // getCharacteristics + + BLEClient* BLERemoteService::getClient() { return m_pClient; } -esp_gatt_srvc_id_t* BLERemoteService::getSrvcId() { + +uint16_t BLERemoteService::getEndHandle() { + return m_endHandle; +} + + +esp_gatt_id_t* BLERemoteService::getSrvcId() { return &m_srvcId; } + +uint16_t BLERemoteService::getStartHandle() { + return m_startHandle; +} + +uint16_t BLERemoteService::getHandle() { + ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str()); + //ESP_LOGE(LOG_TAG, "!!! getHandle: NOT IMPLEMENTED !!!"); + ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle()); + return getStartHandle(); +} + + BLEUUID BLERemoteService::getUUID() { return m_uuid; } @@ -195,8 +323,9 @@ BLEUUID BLERemoteService::getUUID() { void BLERemoteService::removeCharacteristics() { for (auto &myPair : m_characteristicMap) { delete myPair.second; + m_characteristicMap.erase(myPair.first); } - m_characteristicMap.clear(); + m_characteristicMap.clear(); // Clear the map } // removeCharacteristics @@ -208,6 +337,8 @@ void BLERemoteService::removeCharacteristics() { std::string BLERemoteService::toString() { std::ostringstream ss; ss << "Service: uuid: " + m_uuid.toString(); + ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle << + ", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle; for (auto &myPair : m_characteristicMap) { ss << "\n" << myPair.second->toString(); // myPair.second is the value @@ -217,4 +348,6 @@ std::string BLERemoteService::toString() { + + #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLERemoteService.h b/src/BLERemoteService.h index 4393fbc..2007fa2 100644 --- a/src/BLERemoteService.h +++ b/src/BLERemoteService.h @@ -26,24 +26,32 @@ class BLERemoteCharacteristic; */ class BLERemoteService { public: - BLERemoteService(esp_gatt_srvc_id_t srvcId, BLEClient* pClient); + virtual ~BLERemoteService(); // Public methods BLERemoteCharacteristic* getCharacteristic(const char* uuid); BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); - void getCharacteristics(void); + std::map<std::string, BLERemoteCharacteristic*>* getCharacteristics(); + BLEClient* getClient(void); + uint16_t getHandle(); BLEUUID getUUID(void); std::string toString(void); private: + // Private constructor ... never meant to be created by a user application. + BLERemoteService(esp_gatt_id_t srvcId, BLEClient* pClient, uint16_t startHandle, uint16_t endHandle); + // Friends friend class BLEClient; friend class BLERemoteCharacteristic; // Private methods - esp_gatt_srvc_id_t* getSrvcId(void); + void retrieveCharacteristics(void); + esp_gatt_id_t* getSrvcId(void); + uint16_t getStartHandle(); + uint16_t getEndHandle(); void gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, @@ -51,12 +59,17 @@ private: void removeCharacteristics(); // Properties + + // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. std::map<std::string, BLERemoteCharacteristic *> m_characteristicMap; + bool m_haveCharacteristics; // Have we previously obtained the characteristics. BLEClient* m_pClient; FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt"); - esp_gatt_srvc_id_t m_srvcId; + esp_gatt_id_t m_srvcId; BLEUUID m_uuid; + uint16_t m_startHandle; + uint16_t m_endHandle; }; // BLERemoteService #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLEScan.cpp b/src/BLEScan.cpp index 54fcac0..925c09d 100644 --- a/src/BLEScan.cpp +++ b/src/BLEScan.cpp @@ -21,32 +21,20 @@ static const char* LOG_TAG = "BLEScan"; +/** + * Constructor + */ BLEScan::BLEScan() { m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; + m_pAdvertisedDeviceCallbacks = nullptr; + m_stopped = true; setInterval(100); setWindow(100); - m_pAdvertisedDeviceCallbacks = nullptr; - m_stopped = true; } // BLEScan - -/** - * @brief Clear the history of previously detected advertised devices. - * @return N/A - */ -/* -void BLEScan::clearAdvertisedDevices() { - for (int i=0; i<m_vectorAvdertisedDevices.size(); i++) { - delete m_vectorAvdertisedDevices[i]; - } - m_vectorAvdertisedDevices.clear(); -} // clearAdvertisedDevices -*/ - - /** * @brief Handle GAP events related to scans. * @param [in] event The event type for this event. @@ -90,14 +78,7 @@ void BLEScan::gapEventHandler( // ignore it. BLEAddress advertisedAddress(param->scan_rst.bda); bool found = false; - /* - for (int i=0; i<m_vectorAvdertisedDevices.size(); i++) { - if (m_vectorAvdertisedDevices[i]->getAddress().equals(advertisedAddress)) { - found = true; - break; - } - } - */ + for (int i=0; i<m_scanResults.getCount(); i++) { if (m_scanResults.getDevice(i).getAddress().equals(advertisedAddress)) { found = true; @@ -118,7 +99,6 @@ void BLEScan::gapEventHandler( advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); advertisedDevice.setScan(this); - //m_vectorAvdertisedDevices.push_back(pAdvertisedDevice); if (m_pAdvertisedDeviceCallbacks) { m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); } @@ -144,14 +124,6 @@ void BLEScan::gapEventHandler( } // gapEventHandler -/* -void BLEScan::onResults() { - ESP_LOGD(LOG_TAG, ">> onResults: default"); - ESP_LOGD(LOG_TAG, "<< onResults"); -} // onResults -*/ - - /** * @brief Should we perform an active or passive scan? * The default is a passive scan. An active scan means that we will wish a scan response. @@ -200,7 +172,7 @@ void BLEScan::setWindow(uint16_t windowMSecs) { * @return N/A. */ BLEScanResults BLEScan::start(uint32_t duration) { - ESP_LOGD(LOG_TAG, ">> start(%d)", duration); + ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); m_semaphoreScanEnd.take("start"); diff --git a/src/BLEScan.h b/src/BLEScan.h index f9575ea..bc7f431 100644 --- a/src/BLEScan.h +++ b/src/BLEScan.h @@ -21,10 +21,19 @@ class BLEAdvertisedDeviceCallbacks; class BLEClient; class BLEScan; + +/** + * @brief The result of having performed a scan. + * When a scan completes, we have a set of found devices. Each device is described + * by a BLEAdvertisedDevice object. The number of items in the set is given by + * getCount(). We can retrieve a device by calling getDevice() passing in the + * index (starting at 0) of the desired device. + */ class BLEScanResults { public: - int getCount(); + int getCount(); BLEAdvertisedDevice getDevice(uint32_t i); + private: friend BLEScan; std::vector<BLEAdvertisedDevice> m_vectorAdvertisedDevices; @@ -39,7 +48,6 @@ class BLEScan { public: BLEScan(); - //virtual void onResults(); void setActiveScan(bool active); void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks); void setInterval(uint16_t intervalMSecs); @@ -58,9 +66,8 @@ private: esp_ble_scan_params_t m_scan_params; BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; bool m_stopped; - FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); - //std::vector<BLEAdvertisedDevice*> m_vectorAvdertisedDevices; - BLEScanResults m_scanResults; + FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + BLEScanResults m_scanResults; }; // BLEScan #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLEServer.cpp b/src/BLEServer.cpp index be9773d..4a8dfd5 100644 --- a/src/BLEServer.cpp +++ b/src/BLEServer.cpp @@ -35,17 +35,16 @@ BLEServer::BLEServer() { m_gatts_if = -1; m_connectedCount = 0; m_connId = -1; - BLEDevice::m_bleServer = this; m_pServerCallbacks = nullptr; - createApp(0); + //createApp(0); } // BLEServer void BLEServer::createApp(uint16_t appId) { m_appId = appId; registerApp(); -} +} // createApp /** @@ -67,9 +66,10 @@ BLEService* BLEServer::createService(const char* uuid) { * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition * of a new service. Every service must have a unique UUID. * @param [in] uuid The UUID of the new service. + * @param [in] numHandles The maximum number of handles associated with this service. * @return A reference to the new service object. */ -BLEService* BLEServer::createService(BLEUUID uuid) { +BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) { ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); m_semaphoreCreateEvt.take("createService"); @@ -81,7 +81,7 @@ BLEService* BLEServer::createService(BLEUUID uuid) { return nullptr; } - BLEService* pService = new BLEService(uuid); + BLEService* pService = new BLEService(uuid, numHandles); m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. pService->executeCreate(this); // Perform the API calls to actually create the service. @@ -169,7 +169,7 @@ void BLEServer::handleGATTServerEvent( esp_ble_gatts_cb_param_t* param) { ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", - BLEUtils::gattServerEventTypeToString(event).c_str()); + BLEUtils::gattServerEventTypeToString(event).c_str()); // Invoke the handler for every Service we have. m_serviceMap.handleGATTServerEvent(event, gatts_if, param); @@ -199,7 +199,7 @@ void BLEServer::handleGATTServerEvent( case ESP_GATTS_REG_EVT: { m_gatts_if = gatts_if; - m_semaphoreRegisterAppEvt.give(); + m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. break; } // ESP_GATTS_REG_EVT @@ -350,4 +350,14 @@ void BLEServer::addCharacteristic(BLECharacteristic *characteristic, BLEService } */ +void BLEServerCallbacks::onConnect(BLEServer* pServer) { + ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect + +void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { + ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); +} // onDisconnect + #endif // CONFIG_BT_ENABLED diff --git a/src/BLEServer.h b/src/BLEServer.h index c6307bc..64b34b4 100644 --- a/src/BLEServer.h +++ b/src/BLEServer.h @@ -51,18 +51,16 @@ private: */ class BLEServer { public: - BLEServer(); - - uint32_t getConnectedCount(); BLEService* createService(const char* uuid); - BLEService* createService(BLEUUID uuid); + BLEService* createService(BLEUUID uuid, uint32_t numHandles=15); BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks *pCallbacks); void startAdvertising(); private: + BLEServer(); friend class BLEService; friend class BLECharacteristic; friend class BLEDevice; diff --git a/src/BLEService.cpp b/src/BLEService.cpp index ec16db8..9e7fa28 100644 --- a/src/BLEService.cpp +++ b/src/BLEService.cpp @@ -29,21 +29,24 @@ static const char* LOG_TAG = "BLEService"; // Tag for logging. /** * @brief Construct an instance of the BLEService * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(const char* uuid) : BLEService(BLEUUID(uuid)) { +BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { } /** * @brief Construct an instance of the BLEService * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(BLEUUID uuid) { - m_uuid = uuid; - m_handle = NULL_HANDLE; - m_pServer = nullptr; +BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pServer = nullptr; //m_serializeMutex.setName("BLEService"); m_lastCreatedCharacteristic = nullptr; + m_numHandles = numHandles; } // BLEService @@ -54,16 +57,19 @@ BLEService::BLEService(BLEUUID uuid) { * @return N/A. */ void BLEService::executeCreate(BLEServer *pServer) { - ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); - + //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); + getUUID(); // Needed for a weird bug fix m_pServer = pServer; + m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + esp_gatt_srvc_id_t srvc_id; srvc_id.id.inst_id = 0; srvc_id.id.uuid = *m_uuid.getNative(); - - m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT - - esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, 10); + esp_err_t errRc = ::esp_ble_gatts_create_service( + getServer()->getGattsIf(), + &srvc_id, + m_numHandles // The maximum number of handles associated with the service. + ); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -71,7 +77,6 @@ void BLEService::executeCreate(BLEServer *pServer) { } m_semaphoreCreateEvt.wait("executeCreate"); - ESP_LOGD(LOG_TAG, "<< executeCreate"); } // executeCreate @@ -87,18 +92,6 @@ void BLEService::dump() { ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); } // dump -/* -void BLEService::setService(esp_gatt_srvc_id_t srvc_id) { - m_srvc_id = srvc_id; -} -*/ - -/* -esp_gatt_srvc_id_t BLEService::getService() { - return m_srvc_id; -} -*/ - /** * @brief Get the UUID of the service. @@ -184,7 +177,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). // ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); - ESP_LOGD(LOG_TAG, "Adding characteristic (esp_ble_gatts_add_char): uuid=%s to service: %s", + ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), toString().c_str()); @@ -212,6 +205,7 @@ BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t p return createCharacteristic(BLEUUID(uuid), properties); } + /** * @brief Create a new BLE Characteristic associated with this service. * @param [in] uuid - The UUID of the characteristic. @@ -235,7 +229,7 @@ void BLEService::handleGATTServerEvent( switch(event) { - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. // add_char: // - esp_gatt_status_t status // - uint16_t attr_handle @@ -251,18 +245,17 @@ void BLEService::handleGATTServerEvent( ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", BLEUUID(param->add_char.char_uuid).toString().c_str()); dump(); - m_semaphoreAddCharEvt.give(); break; } pCharacteristic->setHandle(param->add_char.attr_handle); m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); - m_semaphoreAddCharEvt.give(); break; } // Reached the correct service. break; } // ESP_GATTS_ADD_CHAR_EVT + // ESP_GATTS_START_EVT // // start: @@ -301,6 +294,7 @@ void BLEService::handleGATTServerEvent( } // Default } // Switch + // Invoke the GATTS handler in each of the associated characteristics. m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); } // handleGATTServerEvent diff --git a/src/BLEService.h b/src/BLEService.h index 86d0776..9a93aff 100644 --- a/src/BLEService.h +++ b/src/BLEService.h @@ -52,8 +52,8 @@ private: */ class BLEService { public: - BLEService(const char* uuid); - BLEService(BLEUUID uuid); + BLEService(const char* uuid, uint32_t numHandles=10); + BLEService(BLEUUID uuid, uint32_t numHandles=10); void addCharacteristic(BLECharacteristic* pCharacteristic); BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); @@ -79,10 +79,10 @@ private: BLECharacteristic* m_lastCreatedCharacteristic; BLEServer* m_pServer; //FreeRTOS::Semaphore m_serializeMutex; - FreeRTOS::Semaphore m_semaphoreAddCharEvt = FreeRTOS::Semaphore("AddCharEvt"); - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); BLEUUID m_uuid; + uint32_t m_numHandles; uint16_t getHandle(); BLECharacteristic* getLastCreatedCharacteristic(); diff --git a/src/BLEUUID.cpp b/src/BLEUUID.cpp index 9a4fe45..19da518 100644 --- a/src/BLEUUID.cpp +++ b/src/BLEUUID.cpp @@ -35,6 +35,7 @@ static const char* LOG_TAG = "BLEUUID"; * @param [in] size The number of bytes to copy */ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { + assert(size > 0); target+=(size-1); // Point target to the last byte of the target data while (size > 0) { *target = *source; @@ -65,15 +66,18 @@ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { BLEUUID::BLEUUID(std::string value) { m_valueSet = true; if (value.length() == 2) { - m_uuid.len = ESP_UUID_LEN_16; + m_uuid.len = ESP_UUID_LEN_16; m_uuid.uuid.uuid16 = value[0] | (value[1] << 8); - } else if (value.length() == 4) { - m_uuid.len = ESP_UUID_LEN_32; + } + else if (value.length() == 4) { + m_uuid.len = ESP_UUID_LEN_32; m_uuid.uuid.uuid32 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); - } else if (value.length() == 16) { + } + else if (value.length() == 16) { m_uuid.len = ESP_UUID_LEN_128; memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); - } else if (value.length() == 36) { + } + else if (value.length() == 36) { // If the length of the string is 36 bytes then we will assume it is a long hex string in // UUID format. m_uuid.len = ESP_UUID_LEN_128; @@ -130,6 +134,7 @@ BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) { m_valueSet = true; } // BLEUUID + /** * @brief Create a UUID from the 16bit value. * @@ -166,11 +171,11 @@ BLEUUID::BLEUUID(esp_bt_uuid_t uuid) { /** - * @brief Create a UUID from the ESP32 esp_gatt_srvc_id_t. + * @brief Create a UUID from the ESP32 esp_gat_id_t. * - * @param [in] srvcId The data to create the UUID from. + * @param [in] gattId The data to create the UUID from. */ -BLEUUID::BLEUUID(esp_gatt_srvc_id_t srcvId) : BLEUUID(srcvId.id.uuid) { +BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) { } // BLEUUID @@ -277,50 +282,67 @@ BLEUUID BLEUUID::to128() { } // to128 -//01234567 8901 2345 6789 012345678901 -//0000180d-0000-1000-8000-00805f9b34fb -//0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + /** * @brief Get a string representation of the UUID. * + * The format of a string is: + * 01234567 8901 2345 6789 012345678901 + * 0000180d-0000-1000-8000-00805f9b34fb + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * * @return A string representation of the UUID. */ std::string BLEUUID::toString() { - if (m_valueSet == false) { + if (m_valueSet == false) { // If we have no value, nothing to format. return "<NULL>"; } - if (m_uuid.len == ESP_UUID_LEN_16) { - std::stringstream ss; - ss << "0000" << std::hex << std::setfill('0') << std::setw(4) << m_uuid.uuid.uuid16 << "-0000-1000-8000-00805f9b34fb"; - return ss.str(); - } - - if (m_uuid.len == ESP_UUID_LEN_32) { - std::stringstream ss; - ss << std::hex << std::setfill('0') << std::setw(8) << m_uuid.uuid.uuid32 << "-0000-1000-8000-00805f9b34fb"; - return ss.str(); - } - + // If the UUIDs are 16 or 32 bit, pad correctly. std::stringstream ss; + + if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly. + ss << "0000" << + std::hex << + std::setfill('0') << + std::setw(4) << + m_uuid.uuid.uuid16 << + "-0000-1000-8000-00805f9b34fb"; + return ss.str(); // Return the string + } // End 16bit UUID + + if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly. + ss << std::hex << + std::setfill('0') << + std::setw(8) << + m_uuid.uuid.uuid32 << + "-0000-1000-8000-00805f9b34fb"; + return ss.str(); // return the string + } // End 32bit UUID + + // The UUID is not 16bit or 32bit which means that it is 128bit. + // + // UUID string format: + // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP + // ss << std::hex << std::setfill('0') << - std::setw(2) << (int)m_uuid.uuid.uuid128[15] << - std::setw(2) << (int)m_uuid.uuid.uuid128[14] << - std::setw(2) << (int)m_uuid.uuid.uuid128[13] << - std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[11] << - std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[9] << - std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[7] << - std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[5] << - std::setw(2) << (int)m_uuid.uuid.uuid128[4] << - std::setw(2) << (int)m_uuid.uuid.uuid128[3] << - std::setw(2) << (int)m_uuid.uuid.uuid128[2] << - std::setw(2) << (int)m_uuid.uuid.uuid128[1] << - std::setw(2) << (int)m_uuid.uuid.uuid128[0]; + std::setw(2) << (int)m_uuid.uuid.uuid128[15] << + std::setw(2) << (int)m_uuid.uuid.uuid128[14] << + std::setw(2) << (int)m_uuid.uuid.uuid128[13] << + std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[11] << + std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[9] << + std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[7] << + std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" << + std::setw(2) << (int)m_uuid.uuid.uuid128[5] << + std::setw(2) << (int)m_uuid.uuid.uuid128[4] << + std::setw(2) << (int)m_uuid.uuid.uuid128[3] << + std::setw(2) << (int)m_uuid.uuid.uuid128[2] << + std::setw(2) << (int)m_uuid.uuid.uuid128[1] << + std::setw(2) << (int)m_uuid.uuid.uuid128[0]; return ss.str(); } // toString #endif /* CONFIG_BT_ENABLED */ diff --git a/src/BLEUUID.h b/src/BLEUUID.h index c364796..a11220b 100644 --- a/src/BLEUUID.h +++ b/src/BLEUUID.h @@ -22,7 +22,7 @@ public: BLEUUID(uint32_t uuid); BLEUUID(esp_bt_uuid_t uuid); BLEUUID(uint8_t* pData, size_t size, bool msbFirst); - BLEUUID(esp_gatt_srvc_id_t srcvId); + BLEUUID(esp_gatt_id_t gattId); BLEUUID(); bool equals(BLEUUID uuid); esp_bt_uuid_t* getNative(); @@ -30,8 +30,8 @@ public: std::string toString(); private: - esp_bt_uuid_t m_uuid; - bool m_valueSet; + esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. + bool m_valueSet; // Is there a value set for this instance. }; // BLEUUID #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ diff --git a/src/BLEUtils.cpp b/src/BLEUtils.cpp index 468a7e1..a91bbcc 100644 --- a/src/BLEUtils.cpp +++ b/src/BLEUtils.cpp @@ -6,10 +6,10 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "BLEAddress.h" +#include "BLEClient.h" #include "BLEUtils.h" #include "BLEUUID.h" -#include "BLEClient.h" -#include "BLEAddress.h" #include "GeneralUtils.h" #include <freertos/FreeRTOS.h> @@ -24,7 +24,7 @@ #include <sstream> #include <iomanip> -static const char* LOG_TAG = "BLEUtils"; +static const char* LOG_TAG = "BLEUtils"; // Tag for logging. /* static std::map<std::string, BLEClient *> g_addressMap; @@ -32,13 +32,509 @@ static std::map<uint16_t, BLEClient *> g_connIdMap; */ typedef struct { - uint32_t assignedNumber; + uint32_t assignedNumber; + std::string name; +} member_t; + +static const member_t members_ids[] = { + {0xFE08, "Microsoft"}, + {0xFE09, "Pillsy, Inc."}, + {0xFE0A, "ruwido austria gmbh"}, + {0xFE0B, "ruwido austria gmbh"}, + {0xFE0C, "Procter & Gamble"}, + {0xFE0D, "Procter & Gamble"}, + {0xFE0E, "Setec Pty Ltd"}, + {0xFE0F, "Philips Lighting B.V."}, + {0xFE10, "Lapis Semiconductor Co., Ltd."}, + {0xFE11, "GMC-I Messtechnik GmbH"}, + {0xFE12, "M-Way Solutions GmbH"}, + {0xFE13, "Apple Inc."}, + {0xFE14, "Flextronics International USA Inc."}, + {0xFE15, "Amazon Fulfillment Services, Inc."}, + {0xFE16, "Footmarks, Inc."}, + {0xFE17, "Telit Wireless Solutions GmbH"}, + {0xFE18, "Runtime, Inc."}, + {0xFE19, "Google Inc."}, + {0xFE1A, "Tyto Life LLC"}, + {0xFE1B, "Tyto Life LLC"}, + {0xFE1C, "NetMedia, Inc."}, + {0xFE1D, "Illuminati Instrument Corporation"}, + {0xFE1E, "Smart Innovations Co., Ltd"}, + {0xFE1F, "Garmin International, Inc."}, + {0xFE20, "Emerson"}, + {0xFE21, "Bose Corporation"}, + {0xFE22, "Zoll Medical Corporation"}, + {0xFE23, "Zoll Medical Corporation"}, + {0xFE24, "August Home Inc"}, + {0xFE25, "Apple, Inc. "}, + {0xFE26, "Google Inc."}, + {0xFE27, "Google Inc."}, + {0xFE28, "Ayla Networks"}, + {0xFE29, "Gibson Innovations"}, + {0xFE2A, "DaisyWorks, Inc."}, + {0xFE2B, "ITT Industries"}, + {0xFE2C, "Google Inc."}, + {0xFE2D, "SMART INNOVATION Co.,Ltd"}, + {0xFE2E, "ERi,Inc."}, + {0xFE2F, "CRESCO Wireless, Inc"}, + {0xFE30, "Volkswagen AG"}, + {0xFE31, "Volkswagen AG"}, + {0xFE32, "Pro-Mark, Inc."}, + {0xFE33, "CHIPOLO d.o.o."}, + {0xFE34, "SmallLoop LLC"}, + {0xFE35, "HUAWEI Technologies Co., Ltd"}, + {0xFE36, "HUAWEI Technologies Co., Ltd"}, + {0xFE37, "Spaceek LTD"}, + {0xFE38, "Spaceek LTD"}, + {0xFE39, "TTS Tooltechnic Systems AG & Co. KG"}, + {0xFE3A, "TTS Tooltechnic Systems AG & Co. KG"}, + {0xFE3B, "Dolby Laboratories"}, + {0xFE3C, "Alibaba"}, + {0xFE3D, "BD Medical"}, + {0xFE3E, "BD Medical"}, + {0xFE3F, "Friday Labs Limited"}, + {0xFE40, "Inugo Systems Limited"}, + {0xFE41, "Inugo Systems Limited"}, + {0xFE42, "Nets A/S "}, + {0xFE43, "Andreas Stihl AG & Co. KG"}, + {0xFE44, "SK Telecom "}, + {0xFE45, "Snapchat Inc"}, + {0xFE46, "B&O Play A/S "}, + {0xFE47, "General Motors"}, + {0xFE48, "General Motors"}, + {0xFE49, "SenionLab AB"}, + {0xFE4A, "OMRON HEALTHCARE Co., Ltd."}, + {0xFE4B, "Philips Lighting B.V."}, + {0xFE4C, "Volkswagen AG"}, + {0xFE4D, "Casambi Technologies Oy"}, + {0xFE4E, "NTT docomo"}, + {0xFE4F, "Molekule, Inc."}, + {0xFE50, "Google Inc."}, + {0xFE51, "SRAM"}, + {0xFE52, "SetPoint Medical"}, + {0xFE53, "3M"}, + {0xFE54, "Motiv, Inc."}, + {0xFE55, "Google Inc."}, + {0xFE56, "Google Inc."}, + {0xFE57, "Dotted Labs"}, + {0xFE58, "Nordic Semiconductor ASA"}, + {0xFE59, "Nordic Semiconductor ASA"}, + {0xFE5A, "Chronologics Corporation"}, + {0xFE5B, "GT-tronics HK Ltd"}, + {0xFE5C, "million hunters GmbH"}, + {0xFE5D, "Grundfos A/S"}, + {0xFE5E, "Plastc Corporation"}, + {0xFE5F, "Eyefi, Inc."}, + {0xFE60, "Lierda Science & Technology Group Co., Ltd."}, + {0xFE61, "Logitech International SA"}, + {0xFE62, "Indagem Tech LLC"}, + {0xFE63, "Connected Yard, Inc."}, + {0xFE64, "Siemens AG"}, + {0xFE65, "CHIPOLO d.o.o."}, + {0xFE66, "Intel Corporation"}, + {0xFE67, "Lab Sensor Solutions"}, + {0xFE68, "Qualcomm Life Inc"}, + {0xFE69, "Qualcomm Life Inc"}, + {0xFE6A, "Kontakt Micro-Location Sp. z o.o."}, + {0xFE6B, "TASER International, Inc."}, + {0xFE6C, "TASER International, Inc."}, + {0xFE6D, "The University of Tokyo"}, + {0xFE6E, "The University of Tokyo"}, + {0xFE6F, "LINE Corporation"}, + {0xFE70, "Beijing Jingdong Century Trading Co., Ltd."}, + {0xFE71, "Plume Design Inc"}, + {0xFE72, "St. Jude Medical, Inc."}, + {0xFE73, "St. Jude Medical, Inc."}, + {0xFE74, "unwire"}, + {0xFE75, "TangoMe"}, + {0xFE76, "TangoMe"}, + {0xFE77, "Hewlett-Packard Company"}, + {0xFE78, "Hewlett-Packard Company"}, + {0xFE79, "Zebra Technologies"}, + {0xFE7A, "Bragi GmbH"}, + {0xFE7B, "Orion Labs, Inc."}, + {0xFE7C, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"}, + {0xFE7D, "Aterica Health Inc."}, + {0xFE7E, "Awear Solutions Ltd"}, + {0xFE7F, "Doppler Lab"}, + {0xFE80, "Doppler Lab"}, + {0xFE81, "Medtronic Inc."}, + {0xFE82, "Medtronic Inc."}, + {0xFE83, "Blue Bite"}, + {0xFE84, "RF Digital Corp"}, + {0xFE85, "RF Digital Corp"}, + {0xFE86, "HUAWEI Technologies Co., Ltd. ( )"}, + {0xFE87, "Qingdao Yeelink Information Technology Co., Ltd. ( )"}, + {0xFE88, "SALTO SYSTEMS S.L."}, + {0xFE89, "B&O Play A/S"}, + {0xFE8A, "Apple, Inc."}, + {0xFE8B, "Apple, Inc."}, + {0xFE8C, "TRON Forum"}, + {0xFE8D, "Interaxon Inc."}, + {0xFE8E, "ARM Ltd"}, + {0xFE8F, "CSR"}, + {0xFE90, "JUMA"}, + {0xFE91, "Shanghai Imilab Technology Co.,Ltd"}, + {0xFE92, "Jarden Safety & Security"}, + {0xFE93, "OttoQ Inc."}, + {0xFE94, "OttoQ Inc."}, + {0xFE95, "Xiaomi Inc."}, + {0xFE96, "Tesla Motor Inc."}, + {0xFE97, "Tesla Motor Inc."}, + {0xFE98, "Currant, Inc."}, + {0xFE99, "Currant, Inc."}, + {0xFE9A, "Estimote"}, + {0xFE9B, "Samsara Networks, Inc"}, + {0xFE9C, "GSI Laboratories, Inc."}, + {0xFE9D, "Mobiquity Networks Inc"}, + {0xFE9E, "Dialog Semiconductor B.V."}, + {0xFE9F, "Google Inc."}, + {0xFEA0, "Google Inc."}, + {0xFEA1, "Intrepid Control Systems, Inc."}, + {0xFEA2, "Intrepid Control Systems, Inc."}, + {0xFEA3, "ITT Industries"}, + {0xFEA4, "Paxton Access Ltd"}, + {0xFEA5, "GoPro, Inc."}, + {0xFEA6, "GoPro, Inc."}, + {0xFEA7, "UTC Fire and Security"}, + {0xFEA8, "Savant Systems LLC"}, + {0xFEA9, "Savant Systems LLC"}, + {0xFEAA, "Google Inc."}, + {0xFEAB, "Nokia Corporation"}, + {0xFEAC, "Nokia Corporation"}, + {0xFEAD, "Nokia Corporation"}, + {0xFEAE, "Nokia Corporation"}, + {0xFEAF, "Nest Labs Inc."}, + {0xFEB0, "Nest Labs Inc."}, + {0xFEB1, "Electronics Tomorrow Limited"}, + {0xFEB2, "Microsoft Corporation"}, + {0xFEB3, "Taobao"}, + {0xFEB4, "WiSilica Inc."}, + {0xFEB5, "WiSilica Inc."}, + {0xFEB6, "Vencer Co, Ltd"}, + {0xFEB7, "Facebook, Inc."}, + {0xFEB8, "Facebook, Inc."}, + {0xFEB9, "LG Electronics"}, + {0xFEBA, "Tencent Holdings Limited"}, + {0xFEBB, "adafruit industries"}, + {0xFEBC, "Dexcom, Inc. "}, + {0xFEBD, "Clover Network, Inc."}, + {0xFEBE, "Bose Corporation"}, + {0xFEBF, "Nod, Inc."}, + {0xFEC0, "KDDI Corporation"}, + {0xFEC1, "KDDI Corporation"}, + {0xFEC2, "Blue Spark Technologies, Inc."}, + {0xFEC3, "360fly, Inc."}, + {0xFEC4, "PLUS Location Systems"}, + {0xFEC5, "Realtek Semiconductor Corp."}, + {0xFEC6, "Kocomojo, LLC"}, + {0xFEC7, "Apple, Inc."}, + {0xFEC8, "Apple, Inc."}, + {0xFEC9, "Apple, Inc."}, + {0xFECA, "Apple, Inc."}, + {0xFECB, "Apple, Inc."}, + {0xFECC, "Apple, Inc."}, + {0xFECD, "Apple, Inc."}, + {0xFECE, "Apple, Inc."}, + {0xFECF, "Apple, Inc."}, + {0xFED0, "Apple, Inc."}, + {0xFED1, "Apple, Inc."}, + {0xFED2, "Apple, Inc."}, + {0xFED3, "Apple, Inc."}, + {0xFED4, "Apple, Inc."}, + {0xFED5, "Plantronics Inc."}, + {0xFED6, "Broadcom Corporation"}, + {0xFED7, "Broadcom Corporation"}, + {0xFED8, "Google Inc."}, + {0xFED9, "Pebble Technology Corporation"}, + {0xFEDA, "ISSC Technologies Corporation"}, + {0xFEDB, "Perka, Inc."}, + {0xFEDC, "Jawbone"}, + {0xFEDD, "Jawbone"}, + {0xFEDE, "Coin, Inc."}, + {0xFEDF, "Design SHIFT"}, + {0xFEE0, "Anhui Huami Information Technology Co."}, + {0xFEE1, "Anhui Huami Information Technology Co."}, + {0xFEE2, "Anki, Inc."}, + {0xFEE3, "Anki, Inc."}, + {0xFEE4, "Nordic Semiconductor ASA"}, + {0xFEE5, "Nordic Semiconductor ASA"}, + {0xFEE6, "Silvair, Inc."}, + {0xFEE7, "Tencent Holdings Limited"}, + {0xFEE8, "Quintic Corp."}, + {0xFEE9, "Quintic Corp."}, + {0xFEEA, "Swirl Networks, Inc."}, + {0xFEEB, "Swirl Networks, Inc."}, + {0xFEEC, "Tile, Inc."}, + {0xFEED, "Tile, Inc."}, + {0xFEEE, "Polar Electro Oy"}, + {0xFEEF, "Polar Electro Oy"}, + {0xFEF0, "Intel"}, + {0xFEF1, "CSR"}, + {0xFEF2, "CSR"}, + {0xFEF3, "Google Inc."}, + {0xFEF4, "Google Inc."}, + {0xFEF5, "Dialog Semiconductor GmbH"}, + {0xFEF6, "Wicentric, Inc."}, + {0xFEF7, "Aplix Corporation"}, + {0xFEF8, "Aplix Corporation"}, + {0xFEF9, "PayPal, Inc."}, + {0xFEFA, "PayPal, Inc."}, + {0xFEFB, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"}, + {0xFEFC, "Gimbal, Inc."}, + {0xFEFD, "Gimbal, Inc."}, + {0xFEFE, "GN ReSound A/S"}, + {0xFEFF, "GN Netcom"}, + {0xFFFF, "Reserved"}, /*for testing purposes only*/ + {0, "" } +}; + +typedef struct { + uint32_t assignedNumber; + std::string name; +} gattdescriptor_t; + +static const gattdescriptor_t g_descriptor_ids[] = { + {0x2905,"Characteristic Aggregate Format"}, + {0x2900,"Characteristic Extended Properties"}, + {0x2904,"Characteristic Presentation Format"}, + {0x2901,"Characteristic User Description"}, + {0x2902,"Client Characteristic Configuration"}, + {0x290B,"Environmental Sensing Configuration"}, + {0x290C,"Environmental Sensing Measurement"}, + {0x290D,"Environmental Sensing Trigger Setting"}, + {0x2907,"External Report Reference"}, + {0x2909,"Number of Digitals"}, + {0x2908,"Report Reference"}, + {0x2903,"Server Characteristic Configuration"}, + {0x290E,"Time Trigger Setting"}, + {0x2906,"Valid Range"}, + {0x290A,"Value Trigger Setting"}, + { 0, "" } +}; + +typedef struct { + uint32_t assignedNumber; std::string name; } characteristicMap_t; -static characteristicMap_t g_characteristicsMappings[] = { - {0x2a00, "Device Name"}, - {0x2a01, "Appearance"}, +static const characteristicMap_t g_characteristicsMappings[] = { + {0x2A7E,"Aerobic Heart Rate Lower Limit"}, + {0x2A84,"Aerobic Heart Rate Upper Limit"}, + {0x2A7F,"Aerobic Threshold"}, + {0x2A80,"Age"}, + {0x2A5A,"Aggregate"}, + {0x2A43,"Alert Category ID"}, + {0x2A42,"Alert Category ID Bit Mask"}, + {0x2A06,"Alert Level"}, + {0x2A44,"Alert Notification Control Point"}, + {0x2A3F,"Alert Status"}, + {0x2AB3,"Altitude"}, + {0x2A81,"Anaerobic Heart Rate Lower Limit"}, + {0x2A82,"Anaerobic Heart Rate Upper Limit"}, + {0x2A83,"Anaerobic Threshold"}, + {0x2A58,"Analog"}, + {0x2A59,"Analog Output"}, + {0x2A73,"Apparent Wind Direction"}, + {0x2A72,"Apparent Wind Speed"}, + {0x2A01,"Appearance"}, + {0x2AA3,"Barometric Pressure Trend"}, + {0x2A19,"Battery Level"}, + {0x2A1B,"Battery Level State"}, + {0x2A1A,"Battery Power State"}, + {0x2A49,"Blood Pressure Feature"}, + {0x2A35,"Blood Pressure Measurement"}, + {0x2A9B,"Body Composition Feature"}, + {0x2A9C,"Body Composition Measurement"}, + {0x2A38,"Body Sensor Location"}, + {0x2AA4,"Bond Management Control Point"}, + {0x2AA5,"Bond Management Features"}, + {0x2A22,"Boot Keyboard Input Report"}, + {0x2A32,"Boot Keyboard Output Report"}, + {0x2A33,"Boot Mouse Input Report"}, + {0x2AA6,"Central Address Resolution"}, + {0x2AA8,"CGM Feature"}, + {0x2AA7,"CGM Measurement"}, + {0x2AAB,"CGM Session Run Time"}, + {0x2AAA,"CGM Session Start Time"}, + {0x2AAC,"CGM Specific Ops Control Point"}, + {0x2AA9,"CGM Status"}, + {0x2ACE,"Cross Trainer Data"}, + {0x2A5C,"CSC Feature"}, + {0x2A5B,"CSC Measurement"}, + {0x2A2B,"Current Time"}, + {0x2A66,"Cycling Power Control Point"}, + {0x2A66,"Cycling Power Control Point"}, + {0x2A65,"Cycling Power Feature"}, + {0x2A65,"Cycling Power Feature"}, + {0x2A63,"Cycling Power Measurement"}, + {0x2A64,"Cycling Power Vector"}, + {0x2A99,"Database Change Increment"}, + {0x2A85,"Date of Birth"}, + {0x2A86,"Date of Threshold Assessment"}, + {0x2A08,"Date Time"}, + {0x2A0A,"Day Date Time"}, + {0x2A09,"Day of Week"}, + {0x2A7D,"Descriptor Value Changed"}, + {0x2A00,"Device Name"}, + {0x2A7B,"Dew Point"}, + {0x2A56,"Digital"}, + {0x2A57,"Digital Output"}, + {0x2A0D,"DST Offset"}, + {0x2A6C,"Elevation"}, + {0x2A87,"Email Address"}, + {0x2A0B,"Exact Time 100"}, + {0x2A0C,"Exact Time 256"}, + {0x2A88,"Fat Burn Heart Rate Lower Limit"}, + {0x2A89,"Fat Burn Heart Rate Upper Limit"}, + {0x2A26,"Firmware Revision String"}, + {0x2A8A,"First Name"}, + {0x2AD9,"Fitness Machine Control Point"}, + {0x2ACC,"Fitness Machine Feature"}, + {0x2ADA,"Fitness Machine Status"}, + {0x2A8B,"Five Zone Heart Rate Limits"}, + {0x2AB2,"Floor Number"}, + {0x2A8C,"Gender"}, + {0x2A51,"Glucose Feature"}, + {0x2A18,"Glucose Measurement"}, + {0x2A34,"Glucose Measurement Context"}, + {0x2A74,"Gust Factor"}, + {0x2A27,"Hardware Revision String"}, + {0x2A39,"Heart Rate Control Point"}, + {0x2A8D,"Heart Rate Max"}, + {0x2A37,"Heart Rate Measurement"}, + {0x2A7A,"Heat Index"}, + {0x2A8E,"Height"}, + {0x2A4C,"HID Control Point"}, + {0x2A4A,"HID Information"}, + {0x2A8F,"Hip Circumference"}, + {0x2ABA,"HTTP Control Point"}, + {0x2AB9,"HTTP Entity Body"}, + {0x2AB7,"HTTP Headers"}, + {0x2AB8,"HTTP Status Code"}, + {0x2ABB,"HTTPS Security"}, + {0x2A6F,"Humidity"}, + {0x2A2A,"IEEE 11073-20601 Regulatory Certification Data List"}, + {0x2AD2,"Indoor Bike Data"}, + {0x2AAD,"Indoor Positioning Configuration"}, + {0x2A36,"Intermediate Cuff Pressure"}, + {0x2A1E,"Intermediate Temperature"}, + {0x2A77,"Irradiance"}, + {0x2AA2,"Language"}, + {0x2A90,"Last Name"}, + {0x2AAE,"Latitude"}, + {0x2A6B,"LN Control Point"}, + {0x2A6A,"LN Feature"}, + {0x2AB1,"Local East Coordinate"}, + {0x2AB0,"Local North Coordinate"}, + {0x2A0F,"Local Time Information"}, + {0x2A67,"Location and Speed Characteristic"}, + {0x2AB5,"Location Name"}, + {0x2AAF,"Longitude"}, + {0x2A2C,"Magnetic Declination"}, + {0x2AA0,"Magnetic Flux Density - 2D"}, + {0x2AA1,"Magnetic Flux Density - 3D"}, + {0x2A29,"Manufacturer Name String"}, + {0x2A91,"Maximum Recommended Heart Rate"}, + {0x2A21,"Measurement Interval"}, + {0x2A24,"Model Number String"}, + {0x2A68,"Navigation"}, + {0x2A3E,"Network Availability"}, + {0x2A46,"New Alert"}, + {0x2AC5,"Object Action Control Point"}, + {0x2AC8,"Object Changed"}, + {0x2AC1,"Object First-Created"}, + {0x2AC3,"Object ID"}, + {0x2AC2,"Object Last-Modified"}, + {0x2AC6,"Object List Control Point"}, + {0x2AC7,"Object List Filter"}, + {0x2ABE,"Object Name"}, + {0x2AC4,"Object Properties"}, + {0x2AC0,"Object Size"}, + {0x2ABF,"Object Type"}, + {0x2ABD,"OTS Feature"}, + {0x2A04,"Peripheral Preferred Connection Parameters"}, + {0x2A02,"Peripheral Privacy Flag"}, + {0x2A5F,"PLX Continuous Measurement Characteristic"}, + {0x2A60,"PLX Features"}, + {0x2A5E,"PLX Spot-Check Measurement"}, + {0x2A50,"PnP ID"}, + {0x2A75,"Pollen Concentration"}, + {0x2A2F,"Position 2D"}, + {0x2A30,"Position 3D"}, + {0x2A69,"Position Quality"}, + {0x2A6D,"Pressure"}, + {0x2A4E,"Protocol Mode"}, + {0x2A62,"Pulse Oximetry Control Point"}, + {0x2A60,"Pulse Oximetry Pulsatile Event Characteristic"}, + {0x2A78,"Rainfall"}, + {0x2A03,"Reconnection Address"}, + {0x2A52,"Record Access Control Point"}, + {0x2A14,"Reference Time Information"}, + {0x2A3A,"Removable"}, + {0x2A4D,"Report"}, + {0x2A4B,"Report Map"}, + {0x2AC9,"Resolvable Private Address Only"}, + {0x2A92,"Resting Heart Rate"}, + {0x2A40,"Ringer Control point"}, + {0x2A41,"Ringer Setting"}, + {0x2AD1,"Rower Data"}, + {0x2A54,"RSC Feature"}, + {0x2A53,"RSC Measurement"}, + {0x2A55,"SC Control Point"}, + {0x2A4F,"Scan Interval Window"}, + {0x2A31,"Scan Refresh"}, + {0x2A3C,"Scientific Temperature Celsius"}, + {0x2A10,"Secondary Time Zone"}, + {0x2A5D,"Sensor Location"}, + {0x2A25,"Serial Number String"}, + {0x2A05,"Service Changed"}, + {0x2A3B,"Service Required"}, + {0x2A28,"Software Revision String"}, + {0x2A93,"Sport Type for Aerobic and Anaerobic Thresholds"}, + {0x2AD0,"Stair Climber Data"}, + {0x2ACF,"Step Climber Data"}, + {0x2A3D,"String"}, + {0x2AD7,"Supported Heart Rate Range"}, + {0x2AD5,"Supported Inclination Range"}, + {0x2A47,"Supported New Alert Category"}, + {0x2AD8,"Supported Power Range"}, + {0x2AD6,"Supported Resistance Level Range"}, + {0x2AD4,"Supported Speed Range"}, + {0x2A48,"Supported Unread Alert Category"}, + {0x2A23,"System ID"}, + {0x2ABC,"TDS Control Point"}, + {0x2A6E,"Temperature"}, + {0x2A1F,"Temperature Celsius"}, + {0x2A20,"Temperature Fahrenheit"}, + {0x2A1C,"Temperature Measurement"}, + {0x2A1D,"Temperature Type"}, + {0x2A94,"Three Zone Heart Rate Limits"}, + {0x2A12,"Time Accuracy"}, + {0x2A15,"Time Broadcast"}, + {0x2A13,"Time Source"}, + {0x2A16,"Time Update Control Point"}, + {0x2A17,"Time Update State"}, + {0x2A11,"Time with DST"}, + {0x2A0E,"Time Zone"}, + {0x2AD3,"Training Status"}, + {0x2ACD,"Treadmill Data"}, + {0x2A71,"True Wind Direction"}, + {0x2A70,"True Wind Speed"}, + {0x2A95,"Two Zone Heart Rate Limit"}, + {0x2A07,"Tx Power Level"}, + {0x2AB4,"Uncertainty"}, + {0x2A45,"Unread Alert Status"}, + {0x2AB6,"URI"}, + {0x2A9F,"User Control Point"}, + {0x2A9A,"User Index"}, + {0x2A76,"UV Index"}, + {0x2A96,"VO2 Max"}, + {0x2A97,"Waist Circumference"}, + {0x2A98,"Weight"}, + {0x2A9D,"Weight Measurement"}, + {0x2A9E,"Weight Scale Feature"}, + {0x2A79,"Wind Chill"}, {0, ""} }; @@ -48,7 +544,7 @@ static characteristicMap_t g_characteristicsMappings[] = { typedef struct { std::string name; std::string type; - uint32_t assignedNumber; + uint32_t assignedNumber; } gattService_t; @@ -94,6 +590,7 @@ static const gattService_t g_gattServices[] = { {"", "", 0 } }; + /** * @brief Convert characteristic properties into a string representation. * @param [in] prop Characteristic properties. @@ -137,7 +634,7 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { case BLE_ADDR_TYPE_RPA_RANDOM: return "BLE_ADDR_TYPE_RPA_RANDOM"; default: - return "Unknown addr_t"; + return "Unknown esp_ble_addr_type_t"; } } // addressTypeToString @@ -232,13 +729,12 @@ esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, * @param [in] length The length of the data to convert. * @return A pointer to the formatted buffer. */ -char *BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) { +char* BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) { // Guard against too much data. if (length > 100) { length = 100; } - if (target == nullptr) { target = (uint8_t *)malloc(length * 2 + 1); if (target == nullptr) { @@ -349,12 +845,12 @@ std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType return "ESP_GATTC_ENC_CMPL_CB_EVT"; case ESP_GATTC_EXEC_EVT: return "ESP_GATTC_EXEC_EVT"; - case ESP_GATTC_GET_CHAR_EVT: - return "ESP_GATTC_GET_CHAR_EVT"; - case ESP_GATTC_GET_DESCR_EVT: - return "ESP_GATTC_GET_DESCR_EVT"; - case ESP_GATTC_GET_INCL_SRVC_EVT: - return "ESP_GATTC_GET_INCL_SRVC_EVT"; + //case ESP_GATTC_GET_CHAR_EVT: +// return "ESP_GATTC_GET_CHAR_EVT"; + //case ESP_GATTC_GET_DESCR_EVT: +// return "ESP_GATTC_GET_DESCR_EVT"; + //case ESP_GATTC_GET_INCL_SRVC_EVT: +// return "ESP_GATTC_GET_INCL_SRVC_EVT"; case ESP_GATTC_MULT_ADV_DATA_EVT: return "ESP_GATTC_MULT_ADV_DATA_EVT"; case ESP_GATTC_MULT_ADV_DIS_EVT: @@ -487,24 +983,41 @@ const char* BLEUtils::devTypeToString(esp_bt_dev_type_t type) { * @brief Dump the GAP event to the log. */ void BLEUtils::dumpGapEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t *param) { + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { ESP_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event)); switch(event) { // // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT + // adv_data_cmpl + // - esp_bt_status_t // case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); + ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_cmpl.status); break; } // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT // + // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT + // + // adv_data_raw_cmpl + // - esp_bt_status_t status + // + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { + ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status); + break; + } // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT + + + // // ESP_GAP_BLE_ADV_START_COMPLETE_EVT // + // adv_start_cmpl + // - esp_bt_status_t status + // case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); + ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); break; } // ESP_GAP_BLE_ADV_START_COMPLETE_EVT @@ -512,8 +1025,11 @@ void BLEUtils::dumpGapEvent( // // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT // + // adv_stop_cmpl + // - esp_bt_status_t status + // case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); + ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); break; } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT @@ -521,6 +1037,15 @@ void BLEUtils::dumpGapEvent( // // ESP_GAP_BLE_AUTH_CMPL_EVT // + // auth_cmpl + // - esp_bd_addr_t bd_addr + // - bool key_present + // - esp_link_key key + // - bool success + // - uint8_t fail_reason + // - esp_bd_addr_type_t addr_type + // - esp_bt_dev_type_t dev_type + // case ESP_GAP_BLE_AUTH_CMPL_EVT: { ESP_LOGD(LOG_TAG, "[bd_addr: %s, key_present: %d, key: ***, key_type: %d, success: %d, fail_reason: %d, addr_type: ***, dev_type: %s]", BLEAddress(param->ble_security.auth_cmpl.bd_addr).toString().c_str(), @@ -535,6 +1060,18 @@ void BLEUtils::dumpGapEvent( // + // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT + // + // clear_bond_dev_cmpl + // - esp_bt_status_t status + // + case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: { + ESP_LOGD(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); + break; + } // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT + + + // // ESP_GAP_BLE_LOCAL_IR_EVT // case ESP_GAP_BLE_LOCAL_IR_EVT: { @@ -562,13 +1099,35 @@ void BLEUtils::dumpGapEvent( // + // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + // + // read_rssi_cmpl + // - esp_bt_status_t status + // - int8_t rssi + // - esp_bd_addr_t remote_addr + // + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: { + ESP_LOGD(LOG_TAG, "[status: %d, rssi: %d, remote_addr: %s]", + param->read_rssi_cmpl.status, + param->read_rssi_cmpl.rssi, + BLEAddress(param->read_rssi_cmpl.remote_addr).toString().c_str() + ); + break; + } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + + + // // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT // + // scan_param_cmpl. + // - esp_bt_status_t status + // case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); break; } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT + // // ESP_GAP_BLE_SCAN_RESULT_EVT // @@ -582,6 +1141,7 @@ void BLEUtils::dumpGapEvent( // - ble_adv // - flag // - num_resps + // case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch(param->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: { @@ -610,31 +1170,75 @@ void BLEUtils::dumpGapEvent( // - // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT + // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT // - case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); + // scan_rsp_data_cmpl + // - esp_bt_status_t status + // + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { + ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); break; - } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT + } // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT // - // ESP_GAP_BLE_SEC_REQ_EVT + // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT // - case ESP_GAP_BLE_SEC_REQ_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); + // scan_start_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: { + ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); break; - } // ESP_GAP_BLE_SEC_REQ_EVT + } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT // // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT // + // scan_stop_cmpl + // - esp_bt_status_t status + // case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: { ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); break; } // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT + + // + // ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT + // + // update_conn_params + // - esp_bt_status_t status + // - esp_bd_addr_t bda + // - uint16_t min_int + // - uint16_t max_int + // - uint16_t latency + // - uint16_t conn_int + // - uint16_t timeout + // + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { + ESP_LOGD(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", + param->update_conn_params.status, + BLEAddress(param->update_conn_params.bda).toString().c_str(), + param->update_conn_params.min_int, + param->update_conn_params.max_int, + param->update_conn_params.latency, + param->update_conn_params.conn_int, + param->update_conn_params.timeout + ); + break; + } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT + + + // + // ESP_GAP_BLE_SEC_REQ_EVT + // + case ESP_GAP_BLE_SEC_REQ_EVT: { + ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); + break; + } // ESP_GAP_BLE_SEC_REQ_EVT + + default: { ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; @@ -650,9 +1254,9 @@ void BLEUtils::dumpGapEvent( * @param [in] evtParam The data associated with the event. */ void BLEUtils::dumpGattClientEvent( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *evtParam) { + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* evtParam) { //esp_ble_gattc_cb_param_t *evtParam = (esp_ble_gattc_cb_param_t *)param; ESP_LOGD(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); @@ -716,6 +1320,7 @@ void BLEUtils::dumpGattClientEvent( // - esp_gatt_id_t char_id // - esp_gatt_char_prop_t char_prop // + /* case ESP_GATTC_GET_CHAR_EVT: { // If the status of the event shows that we have a value other than ESP_GATT_OK then the @@ -742,6 +1347,7 @@ void BLEUtils::dumpGattClientEvent( } break; } // ESP_GATTC_GET_CHAR_EVT + */ // // ESP_GATTC_NOTIFY_EVT @@ -749,20 +1355,17 @@ void BLEUtils::dumpGattClientEvent( // notify // uint16_t conn_id // esp_bd_addr_t remote_bda - // esp_gatt_srvc_id_t srvc_id - // esp_gatt_id_t char_id - // esp_gatt_id_t descr_id + // handle handle // uint16_t value_len // uint8_t* value // bool is_notify // case ESP_GATTC_NOTIFY_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s, srvc_id: <%s>, char_id: <%s>, descr_id: <%s>, value_len: %d, is_notify: %d]", + ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s, handle: %d 0x%.2x, value_len: %d, is_notify: %d]", evtParam->notify.conn_id, BLEAddress(evtParam->notify.remote_bda).toString().c_str(), - BLEUtils::gattServiceIdToString(evtParam->notify.srvc_id).c_str(), - gattIdToString(evtParam->notify.char_id).c_str(), - gattIdToString(evtParam->notify.descr_id).c_str(), + evtParam->notify.handle, + evtParam->notify.handle, evtParam->notify.value_len, evtParam->notify.is_notify ); @@ -796,20 +1399,16 @@ void BLEUtils::dumpGattClientEvent( // read: // esp_gatt_status_t status // uint16_t conn_id - // esp_gatt_srvc_id_t srvc_id - // esp_gatt_id_t char_id - // esp_gatt_id_t descr_id + // uint16_t handle // uint8_t* value // uint16_t value_type // uint16_t value_len case ESP_GATTC_READ_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: <%s>, char_id: <%s>, descr_id: <%s>, value_type: 0x%x, value_len: %d]", + ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, value_len: %d]", BLEUtils::gattStatusToString(evtParam->read.status).c_str(), evtParam->read.conn_id, - BLEUtils::gattServiceIdToString(evtParam->read.srvc_id).c_str(), - gattIdToString(evtParam->read.char_id).c_str(), - gattIdToString(evtParam->read.descr_id).c_str(), - evtParam->read.value_type, + evtParam->read.handle, + evtParam->read.handle, evtParam->read.value_len ); if (evtParam->read.status == ESP_GATT_OK) { @@ -844,13 +1443,13 @@ void BLEUtils::dumpGattClientEvent( // // reg_for_notify: // - esp_gatt_status_t status - // - esp_gatt_srvc_id_t srvc_id - // - esp_gatt_id_t char_id + // - uint16_t handle case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, srvc_id: <%s>, char_id: <%s>]", + ESP_LOGD(LOG_TAG, "[status: %s, handle: %d 0x%.2x]", BLEUtils::gattStatusToString(evtParam->reg_for_notify.status).c_str(), - BLEUtils::gattServiceIdToString(evtParam->reg_for_notify.srvc_id).c_str(), - gattIdToString(evtParam->reg_for_notify.char_id).c_str()); + evtParam->reg_for_notify.handle, + evtParam->reg_for_notify.handle + ); break; } // ESP_GATTC_REG_FOR_NOTIFY_EVT @@ -874,23 +1473,19 @@ void BLEUtils::dumpGattClientEvent( // ESP_GATTC_SEARCH_RES_EVT // // search_res: - // - uint16_t conn_id - // - esp_gatt_srvc_id_t srvc_id + // - uint16_t conn_id + // - uint16_t start_handle + // - uint16_t end_handle + // - esp_gatt_id_t srvc_id // case ESP_GATTC_SEARCH_RES_EVT: { - std::string name = ""; - if (evtParam->search_res.srvc_id.id.uuid.len == ESP_UUID_LEN_16) { - name = BLEUtils::gattServiceToString(evtParam->search_res.srvc_id.id.uuid.uuid.uuid16); - } - if (name.length() == 0) { - name = "<Unknown Service>"; - } - - ESP_LOGD(LOG_TAG, "[srvc_id: %s [%s], instanceId: 0x%.2x conn_id: %d]", - BLEUtils::gattServiceIdToString(evtParam->search_res.srvc_id).c_str(), - name.c_str(), - evtParam->search_res.srvc_id.id.inst_id, - evtParam->search_res.conn_id); + ESP_LOGD(LOG_TAG, "[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s", + evtParam->search_res.conn_id, + evtParam->search_res.start_handle, + evtParam->search_res.start_handle, + evtParam->search_res.end_handle, + evtParam->search_res.end_handle, + gattIdToString(evtParam->search_res.srvc_id).c_str()); break; } // ESP_GATTC_SEARCH_RES_EVT @@ -899,21 +1494,19 @@ void BLEUtils::dumpGattClientEvent( // ESP_GATTC_WRITE_CHAR_EVT // // write: - // esp_gatt_status_t status - // uint16_t conn_id - // esp_gatt_srvc_id_t srvc_id - // esp_gatt_id_t char_id - // esp_gatt_id_t descr_id + // - esp_gatt_status_t status + // - uint16_t conn_id + // - uint16_t handle + // case ESP_GATTC_WRITE_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: <%s>, char_id: <%s>, descr_id: <%s>]", + ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x]", BLEUtils::gattStatusToString(evtParam->write.status).c_str(), evtParam->write.conn_id, - BLEUtils::gattServiceIdToString(evtParam->write.srvc_id).c_str(), - gattIdToString(evtParam->write.char_id).c_str(), - gattIdToString(evtParam->write.descr_id).c_str() + evtParam->write.handle, + evtParam->write.handle ); break; - } + } // ESP_GATTC_WRITE_CHAR_EVT default: break; @@ -932,27 +1525,41 @@ void BLEUtils::dumpGattClientEvent( * @param [in] evtParam A union of structures only one of which is populated. */ void BLEUtils::dumpGattServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *evtParam) { + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* evtParam) { ESP_LOGD(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); switch(event) { case ESP_GATTS_ADD_CHAR_DESCR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: 0x%.2x, service_handle: 0x%.2x, char_uuid: %s]", + ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", gattStatusToString(evtParam->add_char_descr.status).c_str(), evtParam->add_char_descr.attr_handle, + evtParam->add_char_descr.attr_handle, + evtParam->add_char_descr.service_handle, evtParam->add_char_descr.service_handle, BLEUUID(evtParam->add_char_descr.char_uuid).toString().c_str()); break; } // ESP_GATTS_ADD_CHAR_DESCR_EVT case ESP_GATTS_ADD_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: 0x%.2x, service_handle: 0x%.2x, char_uuid: %s]", - gattStatusToString(evtParam->add_char.status).c_str(), - evtParam->add_char.attr_handle, - evtParam->add_char.service_handle, - BLEUUID(evtParam->add_char.char_uuid).toString().c_str()); + if (evtParam->add_char.status == ESP_GATT_OK) { + ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + gattStatusToString(evtParam->add_char.status).c_str(), + evtParam->add_char.attr_handle, + evtParam->add_char.attr_handle, + evtParam->add_char.service_handle, + evtParam->add_char.service_handle, + BLEUUID(evtParam->add_char.char_uuid).toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + gattStatusToString(evtParam->add_char.status).c_str(), + evtParam->add_char.attr_handle, + evtParam->add_char.attr_handle, + evtParam->add_char.service_handle, + evtParam->add_char.service_handle, + BLEUUID(evtParam->add_char.char_uuid).toString().c_str()); + } break; } // ESP_GATTS_ADD_CHAR_EVT @@ -987,9 +1594,10 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_CONNECT_EVT case ESP_GATTS_CREATE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, service_handle: 0x%.2x, service_id: [%s]]", + ESP_LOGD(LOG_TAG, "[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]", gattStatusToString(evtParam->create.status).c_str(), evtParam->create.service_handle, + evtParam->create.service_handle, gattServiceIdToString(evtParam->create.service_id).c_str()); break; } // ESP_GATTS_CREATE_EVT @@ -1108,7 +1716,7 @@ void BLEUtils::dumpGattServerEvent( evtParam->write.need_rsp, evtParam->write.is_prep, evtParam->write.len); - char *pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); + char* pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); ESP_LOGD(LOG_TAG, "[Data: %s]", pHex); free(pHex); break; @@ -1139,7 +1747,7 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { case ESP_BLE_EVT_SCAN_RSP: return "ESP_BLE_EVT_SCAN_RSP"; default: - ESP_LOGD(LOG_TAG, "Unknown esp_ble_evt_type_t: %d", eventType); + ESP_LOGD(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); return "*** Unknown ***"; } } // eventTypeToString @@ -1153,53 +1761,70 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { */ const char* BLEUtils::gapEventToString(uint32_t eventType) { switch(eventType) { - case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: - return "ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT"; case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT"; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_START_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - return "ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_RESULT_EVT: - return "ESP_GAP_BLE_SCAN_RESULT_EVT"; - case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: - return "ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: - return "ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: - return "ESP_GAP_BLE_SCAN_START_COMPLETE_EVT"; + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: /*!< When stop adv complete, the event comes */ + return "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT"; case ESP_GAP_BLE_AUTH_CMPL_EVT: /* Authentication complete indication. */ return "ESP_GAP_BLE_AUTH_CMPL_EVT"; + case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: + return "ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT"; + case ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT: + return "ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT"; case ESP_GAP_BLE_KEY_EVT: /* BLE key event for peer device keys */ return "ESP_GAP_BLE_KEY_EVT"; - case ESP_GAP_BLE_SEC_REQ_EVT: /* BLE security request */ - return "ESP_GAP_BLE_SEC_REQ_EVT"; - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: /* passkey notification event */ - return "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"; - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ - return "ESP_GAP_BLE_PASSKEY_REQ_EVT"; - case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ - return "ESP_GAP_BLE_OOB_REQ_EVT"; case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ return "ESP_GAP_BLE_LOCAL_IR_EVT"; case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ return "ESP_GAP_BLE_LOCAL_ER_EVT"; case ESP_GAP_BLE_NC_REQ_EVT: /* Numeric Comparison request event */ return "ESP_GAP_BLE_NC_REQ_EVT"; - case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: /*!< When stop adv complete, the event comes */ - return "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT"; + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + return "ESP_GAP_BLE_OOB_REQ_EVT"; + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: /* passkey notification event */ + return "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + return "ESP_GAP_BLE_PASSKEY_REQ_EVT"; + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: + return "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT"; + case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: + return "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_RESULT_EVT: + return "ESP_GAP_BLE_SCAN_RESULT_EVT"; + case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_START_COMPLETE_EVT"; case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT"; + case ESP_GAP_BLE_SEC_REQ_EVT: /* BLE security request */ + return "ESP_GAP_BLE_SEC_REQ_EVT"; + case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: + return "ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT"; + case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: + return "ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT"; + case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT: + return "ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT"; + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + return "ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT"; + default: - ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type 0x%x", eventType); + ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; } } // gapEventToString std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) { - characteristicMap_t *p = g_characteristicsMappings; + const characteristicMap_t *p = g_characteristicsMappings; while (p->name.length() > 0) { if (p->assignedNumber == characteristicUUID) { return p->name; @@ -1211,6 +1836,39 @@ std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID /** + * @brief Given the UUID for a BLE defined descriptor, return its string representation. + * @param [in] descriptorUUID UUID of the descriptor to be returned as a string. + * @return The string representation of a descriptor UUID. + */ +std::string BLEUtils::gattDescriptorUUIDToString(uint32_t descriptorUUID) { + gattdescriptor_t* p = (gattdescriptor_t *)g_descriptor_ids; + while (p->name.length() > 0) { + if (p->assignedNumber == descriptorUUID) { + return p->name; + } + p++; + } + return ""; +} // gattDescriptorUUIDToString + + +/** + * @brief Return a string representation of an esp_gattc_service_elem_t. + * @return A string representation of an esp_gattc_service_elem_t. + */ +std::string BLEUtils::gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement) { + std::stringstream ss; + + ss << "[uuid: " << BLEUUID(pGATTCServiceElement->uuid).toString() << + ", start_handle: " << pGATTCServiceElement->start_handle << + " 0x" << std::hex << pGATTCServiceElement->start_handle << + ", end_handle: " << std::dec << pGATTCServiceElement->end_handle << + " 0x" << std::hex << pGATTCServiceElement->end_handle << "]"; + return ss.str(); +} // gattcServiceElementToString + + +/** * @brief Convert an esp_gatt_srvc_id_t to a string. */ std::string BLEUtils::gattServiceIdToString(esp_gatt_srvc_id_t srvcId) { @@ -1312,16 +1970,37 @@ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { return "ESP_GATT_ALREADY_OPEN"; case ESP_GATT_CANCEL: return "ESP_GATT_CANCEL"; + case ESP_GATT_STACK_RSP: + return "ESP_GATT_STACK_RSP"; + case ESP_GATT_APP_RSP: + return "ESP_GATT_APP_RSP"; + case ESP_GATT_UNKNOWN_ERROR: + return "ESP_GATT_UNKNOWN_ERROR"; case ESP_GATT_CCC_CFG_ERR: return "ESP_GATT_CCC_CFG_ERR"; case ESP_GATT_PRC_IN_PROGRESS: return "ESP_GATT_PRC_IN_PROGRESS"; + case ESP_GATT_OUT_OF_RANGE: + return "ESP_GATT_OUT_OF_RANGE"; default: return "Unknown"; } } // gattStatusToString + +std::string BLEUtils::getMember(uint32_t memberId) { + member_t* p = (member_t *)members_ids; + + while (p->name.length() > 0) { + if (p->assignedNumber == memberId) { + return p->name; + } + p++; + } + return "Unknown"; +} + /** * @brief convert a GAP search event to a string. * @param [in] searchEvt diff --git a/src/BLEUtils.h b/src/BLEUtils.h index 28c7a7e..5ef6c37 100644 --- a/src/BLEUtils.h +++ b/src/BLEUtils.h @@ -20,40 +20,42 @@ */ class BLEUtils { public: - static const char* advTypeToString(uint8_t advType); - static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0); - + static const char* addressTypeToString(esp_ble_addr_type_t type); + static const char* advTypeToString(uint8_t advType); + static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0); static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary=true); - static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); - static char* buildHexData(uint8_t *target, uint8_t *source, uint8_t length); - static BLEClient* findByConnId(uint16_t conn_id); - static BLEClient* findByAddress(BLEAddress address); - static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); - static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); - static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); - static std::string gattStatusToString(esp_gatt_status_t status); - static std::string gattServiceToString(uint32_t serviceId); - static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); - static void registerByAddress(BLEAddress address, BLEClient* pDevice); - static void registerByConnId(uint16_t conn_id, BLEClient* pDevice); - static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); - static std::string buildPrintData(uint8_t* source, size_t length); + static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length); + static std::string buildPrintData(uint8_t* source, size_t length); + static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); + static const char* devTypeToString(esp_bt_dev_type_t type); + static void dumpGapEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); static void dumpGattClientEvent( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); static void dumpGattServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* evtParam); - static const char* devTypeToString(esp_bt_dev_type_t type); - static void dumpGapEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param); + static const char* eventTypeToString(esp_ble_evt_type_t eventType); + static BLEClient* findByAddress(BLEAddress address); + static BLEClient* findByConnId(uint16_t conn_id); static const char* gapEventToString(uint32_t eventType); + static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); + static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); + static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); + static std::string gattcServiceElementToString(esp_gattc_service_elem_t *pGATTCServiceElement); + static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID); + static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); + static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); + static std::string gattServiceToString(uint32_t serviceId); + static std::string gattStatusToString(esp_gatt_status_t status); + static std::string getMember(uint32_t memberId); + static void registerByAddress(BLEAddress address, BLEClient* pDevice); + static void registerByConnId(uint16_t conn_id, BLEClient* pDevice); static const char* searchEventTypeToString(esp_gap_search_evt_t searchEvt); - static const char* addressTypeToString(esp_ble_addr_type_t type); - static const char *eventTypeToString(esp_ble_evt_type_t eventType); }; #endif // CONFIG_BT_ENABLED diff --git a/src/BLEValue.cpp b/src/BLEValue.cpp index 1989993..d36d207 100644 --- a/src/BLEValue.cpp +++ b/src/BLEValue.cpp @@ -72,6 +72,24 @@ void BLEValue::commit() { /** + * @brief Get a pointer to the data. + * @return A pointer to the data. + */ +uint8_t* BLEValue::getData() { + return (uint8_t*)m_value.data(); +} + + +/** + * @brief Get the length of the data in bytes. + * @return The length of the data in bytes. + */ +size_t BLEValue::getLength() { + return m_value.length(); +} // getLength + + +/** * @brief Get the read offset. * @return The read offset into the read. */ @@ -113,4 +131,9 @@ void BLEValue::setValue(std::string value) { void BLEValue::setValue(uint8_t* pData, size_t length) { m_value = std::string((char*)pData, length); } // setValue + + + + + #endif // CONFIG_BT_ENABLED diff --git a/src/BLEValue.h b/src/BLEValue.h index a292c6e..92a7f9a 100644 --- a/src/BLEValue.h +++ b/src/BLEValue.h @@ -21,6 +21,8 @@ public: void addPart(uint8_t* pData, size_t length); void cancel(); void commit(); + uint8_t* getData(); + size_t getLength(); uint16_t getReadOffset(); std::string getValue(); void setReadOffset(uint16_t readOffset); diff --git a/src/FreeRTOS.cpp b/src/FreeRTOS.cpp index 663128d..dea73bf 100644 --- a/src/FreeRTOS.cpp +++ b/src/FreeRTOS.cpp @@ -133,11 +133,10 @@ void FreeRTOS::Semaphore::giveFromISR() { */ void FreeRTOS::Semaphore::take(std::string owner) { - - ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); xSemaphoreTake(m_semaphore, portMAX_DELAY); m_owner = owner; - ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } // Semaphore::take diff --git a/src/GeneralUtils.cpp b/src/GeneralUtils.cpp index 4237b14..6bfde84 100644 --- a/src/GeneralUtils.cpp +++ b/src/GeneralUtils.cpp @@ -7,6 +7,7 @@ #include "GeneralUtils.h" #include <esp_log.h> +#include <esp_system.h> #include <string.h> #include <stdio.h> #include <string> @@ -106,6 +107,23 @@ bool GeneralUtils::base64Encode(const std::string &in, std::string *out) { } // base64Encode +/** + * @brief Does the string end with a specific character? + * @param [in] str The string to examine. + * @param [in] c The character to look form. + * @return True if the string ends with the given character. + */ +bool GeneralUtils::endsWith(std::string str, char c) { + if (str.empty()) { + return false; + } + if (str.at(str.length()-1) == c) { + return true; + } + return false; +} // endsWidth + + static int DecodedLength(const std::string &in) { int numEq = 0; int n = in.size(); @@ -261,7 +279,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { * @param [in] length Length of the data (in bytes) to be logged. * @return N/A. */ -void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { +void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { char ascii[80]; char hex[80]; char tempBuf[80]; @@ -386,3 +404,9 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { return "Unknown ESP_ERR error"; } // errorToString +/** + * @brief Restart the ESP32. + */ +void GeneralUtils::restart() { + esp_restart(); +} // restart diff --git a/src/GeneralUtils.h b/src/GeneralUtils.h index 152abca..ce75e43 100644 --- a/src/GeneralUtils.h +++ b/src/GeneralUtils.h @@ -18,11 +18,13 @@ class GeneralUtils { public: GeneralUtils(); virtual ~GeneralUtils(); - static void hexDump(uint8_t *pData, uint32_t length); - static std::string ipToString(uint8_t *ip); static bool base64Encode(const std::string &in, std::string *out); static bool base64Decode(const std::string &in, std::string *out); + static bool endsWith(std::string str, char c); static const char *errorToString(esp_err_t errCode); + static void hexDump(const uint8_t *pData, uint32_t length); + static std::string ipToString(uint8_t *ip); + static void restart(); }; #endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */ |