当前主动读取温湿度计2的Characteristic使用的代码如下:
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
// Read the value of the characteristic.
if(pRemoteCharacteristic->canRead()) {
#if 1
String value = pRemoteCharacteristic->readValue();
//Serial.println("The characteristic value using readValue was: " + value+ " Length of value is: " + String(value.length()));
#endif
#if 1
uint8_t* test = pRemoteCharacteristic->readRawData();
uint8_t i;
uint8_t test_temp_val[5]={0};
for(i = 0; i < 5; i++){
if(test != nullptr){
Serial.print("The characteristic value using readRawData was: ");
Serial.println(*test);
test_temp_val[i] = *test;
}else{
Serial.println("pRemoteCharacteristic->readRawData() returns NULL PTR");
}
test++;
}
static uint16_t Test_TempVal = test_temp_val[1]*256 + test_temp_val[0];
Serial.println("Test Temperature is: "+ String(Test_TempVal/100) + "." + String(Test_TempVal%100) + " degree");
static uint8_t Test_HumidityVal = test_temp_val[2];
Serial.println("Test Humidity is: " + String(test_temp_val[2]) + "% ");
static uint16_t Test_BatteryVal = test_temp_val[4]*256 + test_temp_val[3];
Serial.println("Test Battery Voltage is: "+ String(Test_BatteryVal/100) + "." + String(Test_BatteryVal%100) + " mV");
#endif
可以看到需要先调用:
String value = pRemoteCharacteristic->readValue();
然后再调用:
uint8_t* test = pRemoteCharacteristic->readRawData();
实验发现,如果不调用上面的readValue()系统就会Reset。接下来从底层代码来看一下readValue()的代码实现。
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] Read the value of the remote characteristic.
* [url=home.php?mod=space&uid=784970]@return[/url] The value of the remote characteristic.
*/
String BLERemoteCharacteristic::readValue() {
log_v(">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle());
// Check to see that we are connected.
if (!getRemoteService()->getClient()->isConnected()) {
log_e("Disconnected");
return String();
}
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(), // The connection ID to the BLE server
getHandle(), // The handle of this characteristic
m_auth
); // Security
if (errRc != ESP_OK) {
log_e("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 String found
// in m_value will contain our data.
m_semaphoreReadCharEvt.wait("readValue");
log_v("<< readValue(): length: %d", m_value.length());
return m_value;
} // readValue
可以看到,调用readValue()后,会通知底层的蓝牙协议栈去等待读取事件EVT是否完成。接下来读取事件完成后,接着处理ESP_GATTC_READ_CHAR_EVT:
/**
* @brief Handle GATT Client events.
* When an event arrives for a GATT client we give this characteristic the opportunity to
* take a look at it to see if there is interest in it.
* @param [in] event The type of event.
* @param [in] gattc_if The interface on which the event was received.
* @param [in] evtParam Payload data for the event.
* @returns N/A
*/
void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *evtParam) {
switch (event) {
// ESP_GATTC_NOTIFY_EVT
//
// 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) {
log_d("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
// 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 = String((char *)evtParam->read.value, evtParam->read.value_len);
if (m_rawData != nullptr) {
free(m_rawData);
}
m_rawData = (uint8_t *)calloc(evtParam->read.value_len, sizeof(uint8_t));
memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len);
} else {
m_value = "";
}
m_semaphoreReadCharEvt.give();
break;
} // ESP_GATTC_READ_CHAR_EVT
// ESP_GATTC_REG_FOR_NOTIFY_EVT
//
// reg_for_notify:
// - esp_gatt_status_t status
// - uint16_t handle
case ESP_GATTC_REG_FOR_NOTIFY_EVT:
{
// If the request is not for this BLERemoteCharacteristic then move on to the next.
if (evtParam->reg_for_notify.handle != getHandle()) {
break;
}
// 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_UNREG_FOR_NOTIFY_EVT:
// ESP_GATTC_WRITE_CHAR_EVT
//
// write:
// - esp_gatt_status_t status
// - uint16_t conn_id
// - uint16_t handle
case ESP_GATTC_WRITE_CHAR_EVT:
{
// 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
case ESP_GATTC_READ_DESCR_EVT:
case ESP_GATTC_WRITE_DESCR_EVT:
for (auto &myPair : m_descriptorMap) {
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
break;
case ESP_GATTC_DISCONNECT_EVT:
// Cleanup semaphores to avoid deadlocks.
m_semaphoreReadCharEvt.give(1);
m_semaphoreWriteCharEvt.give(1);
break;
default: break;
} // End switch
}; // gattClientEventHandler
接着调用readRawValue(),返回5 Bytes数据的首字节指针。
/**
* @brief Read raw data from remote characteristic as hex bytes
* @return return pointer data read
*/
uint8_t *BLERemoteCharacteristic::readRawData() {
return m_rawData;
}
以上可见,对于想读取小米温湿度计2的温湿度数据,有两种方式:
方式1:直接读取,先使用readValue(),再使用readRawData()
方式2:通过notify事件来处理
演示视频:
esp32c6
完整代码: