慕容雪花 发表于 2024-5-2 16:32

【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-&gt;getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(charUUID.toString().c_str());
      pClient-&gt;disconnect();
      return false;
    }
    Serial.println(" - Found our characteristic");

    // Read the value of the characteristic.
    if(pRemoteCharacteristic-&gt;canRead()) {
#if 1      
      String value = pRemoteCharacteristic-&gt;readValue();
      //Serial.println("The characteristic value using readValue was: " + value+ "Length of value is: " + String(value.length()));
#endif

#if 1
      uint8_t* test = pRemoteCharacteristic-&gt;readRawData();
      uint8_t i;
      uint8_ttest_temp_val={0};

      for(i = 0; i &lt; 5; i++){
      if(test != nullptr){
          Serial.print("The characteristic value using readRawData was: ");
          Serial.println(*test);
          test_temp_val = *test;
      }else{
          Serial.println("pRemoteCharacteristic-&gt;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-&gt;readValue();</code></pre>

<p>然后再调用:</p>

<pre>
<code>uint8_t* test = pRemoteCharacteristic-&gt;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("&gt;&gt; readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle());

// Check to see that we are connected.
if (!getRemoteService()-&gt;getClient()-&gt;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-&gt;getClient()-&gt;getGattcIf(),
    m_pRemoteService-&gt;getClient()-&gt;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("&lt;&lt; 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-&gt;notify.handle != getHandle()) {
      break;
      }
      if (m_notifyCallback != nullptr) {
      log_d("Invoking callback for notification on characteristic %s", toString().c_str());
      m_notifyCallback(this, evtParam-&gt;notify.value, evtParam-&gt;notify.value_len, evtParam-&gt;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-&gt;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-&gt;read.status == ESP_GATT_OK) {
      m_value = String((char *)evtParam-&gt;read.value, evtParam-&gt;read.value_len);
      if (m_rawData != nullptr) {
          free(m_rawData);
      }
      m_rawData = (uint8_t *)calloc(evtParam-&gt;read.value_len, sizeof(uint8_t));
      memcpy(m_rawData, evtParam-&gt;read.value, evtParam-&gt;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-&gt;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-&gt;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-&gt;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 &amp;myPair : m_descriptorMap) {
      myPair.second-&gt;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>&nbsp;</p>

<p><span style="font-size:18px;"><strong>演示视频:</strong></span></p>

<p>ba198d9520a58be8bf1056f4ca78b2fb<br />
&nbsp;</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>

慕容雪花 发表于 2024-5-2 16:34

<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>

秦天qintian0303 发表于 2024-5-2 19:49

<p>每次读取都要先使用readValue(),再使用readRawData()吗?</p>

慕容雪花 发表于 2024-5-2 20:08

秦天qintian0303 发表于 2024-5-2 19:49
每次读取都要先使用readValue(),再使用readRawData()吗?

<p>&nbsp;我试着注释掉readValue(), 只调用readRawData()然后设备会不停的重启。</p>

慕容雪花 发表于 2024-5-3 08:38

<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-&gt;read.value, evtParam-&gt;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-&gt;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-&gt;read.status == ESP_GATT_OK) {
                                m_value = String((char*) evtParam-&gt;read.value, evtParam-&gt;read.value_len);
                                if(m_rawData != nullptr) free(m_rawData);
                                m_rawData = (uint8_t*) calloc(evtParam-&gt;read.value_len, sizeof(uint8_t));
                                memcpy(m_rawData, evtParam-&gt;read.value, evtParam-&gt;read.value_len);
                        } else {
                                m_value = "";
                        }

                        m_semaphoreReadCharEvt.give();
                        break;
                } // ESP_GATTC_READ_CHAR_EVT</code></pre>

<div></div>

chejm 发表于 2024-5-3 15:13

<p>楼主分享的技术内容非常详实,图文并茂,条理清晰,实用价值很大,</p>
页: [1]
查看完整版本: 【FireBeetle 2 ESP32 C6开发板】-4- 关于读取温湿度Characteristic值的一些问题记录