【FireBeetle 2 ESP32 C6开发板】-4- 关于读取温湿度Characteristic值的一些问题记录
<div class='showpostmsg'><p>当前主动读取温湿度计2的Characteristic使用的代码如下:</p><pre>
<code> // 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_ttest_temp_val={0};
for(i = 0; i < 5; i++){
if(test != nullptr){
Serial.print("The characteristic value using readRawData was: ");
Serial.println(*test);
test_temp_val = *test;
}else{
Serial.println("pRemoteCharacteristic->readRawData() returns NULL PTR");
}
test++;
}
static uint16_t Test_TempVal = test_temp_val*256 + test_temp_val;
Serial.println("Test Temperature is: "+ String(Test_TempVal/100) + "." + String(Test_TempVal%100) + " degree");
static uint8_t Test_HumidityVal = test_temp_val;
Serial.println("Test Humidity is: " + String(test_temp_val) + "% ");
static uint16_t Test_BatteryVal = test_temp_val*256 + test_temp_val;
Serial.println("Test Battery Voltage is: "+ String(Test_BatteryVal/100) + "." + String(Test_BatteryVal%100) + " mV");
#endif </code></pre>
<p>可以看到需要先调用:</p>
<pre>
<code>String value = pRemoteCharacteristic->readValue();</code></pre>
<p>然后再调用:</p>
<pre>
<code>uint8_t* test = pRemoteCharacteristic->readRawData();</code></pre>
<p>实验发现,如果不调用上面的readValue()系统就会Reset。接下来从底层代码来看一下readValue()的代码实现。</p>
<pre>
<code>/**
* @brief Read the value of the remote characteristic.
* @return 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</code></pre>
<p>可以看到,调用readValue()后,会通知底层的蓝牙协议栈去等待读取事件EVT是否完成。接下来读取事件完成后,接着处理ESP_GATTC_READ_CHAR_EVT:</p>
<pre>
<code>/**
* @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 event The type of event.
* @param gattc_if The interface on which the event was received.
* @param 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_tstatus
// - 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</code></pre>
<p>接着调用readRawValue(),返回5 Bytes数据的首字节指针。</p>
<pre>
<code>/**
* @brief Read raw data from remote characteristic as hex bytes
* @return return pointer data read
*/
uint8_t *BLERemoteCharacteristic::readRawData() {
return m_rawData;
}</code></pre>
<p>以上可见,对于想读取小米温湿度计2的温湿度数据,有两种方式:</p>
<p>方式1:直接读取,先使用readValue(),再使用readRawData()</p>
<p>方式2:通过notify事件来处理</p>
<p> </p>
<p><span style="font-size:18px;"><strong>演示视频:</strong></span></p>
<p>ba198d9520a58be8bf1056f4ca78b2fb<br />
</p>
<p><span style="font-size:18px;"><strong>完整代码:</strong></span></p>
<div></div>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> <p>BLERemoteCharacteristic::readValue() does not return the correct value #20 <a href="https://github.com/h2zero/NimBLE-Arduino/issues/20" target="_blank">https://github.com/h2zero/NimBLE-Arduino/issues/20</a></p>
<p>每次读取都要先使用readValue(),再使用readRawData()吗?</p>
秦天qintian0303 发表于 2024-5-2 19:49
每次读取都要先使用readValue(),再使用readRawData()吗?
<p> 我试着注释掉readValue(), 只调用readRawData()然后设备会不停的重启。</p>
<p>在此贴,请教一下Arduino大佬,</p>
<p><strong><a href="https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/" target="_blank">https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/</a>, 查看了里面的解释,还是不太理解</strong>m_value = String((char*) evtParam->read.value, evtParam->read.value_len);</p>
<p>比如,小米温湿度计2的返回数据是5个byte,那么m_value = String((char *) 数据地址,5);</p>
<p>这个m_value就是使用readValue()直接读取Characteristic值的时候返回值。</p>
<pre>
<code> // ESP_GATTC_READ_CHAR_EVT
// This event indicates that the server has responded to the read request.
//
// read:
// - esp_gatt_status_tstatus
// - 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</code></pre>
<div></div>
<p>楼主分享的技术内容非常详实,图文并茂,条理清晰,实用价值很大,</p>
页:
[1]