976|5

136

帖子

2

TA的资源

一粒金砂(高级)

楼主
 

【FireBeetle 2 ESP32 C6开发板】-4- 关于读取温湿度Characteristic值的一些问题记录 [复制链接]

当前主动读取温湿度计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

 

完整代码:

Proj6_Ble_Client_with_XiaomiTempHumi2_FixReadValueIssue_V3.ino (7.92 KB, 下载次数: 0)
此帖出自无线连接论坛

最新回复

楼主分享的技术内容非常详实,图文并茂,条理清晰,实用价值很大,   详情 回复 发表于 2024-5-3 15:13
点赞 关注
 

回复
举报

136

帖子

2

TA的资源

一粒金砂(高级)

沙发
 

BLERemoteCharacteristic::readValue() does not return the correct value #20 

链接已隐藏,如需查看请登录或者注册

此帖出自无线连接论坛
 
 

回复

6483

帖子

9

TA的资源

版主

板凳
 

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

此帖出自无线连接论坛

点评

[attachimg]806053[/attachimg] 我试着注释掉readValue(), 只调用readRawData()然后设备会不停的重启。  详情 回复 发表于 2024-5-2 20:08
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 
 

回复

136

帖子

2

TA的资源

一粒金砂(高级)

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

 我试着注释掉readValue(), 只调用readRawData()然后设备会不停的重启。

此帖出自无线连接论坛
 
 
 

回复

136

帖子

2

TA的资源

一粒金砂(高级)

5
 

在此贴,请教一下Arduino大佬,

https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/, 查看了里面的解释,还是不太理解m_value = String((char*) evtParam->read.value, evtParam->read.value_len);

比如,小米温湿度计2的返回数据是5个byte,那么m_value = String((char *) 数据地址,5);

这个m_value就是使用readValue()直接读取Characteristic值的时候返回值。

		// 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
BLERemoteCharacteristic.cpp (18.86 KB, 下载次数: 0)
此帖出自无线连接论坛
 
 
 

回复

755

帖子

5

TA的资源

纯净的硅(高级)

6
 

楼主分享的技术内容非常详实,图文并茂,条理清晰,实用价值很大,

此帖出自无线连接论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
快速回复 返回顶部 返回列表