From 5137fc5c80e8cd1d9f07879d0bc1ecf5a52e8ce4 Mon Sep 17 00:00:00 2001 From: Roman Savrulin Date: Tue, 20 Aug 2019 17:15:30 +0300 Subject: [PATCH] Ble notification/indication status and timeout (#2998) * add timed wait * Added Notification/Indication data and status callbacks * imply null-object pattern for BLE callback --- libraries/BLE/src/BLECharacteristic.cpp | 68 ++++++++++++++++++++----- libraries/BLE/src/BLECharacteristic.h | 15 ++++++ libraries/BLE/src/FreeRTOS.cpp | 32 ++++++++++++ libraries/BLE/src/FreeRTOS.h | 2 + 4 files changed, 105 insertions(+), 12 deletions(-) diff --git a/libraries/BLE/src/BLECharacteristic.cpp b/libraries/BLE/src/BLECharacteristic.cpp index 45d3c8e1b33..11636921b77 100644 --- a/libraries/BLE/src/BLECharacteristic.cpp +++ b/libraries/BLE/src/BLECharacteristic.cpp @@ -22,6 +22,7 @@ #define NULL_HANDLE (0xffff) +static BLECharacteristicCallbacks defaultCallback; //null-object-pattern /** * @brief Construct a characteristic @@ -40,7 +41,7 @@ BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { m_bleUUID = uuid; m_handle = NULL_HANDLE; m_properties = (esp_gatt_char_prop_t)0; - m_pCallbacks = nullptr; + m_pCallbacks = &defaultCallback; setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); setReadProperty((properties & PROPERTY_READ) != 0); @@ -220,9 +221,7 @@ void BLECharacteristic::handleGATTServerEvent( case ESP_GATTS_EXEC_WRITE_EVT: { if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { m_value.commit(); - if (m_pCallbacks != nullptr) { - m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. - } + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. } else { m_value.cancel(); } @@ -307,7 +306,7 @@ void BLECharacteristic::handleGATTServerEvent( } } // Response needed - if (m_pCallbacks != nullptr && param->write.is_prep != true) { + if (param->write.is_prep != true) { m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. } } // Match on handles. @@ -378,9 +377,9 @@ void BLECharacteristic::handleGATTServerEvent( } } else { // read.is_long == false - if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback - m_pCallbacks->onRead(this); // Invoke the read callback. - } + // If is.long is false then this is the first (or only) request to read data, so invoke the callback + // Invoke the read callback. + m_pCallbacks->onRead(this); std::string value = m_value.getValue(); @@ -480,10 +479,13 @@ void BLECharacteristic::notify(bool is_notification) { assert(getService() != nullptr); assert(getService()->getServer() != nullptr); + m_pCallbacks->onNotify(this); // Invoke the notify callback. + GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length()); if (getService()->getServer()->getConnectedCount() == 0) { log_v("<< notify: No connected clients."); + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NO_CLIENT, 0); return; } @@ -494,12 +496,14 @@ void BLECharacteristic::notify(bool is_notification) { if(is_notification) { if (p2902 != nullptr && !p2902->getNotifications()) { log_v("<< notifications disabled; ignoring"); + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NOTIFY_DISABLED, 0); // Invoke the notify callback. return; } } else{ if (p2902 != nullptr && !p2902->getIndications()) { log_v("<< indications disabled; ignoring"); + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED, 0); // Invoke the notify callback. return; } } @@ -510,7 +514,7 @@ void BLECharacteristic::notify(bool is_notification) { } size_t length = m_value.getValue().length(); - if(!is_notification) + if(!is_notification) // is indication m_semaphoreConfEvt.take("indicate"); esp_err_t errRc = ::esp_ble_gatts_send_indicate( getService()->getServer()->getGattsIf(), @@ -519,10 +523,23 @@ void BLECharacteristic::notify(bool is_notification) { if (errRc != ESP_OK) { log_e("<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc)); m_semaphoreConfEvt.give(); + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_GATT, errRc); // Invoke the notify callback. return; } - if(!is_notification) - m_semaphoreConfEvt.wait("indicate"); + if(!is_notification){ // is indication + if(!m_semaphoreConfEvt.timedWait("indicate", indicationTimeout)){ + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, 0); // Invoke the notify callback. + } else { + auto code = (esp_gatt_status_t) m_semaphoreConfEvt.value(); + if(code == ESP_GATT_OK) { + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_INDICATE, code); // Invoke the notify callback. + } else { + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, code); + } + } + } else { + m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); // Invoke the notify callback. + } } log_v("<< notify"); } // Notify @@ -551,7 +568,11 @@ void BLECharacteristic::setBroadcastProperty(bool value) { */ void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) { log_v(">> setCallbacks: 0x%x", (uint32_t)pCallbacks); - m_pCallbacks = pCallbacks; + if (pCallbacks != nullptr){ + m_pCallbacks = pCallbacks; + } else { + m_pCallbacks = &defaultCallback; + } log_v("<< setCallbacks"); } // setCallbacks @@ -754,4 +775,27 @@ void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) { log_d("BLECharacteristicCallbacks", "<< onWrite"); } // onWrite + +/** + * @brief Callback function to support a Notify request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void BLECharacteristicCallbacks::onNotify(BLECharacteristic* pCharacteristic) { + log_d("BLECharacteristicCallbacks", ">> onNotify: default"); + log_d("BLECharacteristicCallbacks", "<< onNotify"); +} // onNotify + + +/** + * @brief Callback function to support a Notify/Indicate Status report. + * @param [in] pCharacteristic The characteristic that is the source of the event. + * @param [in] s Status of the notification/indication + * @param [in] code Additional code of underlying errors + */ +void BLECharacteristicCallbacks::onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code) { + log_d("BLECharacteristicCallbacks", ">> onStatus: default"); + log_d("BLECharacteristicCallbacks", "<< onStatus"); +} // onStatus + + #endif /* CONFIG_BT_ENABLED */ diff --git a/libraries/BLE/src/BLECharacteristic.h b/libraries/BLE/src/BLECharacteristic.h index dd65cc4b8c4..adec9587ee0 100644 --- a/libraries/BLE/src/BLECharacteristic.h +++ b/libraries/BLE/src/BLECharacteristic.h @@ -90,6 +90,8 @@ class BLECharacteristic { static const uint32_t PROPERTY_INDICATE = 1<<4; static const uint32_t PROPERTY_WRITE_NR = 1<<5; + static const uint32_t indicationTimeout = 1000; + private: friend class BLEServer; @@ -130,9 +132,22 @@ class BLECharacteristic { */ class BLECharacteristicCallbacks { public: + typedef enum { + SUCCESS_INDICATE, + SUCCESS_NOTIFY, + ERROR_INDICATE_DISABLED, + ERROR_NOTIFY_DISABLED, + ERROR_GATT, + ERROR_NO_CLIENT, + ERROR_INDICATE_TIMEOUT, + ERROR_INDICATE_FAILURE + }Status; + virtual ~BLECharacteristicCallbacks(); virtual void onRead(BLECharacteristic* pCharacteristic); virtual void onWrite(BLECharacteristic* pCharacteristic); + virtual void onNotify(BLECharacteristic* pCharacteristic); + virtual void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code); }; #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ diff --git a/libraries/BLE/src/FreeRTOS.cpp b/libraries/BLE/src/FreeRTOS.cpp index 071853cfcfc..895ba267f81 100644 --- a/libraries/BLE/src/FreeRTOS.cpp +++ b/libraries/BLE/src/FreeRTOS.cpp @@ -78,6 +78,38 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { return m_value; } // wait +/** + * @brief Wait for a semaphore to be released in a given period of time by trying to take it and + * then releasing it again. The value associated with the semaphore can be taken by value() call after return + * @param [in] owner A debug tag. + * @param [in] timeoutMs timeout to wait in ms. + * @return True if we took the semaphore within timeframe. + */ +bool FreeRTOS::Semaphore::timedWait(std::string owner, uint32_t timeoutMs) { + log_v(">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + + if (m_usePthreads && timeoutMs != portMAX_DELAY) { + assert(false); // We apparently don't have a timed wait for pthreads. + } + + auto ret = pdTRUE; + + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + ret = xSemaphoreTake(m_semaphore, timeoutMs); + } + + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } + + log_v("<< wait: Semaphore %s released: %d", toString().c_str(), ret); + return ret; +} // wait + FreeRTOS::Semaphore::Semaphore(std::string name) { m_usePthreads = false; // Are we using pThreads or FreeRTOS? diff --git a/libraries/BLE/src/FreeRTOS.h b/libraries/BLE/src/FreeRTOS.h index b861c8757c8..4d089c81db6 100644 --- a/libraries/BLE/src/FreeRTOS.h +++ b/libraries/BLE/src/FreeRTOS.h @@ -40,6 +40,8 @@ class FreeRTOS { bool take(uint32_t timeoutMs, std::string owner = ""); std::string toString(); uint32_t wait(std::string owner = ""); + bool timedWait(std::string owner = "", uint32_t timeoutMs = portMAX_DELAY); + uint32_t value(){ return m_value; }; private: SemaphoreHandle_t m_semaphore;