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:
		
							parent
							
								
									03066e42ef
								
							
						
					
					
						commit
						5137fc5c80
					
				| @ -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 */ | ||||
|  | ||||
| @ -90,6 +90,8 @@ public: | ||||
| 	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 @@ private: | ||||
|  */ | ||||
| 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_ */ | ||||
|  | ||||
| @ -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?
 | ||||
|  | ||||
| @ -40,6 +40,8 @@ public: | ||||
| 		bool        take(uint32_t timeoutMs, std::string owner = "<Unknown>"); | ||||
| 		std::string toString(); | ||||
| 		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: | ||||
| 		SemaphoreHandle_t m_semaphore; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user