mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Update ESP32 BLE ADV parse to match BLE spec (#904)
* Update ESP32 BLE ADV parse to match BLE spec * Update xiaomi * Update ruuvi * Format * Update esp32_ble_tracker.cpp * Fix log * Format * Update xiaomi_ble.cpp
This commit is contained in:
		| @@ -203,10 +203,6 @@ void ESP32BLETracker::gap_scan_result(const esp_ble_gap_cb_param_t::ble_scan_res | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| std::string hexencode_string(const std::string &raw_data) { |  | ||||||
|   return hexencode(reinterpret_cast<const uint8_t *>(raw_data.c_str()), raw_data.size()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ESPBTUUID::ESPBTUUID() : uuid_() {} | ESPBTUUID::ESPBTUUID() : uuid_() {} | ||||||
| ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) { | ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) { | ||||||
|   ESPBTUUID ret; |   ESPBTUUID ret; | ||||||
| @@ -267,13 +263,13 @@ std::string ESPBTUUID::to_string() { | |||||||
| } | } | ||||||
|  |  | ||||||
| ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } | ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } | ||||||
| optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const std::string &data) { | optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) { | ||||||
|   if (data.size() != 25) |   if (!data.uuid.contains(0x4C, 0x00)) | ||||||
|     return {}; |  | ||||||
|   if (data[0] != 0x4C || data[1] != 0x00) |  | ||||||
|     return {}; |     return {}; | ||||||
|  |  | ||||||
|   return ESPBLEiBeacon(reinterpret_cast<const uint8_t *>(data.data())); |   if (data.data.size() != 23) | ||||||
|  |     return {}; | ||||||
|  |   return ESPBLEiBeacon(data.data.data()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { | void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { | ||||||
| @@ -305,8 +301,8 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e | |||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "  RSSI: %d", this->rssi_); |   ESP_LOGVV(TAG, "  RSSI: %d", this->rssi_); | ||||||
|   ESP_LOGVV(TAG, "  Name: '%s'", this->name_.c_str()); |   ESP_LOGVV(TAG, "  Name: '%s'", this->name_.c_str()); | ||||||
|   if (this->tx_power_.has_value()) { |   for (auto &it : this->tx_powers_) { | ||||||
|     ESP_LOGVV(TAG, "  TX Power: %d", *this->tx_power_); |     ESP_LOGVV(TAG, "  TX Power: %d", it); | ||||||
|   } |   } | ||||||
|   if (this->appearance_.has_value()) { |   if (this->appearance_.has_value()) { | ||||||
|     ESP_LOGVV(TAG, "  Appearance: %u", *this->appearance_); |     ESP_LOGVV(TAG, "  Appearance: %u", *this->appearance_); | ||||||
| @@ -314,20 +310,19 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e | |||||||
|   if (this->ad_flag_.has_value()) { |   if (this->ad_flag_.has_value()) { | ||||||
|     ESP_LOGVV(TAG, "  Ad Flag: %u", *this->ad_flag_); |     ESP_LOGVV(TAG, "  Ad Flag: %u", *this->ad_flag_); | ||||||
|   } |   } | ||||||
|   for (auto uuid : this->service_uuids_) { |   for (auto &uuid : this->service_uuids_) { | ||||||
|     ESP_LOGVV(TAG, "  Service UUID: %s", uuid.to_string().c_str()); |     ESP_LOGVV(TAG, "  Service UUID: %s", uuid.to_string().c_str()); | ||||||
|   } |   } | ||||||
|   ESP_LOGVV(TAG, "  Manufacturer data: %s", hexencode_string(this->manufacturer_data_).c_str()); |   for (auto &data : this->manufacturer_datas_) { | ||||||
|   ESP_LOGVV(TAG, "  Service data: %s", hexencode_string(this->service_data_).c_str()); |     ESP_LOGVV(TAG, "  Manufacturer data: %s", hexencode(data.data).c_str()); | ||||||
|  |   } | ||||||
|   if (this->service_data_uuid_.has_value()) { |   for (auto &data : this->service_datas_) { | ||||||
|     ESP_LOGVV(TAG, "  Service Data UUID: %s", this->service_data_uuid_->to_string().c_str()); |     ESP_LOGVV(TAG, "  Service data:"); | ||||||
|  |     ESP_LOGVV(TAG, "    UUID: %s", data.uuid.to_string().c_str()); | ||||||
|  |     ESP_LOGVV(TAG, "    Data: %s", hexencode(data.data).c_str()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "Adv data: %s", |   ESP_LOGVV(TAG, "Adv data: %s", hexencode(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str()); | ||||||
|             hexencode_string( |  | ||||||
|                 std::string(reinterpret_cast<const char *>(param.ble_adv), param.adv_data_len + param.scan_rsp_len)) |  | ||||||
|                 .c_str()); |  | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { | void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { | ||||||
| @@ -346,25 +341,52 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p | |||||||
|     const uint8_t record_length = field_length - 1; |     const uint8_t record_length = field_length - 1; | ||||||
|     offset += record_length; |     offset += record_length; | ||||||
|  |  | ||||||
|  |     // See also Generic Access Profile Assigned Numbers: | ||||||
|  |     // https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/ See also ADVERTISING AND SCAN | ||||||
|  |     // RESPONSE DATA FORMAT: https://www.bluetooth.com/specifications/bluetooth-core-specification/ (vol 3, part C, 11) | ||||||
|  |     // See also Core Specification Supplement: https://www.bluetooth.com/specifications/bluetooth-core-specification/ | ||||||
|  |     // (called CSS here) | ||||||
|  |  | ||||||
|     switch (record_type) { |     switch (record_type) { | ||||||
|       case ESP_BLE_AD_TYPE_NAME_CMPL: { |       case ESP_BLE_AD_TYPE_NAME_CMPL: { | ||||||
|  |         // CSS 1.2 LOCAL NAME | ||||||
|  |         // "The Local Name data type shall be the same as, or a shortened version of, the local name assigned to the | ||||||
|  |         // device." CSS 1: Optional in this context; shall not appear more than once in a block. | ||||||
|         this->name_ = std::string(reinterpret_cast<const char *>(record), record_length); |         this->name_ = std::string(reinterpret_cast<const char *>(record), record_length); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_TYPE_TX_PWR: { |       case ESP_BLE_AD_TYPE_TX_PWR: { | ||||||
|         this->tx_power_ = *payload; |         // CSS 1.5 TX POWER LEVEL | ||||||
|  |         // "The TX Power Level data type indicates the transmitted power level of the packet containing the data type." | ||||||
|  |         // CSS 1: Optional in this context (may appear more than once in a block). | ||||||
|  |         this->tx_powers_.push_back(*payload); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_TYPE_APPEARANCE: { |       case ESP_BLE_AD_TYPE_APPEARANCE: { | ||||||
|  |         // CSS 1.12 APPEARANCE | ||||||
|  |         // "The Appearance data type defines the external appearance of the device." | ||||||
|  |         // See also https://www.bluetooth.com/specifications/gatt/characteristics/ | ||||||
|  |         // CSS 1: Optional in this context; shall not appear more than once in a block and shall not appear in both | ||||||
|  |         // the AD and SRD of the same extended advertising interval. | ||||||
|         this->appearance_ = *reinterpret_cast<const uint16_t *>(record); |         this->appearance_ = *reinterpret_cast<const uint16_t *>(record); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_TYPE_FLAG: { |       case ESP_BLE_AD_TYPE_FLAG: { | ||||||
|  |         // CSS 1.3 FLAGS | ||||||
|  |         // "The Flags data type contains one bit Boolean flags. The Flags data type shall be included when any of the | ||||||
|  |         // Flag bits are non-zero and the advertising packet is connectable, otherwise the Flags data type may be | ||||||
|  |         // omitted." | ||||||
|  |         // CSS 1: Optional in this context; shall not appear more than once in a block. | ||||||
|         this->ad_flag_ = *record; |         this->ad_flag_ = *record; | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|  |       // CSS 1.1 SERVICE UUID | ||||||
|  |       // The Service UUID data type is used to include a list of Service or Service Class UUIDs. | ||||||
|  |       // There are six data types defined for the three sizes of Service UUIDs that may be returned: | ||||||
|  |       // CSS 1: Optional in this context (may appear more than once in a block). | ||||||
|       case ESP_BLE_AD_TYPE_16SRV_CMPL: |       case ESP_BLE_AD_TYPE_16SRV_CMPL: | ||||||
|       case ESP_BLE_AD_TYPE_16SRV_PART: { |       case ESP_BLE_AD_TYPE_16SRV_PART: { | ||||||
|  |         // • 16-bit Bluetooth Service UUIDs | ||||||
|         for (uint8_t i = 0; i < record_length / 2; i++) { |         for (uint8_t i = 0; i < record_length / 2; i++) { | ||||||
|           this->service_uuids_.push_back(ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record + 2 * i))); |           this->service_uuids_.push_back(ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record + 2 * i))); | ||||||
|         } |         } | ||||||
| @@ -372,6 +394,7 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p | |||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_TYPE_32SRV_CMPL: |       case ESP_BLE_AD_TYPE_32SRV_CMPL: | ||||||
|       case ESP_BLE_AD_TYPE_32SRV_PART: { |       case ESP_BLE_AD_TYPE_32SRV_PART: { | ||||||
|  |         // • 32-bit Bluetooth Service UUIDs | ||||||
|         for (uint8_t i = 0; i < record_length / 4; i++) { |         for (uint8_t i = 0; i < record_length / 4; i++) { | ||||||
|           this->service_uuids_.push_back(ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record + 4 * i))); |           this->service_uuids_.push_back(ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record + 4 * i))); | ||||||
|         } |         } | ||||||
| @@ -379,41 +402,70 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p | |||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_TYPE_128SRV_CMPL: |       case ESP_BLE_AD_TYPE_128SRV_CMPL: | ||||||
|       case ESP_BLE_AD_TYPE_128SRV_PART: { |       case ESP_BLE_AD_TYPE_128SRV_PART: { | ||||||
|  |         // • Global 128-bit Service UUIDs | ||||||
|         this->service_uuids_.push_back(ESPBTUUID::from_raw(record)); |         this->service_uuids_.push_back(ESPBTUUID::from_raw(record)); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: { |       case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: { | ||||||
|         this->manufacturer_data_ = std::string(reinterpret_cast<const char *>(record), record_length); |         // CSS 1.4 MANUFACTURER SPECIFIC DATA | ||||||
|  |         // "The Manufacturer Specific data type is used for manufacturer specific data. The first two data octets shall | ||||||
|  |         // contain a company identifier from Assigned Numbers. The interpretation of any other octets within the data | ||||||
|  |         // shall be defined by the manufacturer specified by the company identifier." | ||||||
|  |         // CSS 1: Optional in this context (may appear more than once in a block). | ||||||
|  |         if (record_length < 2) { | ||||||
|  |           ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"); | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
|  |         ServiceData data{}; | ||||||
|  |         data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record)); | ||||||
|  |         data.data.assign(record + 2UL, record + record_length); | ||||||
|  |         this->manufacturer_datas_.push_back(data); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // CSS 1.11 SERVICE DATA | ||||||
|  |       // "The Service Data data type consists of a service UUID with the data associated with that service." | ||||||
|  |       // CSS 1: Optional in this context (may appear more than once in a block). | ||||||
|       case ESP_BLE_AD_TYPE_SERVICE_DATA: { |       case ESP_BLE_AD_TYPE_SERVICE_DATA: { | ||||||
|  |         // «Service Data - 16 bit UUID» | ||||||
|  |         // Size: 2 or more octets | ||||||
|  |         // The first 2 octets contain the 16 bit Service UUID fol- lowed by additional service data | ||||||
|         if (record_length < 2) { |         if (record_length < 2) { | ||||||
|           ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); |           ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
|         this->service_data_uuid_ = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record)); |         ServiceData data{}; | ||||||
|         if (record_length > 2) |         data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record)); | ||||||
|           this->service_data_ = std::string(reinterpret_cast<const char *>(record + 2), record_length - 2UL); |         data.data.assign(record + 2UL, record + record_length); | ||||||
|  |         this->service_datas_.push_back(data); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_TYPE_32SERVICE_DATA: { |       case ESP_BLE_AD_TYPE_32SERVICE_DATA: { | ||||||
|  |         // «Service Data - 32 bit UUID» | ||||||
|  |         // Size: 4 or more octets | ||||||
|  |         // The first 4 octets contain the 32 bit Service UUID fol- lowed by additional service data | ||||||
|         if (record_length < 4) { |         if (record_length < 4) { | ||||||
|           ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); |           ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
|         this->service_data_uuid_ = ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record)); |         ServiceData data{}; | ||||||
|         if (record_length > 4) |         data.uuid = ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record)); | ||||||
|           this->service_data_ = std::string(reinterpret_cast<const char *>(record + 4), record_length - 4UL); |         data.data.assign(record + 4UL, record + record_length); | ||||||
|  |         this->service_datas_.push_back(data); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       case ESP_BLE_AD_TYPE_128SERVICE_DATA: { |       case ESP_BLE_AD_TYPE_128SERVICE_DATA: { | ||||||
|  |         // «Service Data - 128 bit UUID» | ||||||
|  |         // Size: 16 or more octets | ||||||
|  |         // The first 16 octets contain the 128 bit Service UUID followed by additional service data | ||||||
|         if (record_length < 16) { |         if (record_length < 16) { | ||||||
|           ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); |           ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
|         this->service_data_uuid_ = ESPBTUUID::from_raw(record); |         ServiceData data{}; | ||||||
|         if (record_length > 16) |         data.uuid = ESPBTUUID::from_raw(record); | ||||||
|           this->service_data_ = std::string(reinterpret_cast<const char *>(record + 16), record_length - 16UL); |         data.data.assign(record + 16UL, record + record_length); | ||||||
|  |         this->service_datas_.push_back(data); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       default: { |       default: { | ||||||
| @@ -430,16 +482,6 @@ std::string ESPBTDevice::address_str() const { | |||||||
|   return mac; |   return mac; | ||||||
| } | } | ||||||
| uint64_t ESPBTDevice::address_uint64() const { return ble_addr_to_uint64(this->address_); } | uint64_t ESPBTDevice::address_uint64() const { return ble_addr_to_uint64(this->address_); } | ||||||
| esp_ble_addr_type_t ESPBTDevice::get_address_type() const { return this->address_type_; } |  | ||||||
| int ESPBTDevice::get_rssi() const { return this->rssi_; } |  | ||||||
| const std::string &ESPBTDevice::get_name() const { return this->name_; } |  | ||||||
| const optional<int8_t> &ESPBTDevice::get_tx_power() const { return this->tx_power_; } |  | ||||||
| const optional<uint16_t> &ESPBTDevice::get_appearance() const { return this->appearance_; } |  | ||||||
| const optional<uint8_t> &ESPBTDevice::get_ad_flag() const { return this->ad_flag_; } |  | ||||||
| const std::vector<ESPBTUUID> &ESPBTDevice::get_service_uuids() const { return this->service_uuids_; } |  | ||||||
| const std::string &ESPBTDevice::get_manufacturer_data() const { return this->manufacturer_data_; } |  | ||||||
| const std::string &ESPBTDevice::get_service_data() const { return this->service_data_; } |  | ||||||
| const optional<ESPBTUUID> &ESPBTDevice::get_service_data_uuid() const { return this->service_data_uuid_; } |  | ||||||
|  |  | ||||||
| void ESP32BLETracker::dump_config() { | void ESP32BLETracker::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "BLE Tracker:"); |   ESP_LOGCONFIG(TAG, "BLE Tracker:"); | ||||||
| @@ -480,8 +522,8 @@ void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { | |||||||
|   ESP_LOGD(TAG, "  Address Type: %s", address_type_s); |   ESP_LOGD(TAG, "  Address Type: %s", address_type_s); | ||||||
|   if (!device.get_name().empty()) |   if (!device.get_name().empty()) | ||||||
|     ESP_LOGD(TAG, "  Name: '%s'", device.get_name().c_str()); |     ESP_LOGD(TAG, "  Name: '%s'", device.get_name().c_str()); | ||||||
|   if (device.get_tx_power().has_value()) { |   for (auto &tx_power : device.get_tx_powers()) { | ||||||
|     ESP_LOGD(TAG, "  TX Power: %d", *device.get_tx_power()); |     ESP_LOGD(TAG, "  TX Power: %d", tx_power); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -33,11 +33,18 @@ class ESPBTUUID { | |||||||
|   esp_bt_uuid_t uuid_; |   esp_bt_uuid_t uuid_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | using adv_data_t = std::vector<uint8_t>; | ||||||
|  |  | ||||||
|  | struct ServiceData { | ||||||
|  |   ESPBTUUID uuid; | ||||||
|  |   adv_data_t data; | ||||||
|  | }; | ||||||
|  |  | ||||||
| class ESPBLEiBeacon { | class ESPBLEiBeacon { | ||||||
|  public: |  public: | ||||||
|   ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); } |   ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); } | ||||||
|   ESPBLEiBeacon(const uint8_t *data); |   ESPBLEiBeacon(const uint8_t *data); | ||||||
|   static optional<ESPBLEiBeacon> from_manufacturer_data(const std::string &data); |   static optional<ESPBLEiBeacon> from_manufacturer_data(const ServiceData &data); | ||||||
|  |  | ||||||
|   uint16_t get_major() { return reverse_bits_16(this->beacon_data_.major); } |   uint16_t get_major() { return reverse_bits_16(this->beacon_data_.major); } | ||||||
|   uint16_t get_minor() { return reverse_bits_16(this->beacon_data_.minor); } |   uint16_t get_minor() { return reverse_bits_16(this->beacon_data_.minor); } | ||||||
| @@ -46,7 +53,6 @@ class ESPBLEiBeacon { | |||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   struct { |   struct { | ||||||
|     uint16_t manufacturer_id; |  | ||||||
|     uint8_t sub_type; |     uint8_t sub_type; | ||||||
|     uint8_t proximity_uuid[16]; |     uint8_t proximity_uuid[16]; | ||||||
|     uint16_t major; |     uint16_t major; | ||||||
| @@ -63,18 +69,33 @@ class ESPBTDevice { | |||||||
|  |  | ||||||
|   uint64_t address_uint64() const; |   uint64_t address_uint64() const; | ||||||
|  |  | ||||||
|   esp_ble_addr_type_t get_address_type() const; |   esp_ble_addr_type_t get_address_type() const { return this->address_type_; } | ||||||
|   int get_rssi() const; |   int get_rssi() const { return rssi_; } | ||||||
|   const std::string &get_name() const; |   const std::string &get_name() const { return this->name_; } | ||||||
|   const optional<int8_t> &get_tx_power() const; |  | ||||||
|   const optional<uint16_t> &get_appearance() const; |   ESPDEPRECATED("Use get_tx_powers() instead") | ||||||
|   const optional<uint8_t> &get_ad_flag() const; |   optional<int8_t> get_tx_power() const { | ||||||
|   const std::vector<ESPBTUUID> &get_service_uuids() const; |     if (this->tx_powers_.empty()) | ||||||
|   const std::string &get_manufacturer_data() const; |       return {}; | ||||||
|   const std::string &get_service_data() const; |     return this->tx_powers_[0]; | ||||||
|   const optional<ESPBTUUID> &get_service_data_uuid() const; |   } | ||||||
|   const optional<ESPBLEiBeacon> get_ibeacon() const { |   const std::vector<int8_t> &get_tx_powers() const { return tx_powers_; } | ||||||
|     return ESPBLEiBeacon::from_manufacturer_data(this->manufacturer_data_); |  | ||||||
|  |   const optional<uint16_t> &get_appearance() const { return appearance_; } | ||||||
|  |   const optional<uint8_t> &get_ad_flag() const { return ad_flag_; } | ||||||
|  |   const std::vector<ESPBTUUID> &get_service_uuids() const { return service_uuids_; } | ||||||
|  |  | ||||||
|  |   const std::vector<ServiceData> &get_manufacturer_datas() const { return manufacturer_datas_; } | ||||||
|  |  | ||||||
|  |   const std::vector<ServiceData> &get_service_datas() const { return service_datas_; } | ||||||
|  |  | ||||||
|  |   optional<ESPBLEiBeacon> get_ibeacon() const { | ||||||
|  |     for (auto &it : this->manufacturer_datas_) { | ||||||
|  |       auto res = ESPBLEiBeacon::from_manufacturer_data(it); | ||||||
|  |       if (res.has_value()) | ||||||
|  |         return *res; | ||||||
|  |     } | ||||||
|  |     return {}; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
| @@ -86,13 +107,12 @@ class ESPBTDevice { | |||||||
|   esp_ble_addr_type_t address_type_{BLE_ADDR_TYPE_PUBLIC}; |   esp_ble_addr_type_t address_type_{BLE_ADDR_TYPE_PUBLIC}; | ||||||
|   int rssi_{0}; |   int rssi_{0}; | ||||||
|   std::string name_{}; |   std::string name_{}; | ||||||
|   optional<int8_t> tx_power_{}; |   std::vector<int8_t> tx_powers_{}; | ||||||
|   optional<uint16_t> appearance_{}; |   optional<uint16_t> appearance_{}; | ||||||
|   optional<uint8_t> ad_flag_{}; |   optional<uint8_t> ad_flag_{}; | ||||||
|   std::vector<ESPBTUUID> service_uuids_; |   std::vector<ESPBTUUID> service_uuids_; | ||||||
|   std::string manufacturer_data_{}; |   std::vector<ServiceData> manufacturer_datas_{}; | ||||||
|   std::string service_data_{}; |   std::vector<ServiceData> service_datas_{}; | ||||||
|   optional<ESPBTUUID> service_data_uuid_{}; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ESP32BLETracker; | class ESP32BLETracker; | ||||||
|   | |||||||
| @@ -8,10 +8,12 @@ namespace ruuvi_ble { | |||||||
|  |  | ||||||
| static const char *TAG = "ruuvi_ble"; | static const char *TAG = "ruuvi_ble"; | ||||||
|  |  | ||||||
| bool parse_ruuvi_data_byte(uint8_t data_type, uint8_t data_length, const uint8_t *data, RuuviParseResult &result) { | bool parse_ruuvi_data_byte(const esp32_ble_tracker::adv_data_t &adv_data, RuuviParseResult &result) { | ||||||
|  |   const uint8_t data_type = adv_data[0]; | ||||||
|  |   const auto *data = &adv_data[1]; | ||||||
|   switch (data_type) { |   switch (data_type) { | ||||||
|     case 0x03: {  // RAWv1 |     case 0x03: {  // RAWv1 | ||||||
|       if (data_length != 16) |       if (adv_data.size() != 14) | ||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
|       const uint8_t temp_sign = (data[1] >> 7) & 1; |       const uint8_t temp_sign = (data[1] >> 7) & 1; | ||||||
| @@ -32,13 +34,13 @@ bool parse_ruuvi_data_byte(uint8_t data_type, uint8_t data_length, const uint8_t | |||||||
|       result.acceleration_y = acceleration_y; |       result.acceleration_y = acceleration_y; | ||||||
|       result.acceleration_z = acceleration_z; |       result.acceleration_z = acceleration_z; | ||||||
|       result.acceleration = |       result.acceleration = | ||||||
|           sqrt(acceleration_x * acceleration_x + acceleration_y * acceleration_y + acceleration_z * acceleration_z); |           sqrtf(acceleration_x * acceleration_x + acceleration_y * acceleration_y + acceleration_z * acceleration_z); | ||||||
|       result.battery_voltage = battery_voltage; |       result.battery_voltage = battery_voltage; | ||||||
|  |  | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|     case 0x05: {  // RAWv2 |     case 0x05: {  // RAWv2 | ||||||
|       if (data_length != 26) |       if (adv_data.size() != 24) | ||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
|       const float temperature = (int16_t(data[0] << 8) + int16_t(data[1])) * 0.005f; |       const float temperature = (int16_t(data[0] << 8) + int16_t(data[1])) * 0.005f; | ||||||
| @@ -63,7 +65,7 @@ bool parse_ruuvi_data_byte(uint8_t data_type, uint8_t data_length, const uint8_t | |||||||
|       result.acceleration_z = data[10] == 0xFF && data[11] == 0xFF ? NAN : acceleration_z; |       result.acceleration_z = data[10] == 0xFF && data[11] == 0xFF ? NAN : acceleration_z; | ||||||
|       result.acceleration = result.acceleration_x == NAN || result.acceleration_y == NAN || result.acceleration_z == NAN |       result.acceleration = result.acceleration_x == NAN || result.acceleration_y == NAN || result.acceleration_z == NAN | ||||||
|                                 ? NAN |                                 ? NAN | ||||||
|                                 : sqrt(acceleration_x * acceleration_x + acceleration_y * acceleration_y + |                                 : sqrtf(acceleration_x * acceleration_x + acceleration_y * acceleration_y + | ||||||
|                                         acceleration_z * acceleration_z); |                                         acceleration_z * acceleration_z); | ||||||
|       result.battery_voltage = (power_info >> 5) == 0x7FF ? NAN : battery_voltage; |       result.battery_voltage = (power_info >> 5) == 0x7FF ? NAN : battery_voltage; | ||||||
|       result.tx_power = (power_info & 0x1F) == 0x1F ? NAN : tx_power; |       result.tx_power = (power_info & 0x1F) == 0x1F ? NAN : tx_power; | ||||||
| @@ -77,21 +79,16 @@ bool parse_ruuvi_data_byte(uint8_t data_type, uint8_t data_length, const uint8_t | |||||||
|   } |   } | ||||||
| } | } | ||||||
| optional<RuuviParseResult> parse_ruuvi(const esp32_ble_tracker::ESPBTDevice &device) { | optional<RuuviParseResult> parse_ruuvi(const esp32_ble_tracker::ESPBTDevice &device) { | ||||||
|   const auto *raw = reinterpret_cast<const uint8_t *>(device.get_manufacturer_data().data()); |   bool success = false; | ||||||
|  |   RuuviParseResult result{}; | ||||||
|  |   for (auto &it : device.get_manufacturer_datas()) { | ||||||
|  |     bool is_ruuvi = it.uuid.contains(0x99, 0x04); | ||||||
|  |     if (!is_ruuvi) | ||||||
|  |       continue; | ||||||
|  |  | ||||||
|   bool is_ruuvi = raw[0] == 0x99 && raw[1] == 0x04; |     if (parse_ruuvi_data_byte(it.data, result)) | ||||||
|  |       success = true; | ||||||
|   if (!is_ruuvi) { |  | ||||||
|     return {}; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const uint8_t data_length = device.get_manufacturer_data().size(); |  | ||||||
|   const uint8_t format = raw[2]; |  | ||||||
|   const uint8_t *data = &raw[3]; |  | ||||||
|  |  | ||||||
|   RuuviParseResult result; |  | ||||||
|  |  | ||||||
|   bool success = parse_ruuvi_data_byte(format, data_length, data, result); |  | ||||||
|   if (!success) |   if (!success) | ||||||
|     return {}; |     return {}; | ||||||
|   return result; |   return result; | ||||||
|   | |||||||
| @@ -63,22 +63,17 @@ bool parse_xiaomi_data_byte(uint8_t data_type, const uint8_t *data, uint8_t data | |||||||
|       return false; |       return false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &device) { | bool parse_xiaomi_service_data(XiaomiParseResult &result, const esp32_ble_tracker::ServiceData &service_data) { | ||||||
|   if (!device.get_service_data_uuid().has_value()) { |   if (!service_data.uuid.contains(0x95, 0xFE)) { | ||||||
|     // ESP_LOGVV(TAG, "Xiaomi no service data"); |  | ||||||
|     return {}; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (!device.get_service_data_uuid()->contains(0x95, 0xFE)) { |  | ||||||
|     // ESP_LOGVV(TAG, "Xiaomi no service data UUID magic bytes"); |     // ESP_LOGVV(TAG, "Xiaomi no service data UUID magic bytes"); | ||||||
|     return {}; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const auto *raw = reinterpret_cast<const uint8_t *>(device.get_service_data().data()); |   const auto raw = service_data.data; | ||||||
|  |  | ||||||
|   if (device.get_service_data().size() < 14) { |   if (raw.size() < 14) { | ||||||
|     // ESP_LOGVV(TAG, "Xiaomi service data too short!"); |     // ESP_LOGVV(TAG, "Xiaomi service data too short!"); | ||||||
|     return {}; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool is_lywsdcgq = (raw[1] & 0x20) == 0x20 && raw[2] == 0xAA && raw[3] == 0x01; |   bool is_lywsdcgq = (raw[1] & 0x20) == 0x20 && raw[2] == 0xAA && raw[3] == 0x01; | ||||||
| @@ -88,10 +83,9 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d | |||||||
|  |  | ||||||
|   if (!is_lywsdcgq && !is_hhccjcy01 && !is_lywsd02 && !is_cgg1) { |   if (!is_lywsdcgq && !is_hhccjcy01 && !is_lywsd02 && !is_cgg1) { | ||||||
|     // ESP_LOGVV(TAG, "Xiaomi no magic bytes"); |     // ESP_LOGVV(TAG, "Xiaomi no magic bytes"); | ||||||
|     return {}; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   XiaomiParseResult result; |  | ||||||
|   result.type = XiaomiParseResult::TYPE_HHCCJCY01; |   result.type = XiaomiParseResult::TYPE_HHCCJCY01; | ||||||
|   if (is_lywsdcgq) { |   if (is_lywsdcgq) { | ||||||
|     result.type = XiaomiParseResult::TYPE_LYWSDCGQ; |     result.type = XiaomiParseResult::TYPE_LYWSDCGQ; | ||||||
| @@ -111,7 +105,7 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d | |||||||
|  |  | ||||||
|   const uint8_t *raw_data = &raw[raw_offset]; |   const uint8_t *raw_data = &raw[raw_offset]; | ||||||
|   uint8_t data_offset = 0; |   uint8_t data_offset = 0; | ||||||
|   uint8_t data_length = device.get_service_data().size() - raw_offset; |   uint8_t data_length = raw.size() - raw_offset; | ||||||
|   bool success = false; |   bool success = false; | ||||||
|  |  | ||||||
|   while (true) { |   while (true) { | ||||||
| @@ -136,6 +130,15 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d | |||||||
|     data_offset += 3 + datapoint_length; |     data_offset += 3 + datapoint_length; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   return success; | ||||||
|  | } | ||||||
|  | optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &device) { | ||||||
|  |   XiaomiParseResult result; | ||||||
|  |   bool success = false; | ||||||
|  |   for (auto &service_data : device.get_service_datas()) { | ||||||
|  |     if (parse_xiaomi_service_data(result, service_data)) | ||||||
|  |       success = true; | ||||||
|  |   } | ||||||
|   if (!success) |   if (!success) | ||||||
|     return {}; |     return {}; | ||||||
|   return result; |   return result; | ||||||
|   | |||||||
| @@ -158,6 +158,7 @@ ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const ch | |||||||
|  |  | ||||||
| // Encode raw data to a human-readable string (for debugging) | // Encode raw data to a human-readable string (for debugging) | ||||||
| std::string hexencode(const uint8_t *data, uint32_t len); | std::string hexencode(const uint8_t *data, uint32_t len); | ||||||
|  | template<typename T> std::string hexencode(const T &data) { return hexencode(data.data(), data.size()); } | ||||||
|  |  | ||||||
| // https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971 | // https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971 | ||||||
| template<int...> struct seq {};                                       // NOLINT | template<int...> struct seq {};                                       // NOLINT | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user