Ble notification/indication status and timeout (#2998)

* add timed wait

* Added Notification/Indication data and status callbacks

* imply null-object pattern for BLE callback
This commit is contained in:
Roman Savrulin 2019-08-20 17:15:30 +03:00 committed by Me No Dev
parent 03066e42ef
commit 5137fc5c80
4 changed files with 105 additions and 12 deletions

View File

@ -22,6 +22,7 @@
#define NULL_HANDLE (0xffff) #define NULL_HANDLE (0xffff)
static BLECharacteristicCallbacks defaultCallback; //null-object-pattern
/** /**
* @brief Construct a characteristic * @brief Construct a characteristic
@ -40,7 +41,7 @@ BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) {
m_bleUUID = uuid; m_bleUUID = uuid;
m_handle = NULL_HANDLE; m_handle = NULL_HANDLE;
m_properties = (esp_gatt_char_prop_t)0; m_properties = (esp_gatt_char_prop_t)0;
m_pCallbacks = nullptr; m_pCallbacks = &defaultCallback;
setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0);
setReadProperty((properties & PROPERTY_READ) != 0); setReadProperty((properties & PROPERTY_READ) != 0);
@ -220,9 +221,7 @@ void BLECharacteristic::handleGATTServerEvent(
case ESP_GATTS_EXEC_WRITE_EVT: { case ESP_GATTS_EXEC_WRITE_EVT: {
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
m_value.commit(); 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 { } else {
m_value.cancel(); m_value.cancel();
} }
@ -307,7 +306,7 @@ void BLECharacteristic::handleGATTServerEvent(
} }
} // Response needed } // 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. m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
} }
} // Match on handles. } // Match on handles.
@ -378,9 +377,9 @@ void BLECharacteristic::handleGATTServerEvent(
} }
} else { // read.is_long == false } 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 // 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. // Invoke the read callback.
} m_pCallbacks->onRead(this);
std::string value = m_value.getValue(); std::string value = m_value.getValue();
@ -480,10 +479,13 @@ void BLECharacteristic::notify(bool is_notification) {
assert(getService() != nullptr); assert(getService() != nullptr);
assert(getService()->getServer() != 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()); GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length());
if (getService()->getServer()->getConnectedCount() == 0) { if (getService()->getServer()->getConnectedCount() == 0) {
log_v("<< notify: No connected clients."); log_v("<< notify: No connected clients.");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NO_CLIENT, 0);
return; return;
} }
@ -494,12 +496,14 @@ void BLECharacteristic::notify(bool is_notification) {
if(is_notification) { if(is_notification) {
if (p2902 != nullptr && !p2902->getNotifications()) { if (p2902 != nullptr && !p2902->getNotifications()) {
log_v("<< notifications disabled; ignoring"); log_v("<< notifications disabled; ignoring");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NOTIFY_DISABLED, 0); // Invoke the notify callback.
return; return;
} }
} }
else{ else{
if (p2902 != nullptr && !p2902->getIndications()) { if (p2902 != nullptr && !p2902->getIndications()) {
log_v("<< indications disabled; ignoring"); log_v("<< indications disabled; ignoring");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED, 0); // Invoke the notify callback.
return; return;
} }
} }
@ -510,7 +514,7 @@ void BLECharacteristic::notify(bool is_notification) {
} }
size_t length = m_value.getValue().length(); size_t length = m_value.getValue().length();
if(!is_notification) if(!is_notification) // is indication
m_semaphoreConfEvt.take("indicate"); m_semaphoreConfEvt.take("indicate");
esp_err_t errRc = ::esp_ble_gatts_send_indicate( esp_err_t errRc = ::esp_ble_gatts_send_indicate(
getService()->getServer()->getGattsIf(), getService()->getServer()->getGattsIf(),
@ -519,10 +523,23 @@ void BLECharacteristic::notify(bool is_notification) {
if (errRc != ESP_OK) { if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc)); log_e("<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc));
m_semaphoreConfEvt.give(); m_semaphoreConfEvt.give();
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_GATT, errRc); // Invoke the notify callback.
return; return;
} }
if(!is_notification) if(!is_notification){ // is indication
m_semaphoreConfEvt.wait("indicate"); 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"); log_v("<< notify");
} // Notify } // Notify
@ -551,7 +568,11 @@ void BLECharacteristic::setBroadcastProperty(bool value) {
*/ */
void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) { void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) {
log_v(">> setCallbacks: 0x%x", (uint32_t)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"); log_v("<< setCallbacks");
} // setCallbacks } // setCallbacks
@ -754,4 +775,27 @@ void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) {
log_d("BLECharacteristicCallbacks", "<< onWrite"); log_d("BLECharacteristicCallbacks", "<< onWrite");
} // 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 */ #endif /* CONFIG_BT_ENABLED */

View File

@ -90,6 +90,8 @@ public:
static const uint32_t PROPERTY_INDICATE = 1<<4; static const uint32_t PROPERTY_INDICATE = 1<<4;
static const uint32_t PROPERTY_WRITE_NR = 1<<5; static const uint32_t PROPERTY_WRITE_NR = 1<<5;
static const uint32_t indicationTimeout = 1000;
private: private:
friend class BLEServer; friend class BLEServer;
@ -130,9 +132,22 @@ private:
*/ */
class BLECharacteristicCallbacks { class BLECharacteristicCallbacks {
public: 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 ~BLECharacteristicCallbacks();
virtual void onRead(BLECharacteristic* pCharacteristic); virtual void onRead(BLECharacteristic* pCharacteristic);
virtual void onWrite(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 /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */

View File

@ -78,6 +78,38 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) {
return m_value; return m_value;
} // wait } // 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) { FreeRTOS::Semaphore::Semaphore(std::string name) {
m_usePthreads = false; // Are we using pThreads or FreeRTOS? m_usePthreads = false; // Are we using pThreads or FreeRTOS?

View File

@ -40,6 +40,8 @@ public:
bool take(uint32_t timeoutMs, std::string owner = "<Unknown>"); bool take(uint32_t timeoutMs, std::string owner = "<Unknown>");
std::string toString(); std::string toString();
uint32_t wait(std::string owner = "<Unknown>"); uint32_t wait(std::string owner = "<Unknown>");
bool timedWait(std::string owner = "<Unknown>", uint32_t timeoutMs = portMAX_DELAY);
uint32_t value(){ return m_value; };
private: private:
SemaphoreHandle_t m_semaphore; SemaphoreHandle_t m_semaphore;