ESPHome  2022.12.8
esp32_ble_tracker.cpp
Go to the documentation of this file.
1 #ifdef USE_ESP32
2 
3 #include "esp32_ble_tracker.h"
5 #include "esphome/core/defines.h"
6 #include "esphome/core/hal.h"
7 #include "esphome/core/helpers.h"
8 #include "esphome/core/log.h"
9 
10 #include <nvs_flash.h>
11 #include <freertos/FreeRTOSConfig.h>
12 #include <esp_bt_main.h>
13 #include <esp_bt.h>
14 #include <freertos/FreeRTOS.h>
15 #include <freertos/task.h>
16 #include <esp_gap_ble_api.h>
17 #include <esp_bt_defs.h>
18 
19 #ifdef USE_OTA
21 #endif
22 
23 #ifdef USE_ARDUINO
24 #include <esp32-hal-bt.h>
25 #endif
26 
27 // bt_trace.h
28 #undef TAG
29 
30 namespace esphome {
31 namespace esp32_ble_tracker {
32 
33 static const char *const TAG = "esp32_ble_tracker";
34 
35 ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
36 
37 uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) {
38  uint64_t u = 0;
39  u |= uint64_t(address[0] & 0xFF) << 40;
40  u |= uint64_t(address[1] & 0xFF) << 32;
41  u |= uint64_t(address[2] & 0xFF) << 24;
42  u |= uint64_t(address[3] & 0xFF) << 16;
43  u |= uint64_t(address[4] & 0xFF) << 8;
44  u |= uint64_t(address[5] & 0xFF) << 0;
45  return u;
46 }
47 
49 
51  global_esp32_ble_tracker = this;
52  this->scan_result_lock_ = xSemaphoreCreateMutex();
53  this->scan_end_lock_ = xSemaphoreCreateMutex();
54  this->scanner_idle_ = true;
56  this->mark_failed();
57  return;
58  }
59 
60 #ifdef USE_OTA
61  ota::global_ota_component->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) {
62  if (state == ota::OTA_STARTED) {
63  this->stop_scan();
64  }
65  });
66 #endif
67 
68  if (this->scan_continuous_) {
69  if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
70  this->start_scan_(true);
71  } else {
72  ESP_LOGW(TAG, "Cannot start scan!");
73  }
74  }
75 }
76 
78  BLEEvent *ble_event = this->ble_events_.pop();
79  while (ble_event != nullptr) {
80  if (ble_event->type_) {
81  this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if,
82  &ble_event->event_.gattc.gattc_param);
83  } else {
84  this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param);
85  }
86  delete ble_event; // NOLINT(cppcoreguidelines-owning-memory)
87  ble_event = this->ble_events_.pop();
88  }
89 
90  int connecting = 0;
91  int discovered = 0;
92  int searching = 0;
93  int disconnecting = 0;
94  for (auto *client : this->clients_) {
95  switch (client->state()) {
97  disconnecting++;
98  break;
100  discovered++;
101  break;
103  searching++;
104  break;
107  connecting++;
108  break;
109  default:
110  break;
111  }
112  }
113  bool promote_to_connecting = discovered && !searching && !connecting;
114 
115  if (!this->scanner_idle_) {
116  if (this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
117  xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) {
118  uint32_t index = this->scan_result_index_;
119  if (index) {
120  if (index >= 16) {
121  ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
122  }
123  for (size_t i = 0; i < index; i++) {
124  ESPBTDevice device;
125  device.parse_scan_rst(this->scan_result_buffer_[i]);
126 
127  bool found = false;
128  for (auto *listener : this->listeners_) {
129  if (listener->parse_device(device))
130  found = true;
131  }
132 
133  for (auto *client : this->clients_) {
134  if (client->parse_device(device)) {
135  found = true;
136  if (!connecting && client->state() == ClientState::DISCOVERED) {
137  promote_to_connecting = true;
138  }
139  }
140  }
141 
142  if (!found && !this->scan_continuous_) {
143  this->print_bt_device_info(device);
144  }
145  }
146  this->scan_result_index_ = 0;
147  }
148  xSemaphoreGive(this->scan_result_lock_);
149  }
150 
151  /*
152 
153  Avoid starting the scanner if:
154  - we are already scanning
155  - we are connecting to a device
156  - we are disconnecting from a device
157 
158  Otherwise the scanner could fail to ever start again
159  and our only way to recover is to reboot.
160 
161  https://github.com/espressif/esp-idf/issues/6688
162 
163  */
164  if (!connecting && !disconnecting && xSemaphoreTake(this->scan_end_lock_, 0L)) {
165  if (this->scan_continuous_) {
166  if (!promote_to_connecting && !this->scan_start_failed_ && !this->scan_set_param_failed_) {
167  this->start_scan_(false);
168  } else {
169  // We didn't start the scan, so we need to release the lock
170  xSemaphoreGive(this->scan_end_lock_);
171  }
172  } else if (!this->scanner_idle_) {
173  this->end_of_scan_();
174  return;
175  }
176  }
177 
178  if (this->scan_start_failed_ || this->scan_set_param_failed_) {
179  if (this->scan_start_fail_count_ == 255) {
180  ESP_LOGE(TAG, "ESP-IDF BLE scan could not restart after 255 attempts, rebooting to restore BLE stack...");
181  App.reboot();
182  }
183  if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
184  xSemaphoreGive(this->scan_end_lock_);
185  } else {
186  ESP_LOGD(TAG, "Stopping scan after failure...");
187  esp_ble_gap_stop_scanning();
188  this->cancel_timeout("scan");
189  }
190  if (this->scan_start_failed_) {
191  ESP_LOGE(TAG, "Scan start failed: %d", this->scan_start_failed_);
192  this->scan_start_failed_ = ESP_BT_STATUS_SUCCESS;
193  }
194  if (this->scan_set_param_failed_) {
195  ESP_LOGE(TAG, "Scan set param failed: %d", this->scan_set_param_failed_);
196  this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS;
197  }
198  }
199  }
200 
201  // If there is a discovered client and no connecting
202  // clients and no clients using the scanner to search for
203  // devices, then stop scanning and promote the discovered
204  // client to ready to connect.
205  if (promote_to_connecting) {
206  for (auto *client : this->clients_) {
207  if (client->state() == ClientState::DISCOVERED) {
208  if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
209  // Scanner is not running since we got the
210  // lock, so we can promote the client.
211  xSemaphoreGive(this->scan_end_lock_);
212  // We only want to promote one client at a time.
213  // once the scanner is fully stopped.
214  client->set_state(ClientState::READY_TO_CONNECT);
215  } else {
216  ESP_LOGD(TAG, "Pausing scan to make connection...");
217  esp_ble_gap_stop_scanning();
218  this->cancel_timeout("scan");
219  }
220  break;
221  }
222  }
223  }
224 }
225 
227  if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
228  this->start_scan_(true);
229  } else {
230  ESP_LOGW(TAG, "Scan requested when a scan is already in progress. Ignoring.");
231  }
232 }
233 
235  ESP_LOGD(TAG, "Stopping scan.");
236  this->scan_continuous_ = false;
237  esp_ble_gap_stop_scanning();
238  this->cancel_timeout("scan");
239 }
240 
242  // Initialize non-volatile storage for the bluetooth controller
243  esp_err_t err = nvs_flash_init();
244  if (err != ESP_OK) {
245  ESP_LOGE(TAG, "nvs_flash_init failed: %d", err);
246  return false;
247  }
248 
249 #ifdef USE_ARDUINO
250  if (!btStart()) {
251  ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status());
252  return false;
253  }
254 #else
255  if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
256  // start bt controller
257  if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
258  esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
259  err = esp_bt_controller_init(&cfg);
260  if (err != ESP_OK) {
261  ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err));
262  return false;
263  }
264  while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE)
265  ;
266  }
267  if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
268  err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
269  if (err != ESP_OK) {
270  ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err));
271  return false;
272  }
273  }
274  if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
275  ESP_LOGE(TAG, "esp bt controller enable failed");
276  return false;
277  }
278  }
279 #endif
280 
281  esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
282 
283  err = esp_bluedroid_init();
284  if (err != ESP_OK) {
285  ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err);
286  return false;
287  }
288  err = esp_bluedroid_enable();
289  if (err != ESP_OK) {
290  ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err);
291  return false;
292  }
293  err = esp_ble_gap_register_callback(ESP32BLETracker::gap_event_handler);
294  if (err != ESP_OK) {
295  ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
296  return false;
297  }
298  err = esp_ble_gattc_register_callback(ESP32BLETracker::gattc_event_handler);
299  if (err != ESP_OK) {
300  ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
301  return false;
302  }
303 
304  // Empty name
305  esp_ble_gap_set_device_name("");
306 
307  esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
308  err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
309  if (err != ESP_OK) {
310  ESP_LOGE(TAG, "esp_ble_gap_set_security_param failed: %d", err);
311  return false;
312  }
313 
314  // BLE takes some time to be fully set up, 200ms should be more than enough
315  delay(200); // NOLINT
316 
317  return true;
318 }
319 
321  // The lock must be held when calling this function.
322  if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
323  ESP_LOGE(TAG, "start_scan called without holding scan_end_lock_");
324  return;
325  }
326 
327  ESP_LOGD(TAG, "Starting scan...");
328  if (!first) {
329  for (auto *listener : this->listeners_)
330  listener->on_scan_end();
331  }
332  this->already_discovered_.clear();
333  this->scanner_idle_ = false;
334  this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE;
335  this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
336  this->scan_params_.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
337  this->scan_params_.scan_interval = this->scan_interval_;
338  this->scan_params_.scan_window = this->scan_window_;
339 
340  esp_ble_gap_set_scan_params(&this->scan_params_);
341  esp_ble_gap_start_scanning(this->scan_duration_);
342 
343  this->set_timeout("scan", this->scan_duration_ * 2000, []() {
344  ESP_LOGE(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack...");
345  App.reboot();
346  });
347 }
348 
350  // The lock must be held when calling this function.
351  if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
352  ESP_LOGE(TAG, "end_of_scan_ called without holding the scan_end_lock_");
353  return;
354  }
355 
356  ESP_LOGD(TAG, "End of scan.");
357  this->scanner_idle_ = true;
358  this->already_discovered_.clear();
359  xSemaphoreGive(this->scan_end_lock_);
360  this->cancel_timeout("scan");
361 
362  for (auto *listener : this->listeners_)
363  listener->on_scan_end();
364 }
365 
367  client->app_id = ++this->app_id_;
368  this->clients_.push_back(client);
369 }
370 
371 void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
372  BLEEvent *gap_event = new BLEEvent(event, param); // NOLINT(cppcoreguidelines-owning-memory)
373  global_esp32_ble_tracker->ble_events_.push(gap_event);
374 } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
375 
376 void ESP32BLETracker::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
377  switch (event) {
378  case ESP_GAP_BLE_SCAN_RESULT_EVT:
379  this->gap_scan_result_(param->scan_rst);
380  break;
381  case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
382  this->gap_scan_set_param_complete_(param->scan_param_cmpl);
383  break;
384  case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
385  this->gap_scan_start_complete_(param->scan_start_cmpl);
386  break;
387  case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
388  this->gap_scan_stop_complete_(param->scan_stop_cmpl);
389  break;
390  default:
391  break;
392  }
393  for (auto *client : this->clients_) {
394  client->gap_event_handler(event, param);
395  }
396 }
397 
398 void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param &param) {
399  this->scan_set_param_failed_ = param.status;
400 }
401 
402 void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param &param) {
403  this->scan_start_failed_ = param.status;
404  if (param.status == ESP_BT_STATUS_SUCCESS) {
405  this->scan_start_fail_count_ = 0;
406  } else {
407  this->scan_start_fail_count_++;
408  xSemaphoreGive(this->scan_end_lock_);
409  }
410 }
411 
412 void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param &param) {
413  xSemaphoreGive(this->scan_end_lock_);
414 }
415 
416 void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
417  if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
418  if (xSemaphoreTake(this->scan_result_lock_, 0L)) {
419  if (this->scan_result_index_ < 16) {
420  this->scan_result_buffer_[this->scan_result_index_++] = param;
421  }
422  xSemaphoreGive(this->scan_result_lock_);
423  }
424  } else if (param.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
425  xSemaphoreGive(this->scan_end_lock_);
426  }
427 }
428 
429 void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
430  esp_ble_gattc_cb_param_t *param) {
431  BLEEvent *gattc_event = new BLEEvent(event, gattc_if, param); // NOLINT(cppcoreguidelines-owning-memory)
432  global_esp32_ble_tracker->ble_events_.push(gattc_event);
433 } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
434 
435 void ESP32BLETracker::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
436  esp_ble_gattc_cb_param_t *param) {
437  for (auto *client : this->clients_) {
438  client->gattc_event_handler(event, gattc_if, param);
439  }
440 }
441 
442 ESPBTUUID::ESPBTUUID() : uuid_() {}
444  ESPBTUUID ret;
445  ret.uuid_.len = ESP_UUID_LEN_16;
446  ret.uuid_.uuid.uuid16 = uuid;
447  return ret;
448 }
450  ESPBTUUID ret;
451  ret.uuid_.len = ESP_UUID_LEN_32;
452  ret.uuid_.uuid.uuid32 = uuid;
453  return ret;
454 }
455 ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) {
456  ESPBTUUID ret;
457  ret.uuid_.len = ESP_UUID_LEN_128;
458  for (size_t i = 0; i < ESP_UUID_LEN_128; i++)
459  ret.uuid_.uuid.uuid128[i] = data[i];
460  return ret;
461 }
462 ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
463  ESPBTUUID ret;
464  if (data.length() == 4) {
465  ret.uuid_.len = ESP_UUID_LEN_16;
466  ret.uuid_.uuid.uuid16 = 0;
467  for (int i = 0; i < data.length();) {
468  uint8_t msb = data.c_str()[i];
469  uint8_t lsb = data.c_str()[i + 1];
470 
471  if (msb > '9')
472  msb -= 7;
473  if (lsb > '9')
474  lsb -= 7;
475  ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (2 - i) * 4;
476  i += 2;
477  }
478  } else if (data.length() == 8) {
479  ret.uuid_.len = ESP_UUID_LEN_32;
480  ret.uuid_.uuid.uuid32 = 0;
481  for (int i = 0; i < data.length();) {
482  uint8_t msb = data.c_str()[i];
483  uint8_t lsb = data.c_str()[i + 1];
484 
485  if (msb > '9')
486  msb -= 7;
487  if (lsb > '9')
488  lsb -= 7;
489  ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (6 - i) * 4;
490  i += 2;
491  }
492  } else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be
493  // investigated (lack of time)
494  ret.uuid_.len = ESP_UUID_LEN_128;
495  memcpy(ret.uuid_.uuid.uuid128, (uint8_t *) data.data(), 16);
496  } else if (data.length() == 36) {
497  // If the length of the string is 36 bytes then we will assume it is a long hex string in
498  // UUID format.
499  ret.uuid_.len = ESP_UUID_LEN_128;
500  int n = 0;
501  for (int i = 0; i < data.length();) {
502  if (data.c_str()[i] == '-')
503  i++;
504  uint8_t msb = data.c_str()[i];
505  uint8_t lsb = data.c_str()[i + 1];
506 
507  if (msb > '9')
508  msb -= 7;
509  if (lsb > '9')
510  lsb -= 7;
511  ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F);
512  i += 2;
513  }
514  } else {
515  ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data.c_str());
516  }
517  return ret;
518 }
519 ESPBTUUID ESPBTUUID::from_uuid(esp_bt_uuid_t uuid) {
520  ESPBTUUID ret;
521  ret.uuid_.len = uuid.len;
522  if (uuid.len == ESP_UUID_LEN_16) {
523  ret.uuid_.uuid.uuid16 = uuid.uuid.uuid16;
524  } else if (uuid.len == ESP_UUID_LEN_32) {
525  ret.uuid_.uuid.uuid32 = uuid.uuid.uuid32;
526  } else if (uuid.len == ESP_UUID_LEN_128) {
527  memcpy(ret.uuid_.uuid.uuid128, uuid.uuid.uuid128, ESP_UUID_LEN_128);
528  }
529  return ret;
530 }
532  if (this->uuid_.len == ESP_UUID_LEN_128) {
533  return *this;
534  }
535  uint8_t data[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
536  uint32_t uuid32;
537  if (this->uuid_.len == ESP_UUID_LEN_32) {
538  uuid32 = this->uuid_.uuid.uuid32;
539  } else {
540  uuid32 = this->uuid_.uuid.uuid16;
541  }
542  for (uint8_t i = 0; i < this->uuid_.len; i++) {
543  data[12 + i] = ((uuid32 >> i * 8) & 0xFF);
544  }
545  return ESPBTUUID::from_raw(data);
546 }
547 bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const {
548  if (this->uuid_.len == ESP_UUID_LEN_16) {
549  return (this->uuid_.uuid.uuid16 >> 8) == data2 && (this->uuid_.uuid.uuid16 & 0xFF) == data1;
550  } else if (this->uuid_.len == ESP_UUID_LEN_32) {
551  for (uint8_t i = 0; i < 3; i++) {
552  bool a = ((this->uuid_.uuid.uuid32 >> i * 8) & 0xFF) == data1;
553  bool b = ((this->uuid_.uuid.uuid32 >> (i + 1) * 8) & 0xFF) == data2;
554  if (a && b)
555  return true;
556  }
557  } else {
558  for (uint8_t i = 0; i < 15; i++) {
559  if (this->uuid_.uuid.uuid128[i] == data1 && this->uuid_.uuid.uuid128[i + 1] == data2)
560  return true;
561  }
562  }
563  return false;
564 }
565 bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
566  if (this->uuid_.len == uuid.uuid_.len) {
567  switch (this->uuid_.len) {
568  case ESP_UUID_LEN_16:
569  if (uuid.uuid_.uuid.uuid16 == this->uuid_.uuid.uuid16) {
570  return true;
571  }
572  break;
573  case ESP_UUID_LEN_32:
574  if (uuid.uuid_.uuid.uuid32 == this->uuid_.uuid.uuid32) {
575  return true;
576  }
577  break;
578  case ESP_UUID_LEN_128:
579  for (int i = 0; i < ESP_UUID_LEN_128; i++) {
580  if (uuid.uuid_.uuid.uuid128[i] != this->uuid_.uuid.uuid128[i]) {
581  return false;
582  }
583  }
584  return true;
585  break;
586  }
587  } else {
588  return this->as_128bit() == uuid.as_128bit();
589  }
590  return false;
591 }
592 esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; }
593 std::string ESPBTUUID::to_string() const {
594  switch (this->uuid_.len) {
595  case ESP_UUID_LEN_16:
596  return str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
597  case ESP_UUID_LEN_32:
598  return str_snprintf("0x%02X%02X%02X%02X", 10, this->uuid_.uuid.uuid32 >> 24,
599  (this->uuid_.uuid.uuid32 >> 16 & 0xff), (this->uuid_.uuid.uuid32 >> 8 & 0xff),
600  this->uuid_.uuid.uuid32 & 0xff);
601  default:
602  case ESP_UUID_LEN_128:
603  std::string buf;
604  for (int8_t i = 15; i >= 0; i--) {
605  buf += str_snprintf("%02X", 2, this->uuid_.uuid.uuid128[i]);
606  if (i == 6 || i == 8 || i == 10 || i == 12)
607  buf += "-";
608  }
609  return buf;
610  }
611  return "";
612 }
613 
614 uint64_t ESPBTUUID::get_128bit_high() const {
615  esp_bt_uuid_t uuid = this->as_128bit().get_uuid();
616  return ((uint64_t) uuid.uuid.uuid128[15] << 56) | ((uint64_t) uuid.uuid.uuid128[14] << 48) |
617  ((uint64_t) uuid.uuid.uuid128[13] << 40) | ((uint64_t) uuid.uuid.uuid128[12] << 32) |
618  ((uint64_t) uuid.uuid.uuid128[11] << 24) | ((uint64_t) uuid.uuid.uuid128[10] << 16) |
619  ((uint64_t) uuid.uuid.uuid128[9] << 8) | ((uint64_t) uuid.uuid.uuid128[8]);
620 }
621 uint64_t ESPBTUUID::get_128bit_low() const {
622  esp_bt_uuid_t uuid = this->as_128bit().get_uuid();
623  return ((uint64_t) uuid.uuid.uuid128[7] << 56) | ((uint64_t) uuid.uuid.uuid128[6] << 48) |
624  ((uint64_t) uuid.uuid.uuid128[5] << 40) | ((uint64_t) uuid.uuid.uuid128[4] << 32) |
625  ((uint64_t) uuid.uuid.uuid128[3] << 24) | ((uint64_t) uuid.uuid.uuid128[2] << 16) |
626  ((uint64_t) uuid.uuid.uuid128[1] << 8) | ((uint64_t) uuid.uuid.uuid128[0]);
627 }
628 
629 ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
631  if (!data.uuid.contains(0x4C, 0x00))
632  return {};
633 
634  if (data.data.size() != 23)
635  return {};
636  return ESPBLEiBeacon(data.data.data());
637 }
638 
639 void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
640  this->scan_result_ = param;
641  for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
642  this->address_[i] = param.bda[i];
643  this->address_type_ = param.ble_addr_type;
644  this->rssi_ = param.rssi;
645  this->parse_adv_(param);
646 
647 #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
648  ESP_LOGVV(TAG, "Parse Result:");
649  const char *address_type = "";
650  switch (this->address_type_) {
651  case BLE_ADDR_TYPE_PUBLIC:
652  address_type = "PUBLIC";
653  break;
654  case BLE_ADDR_TYPE_RANDOM:
655  address_type = "RANDOM";
656  break;
657  case BLE_ADDR_TYPE_RPA_PUBLIC:
658  address_type = "RPA_PUBLIC";
659  break;
660  case BLE_ADDR_TYPE_RPA_RANDOM:
661  address_type = "RPA_RANDOM";
662  break;
663  }
664  ESP_LOGVV(TAG, " Address: %02X:%02X:%02X:%02X:%02X:%02X (%s)", this->address_[0], this->address_[1],
665  this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type);
666 
667  ESP_LOGVV(TAG, " RSSI: %d", this->rssi_);
668  ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str());
669  for (auto &it : this->tx_powers_) {
670  ESP_LOGVV(TAG, " TX Power: %d", it);
671  }
672  if (this->appearance_.has_value()) {
673  ESP_LOGVV(TAG, " Appearance: %u", *this->appearance_);
674  }
675  if (this->ad_flag_.has_value()) {
676  ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_);
677  }
678  for (auto &uuid : this->service_uuids_) {
679  ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str());
680  }
681  for (auto &data : this->manufacturer_datas_) {
682  ESP_LOGVV(TAG, " Manufacturer data: %s", format_hex_pretty(data.data).c_str());
683  if (this->get_ibeacon().has_value()) {
684  auto ibeacon = this->get_ibeacon().value();
685  ESP_LOGVV(TAG, " iBeacon data:");
686  ESP_LOGVV(TAG, " UUID: %s", ibeacon.get_uuid().to_string().c_str());
687  ESP_LOGVV(TAG, " Major: %u", ibeacon.get_major());
688  ESP_LOGVV(TAG, " Minor: %u", ibeacon.get_minor());
689  ESP_LOGVV(TAG, " TXPower: %d", ibeacon.get_signal_power());
690  }
691  }
692  for (auto &data : this->service_datas_) {
693  ESP_LOGVV(TAG, " Service data:");
694  ESP_LOGVV(TAG, " UUID: %s", data.uuid.to_string().c_str());
695  ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str());
696  }
697 
698  ESP_LOGVV(TAG, "Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
699 #endif
700 }
701 void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
702  size_t offset = 0;
703  const uint8_t *payload = param.ble_adv;
704  uint8_t len = param.adv_data_len + param.scan_rsp_len;
705 
706  while (offset + 2 < len) {
707  const uint8_t field_length = payload[offset++]; // First byte is length of adv record
708  if (field_length == 0) {
709  continue; // Possible zero padded advertisement data
710  }
711 
712  // first byte of adv record is adv record type
713  const uint8_t record_type = payload[offset++];
714  const uint8_t *record = &payload[offset];
715  const uint8_t record_length = field_length - 1;
716  offset += record_length;
717 
718  // See also Generic Access Profile Assigned Numbers:
719  // https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/ See also ADVERTISING AND SCAN
720  // RESPONSE DATA FORMAT: https://www.bluetooth.com/specifications/bluetooth-core-specification/ (vol 3, part C, 11)
721  // See also Core Specification Supplement: https://www.bluetooth.com/specifications/bluetooth-core-specification/
722  // (called CSS here)
723 
724  switch (record_type) {
725  case ESP_BLE_AD_TYPE_NAME_SHORT:
726  case ESP_BLE_AD_TYPE_NAME_CMPL: {
727  // CSS 1.2 LOCAL NAME
728  // "The Local Name data type shall be the same as, or a shortened version of, the local name assigned to the
729  // device." CSS 1: Optional in this context; shall not appear more than once in a block.
730  // SHORTENED LOCAL NAME
731  // "The Shortened Local Name data type defines a shortened version of the Local Name data type. The Shortened
732  // Local Name data type shall not be used to advertise a name that is longer than the Local Name data type."
733  if (record_length > this->name_.length()) {
734  this->name_ = std::string(reinterpret_cast<const char *>(record), record_length);
735  }
736  break;
737  }
738  case ESP_BLE_AD_TYPE_TX_PWR: {
739  // CSS 1.5 TX POWER LEVEL
740  // "The TX Power Level data type indicates the transmitted power level of the packet containing the data type."
741  // CSS 1: Optional in this context (may appear more than once in a block).
742  this->tx_powers_.push_back(*payload);
743  break;
744  }
745  case ESP_BLE_AD_TYPE_APPEARANCE: {
746  // CSS 1.12 APPEARANCE
747  // "The Appearance data type defines the external appearance of the device."
748  // See also https://www.bluetooth.com/specifications/gatt/characteristics/
749  // CSS 1: Optional in this context; shall not appear more than once in a block and shall not appear in both
750  // the AD and SRD of the same extended advertising interval.
751  this->appearance_ = *reinterpret_cast<const uint16_t *>(record);
752  break;
753  }
754  case ESP_BLE_AD_TYPE_FLAG: {
755  // CSS 1.3 FLAGS
756  // "The Flags data type contains one bit Boolean flags. The Flags data type shall be included when any of the
757  // Flag bits are non-zero and the advertising packet is connectable, otherwise the Flags data type may be
758  // omitted."
759  // CSS 1: Optional in this context; shall not appear more than once in a block.
760  this->ad_flag_ = *record;
761  break;
762  }
763  // CSS 1.1 SERVICE UUID
764  // The Service UUID data type is used to include a list of Service or Service Class UUIDs.
765  // There are six data types defined for the three sizes of Service UUIDs that may be returned:
766  // CSS 1: Optional in this context (may appear more than once in a block).
767  case ESP_BLE_AD_TYPE_16SRV_CMPL:
768  case ESP_BLE_AD_TYPE_16SRV_PART: {
769  // • 16-bit Bluetooth Service UUIDs
770  for (uint8_t i = 0; i < record_length / 2; i++) {
771  this->service_uuids_.push_back(ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record + 2 * i)));
772  }
773  break;
774  }
775  case ESP_BLE_AD_TYPE_32SRV_CMPL:
776  case ESP_BLE_AD_TYPE_32SRV_PART: {
777  // • 32-bit Bluetooth Service UUIDs
778  for (uint8_t i = 0; i < record_length / 4; i++) {
779  this->service_uuids_.push_back(ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record + 4 * i)));
780  }
781  break;
782  }
783  case ESP_BLE_AD_TYPE_128SRV_CMPL:
784  case ESP_BLE_AD_TYPE_128SRV_PART: {
785  // • Global 128-bit Service UUIDs
786  this->service_uuids_.push_back(ESPBTUUID::from_raw(record));
787  break;
788  }
789  case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: {
790  // CSS 1.4 MANUFACTURER SPECIFIC DATA
791  // "The Manufacturer Specific data type is used for manufacturer specific data. The first two data octets shall
792  // contain a company identifier from Assigned Numbers. The interpretation of any other octets within the data
793  // shall be defined by the manufacturer specified by the company identifier."
794  // CSS 1: Optional in this context (may appear more than once in a block).
795  if (record_length < 2) {
796  ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE");
797  break;
798  }
799  ServiceData data{};
800  data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record));
801  data.data.assign(record + 2UL, record + record_length);
802  this->manufacturer_datas_.push_back(data);
803  break;
804  }
805 
806  // CSS 1.11 SERVICE DATA
807  // "The Service Data data type consists of a service UUID with the data associated with that service."
808  // CSS 1: Optional in this context (may appear more than once in a block).
809  case ESP_BLE_AD_TYPE_SERVICE_DATA: {
810  // «Service Data - 16 bit UUID»
811  // Size: 2 or more octets
812  // The first 2 octets contain the 16 bit Service UUID fol- lowed by additional service data
813  if (record_length < 2) {
814  ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
815  break;
816  }
817  ServiceData data{};
818  data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast<const uint16_t *>(record));
819  data.data.assign(record + 2UL, record + record_length);
820  this->service_datas_.push_back(data);
821  break;
822  }
823  case ESP_BLE_AD_TYPE_32SERVICE_DATA: {
824  // «Service Data - 32 bit UUID»
825  // Size: 4 or more octets
826  // The first 4 octets contain the 32 bit Service UUID fol- lowed by additional service data
827  if (record_length < 4) {
828  ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
829  break;
830  }
831  ServiceData data{};
832  data.uuid = ESPBTUUID::from_uint32(*reinterpret_cast<const uint32_t *>(record));
833  data.data.assign(record + 4UL, record + record_length);
834  this->service_datas_.push_back(data);
835  break;
836  }
837  case ESP_BLE_AD_TYPE_128SERVICE_DATA: {
838  // «Service Data - 128 bit UUID»
839  // Size: 16 or more octets
840  // The first 16 octets contain the 128 bit Service UUID followed by additional service data
841  if (record_length < 16) {
842  ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
843  break;
844  }
845  ServiceData data{};
846  data.uuid = ESPBTUUID::from_raw(record);
847  data.data.assign(record + 16UL, record + record_length);
848  this->service_datas_.push_back(data);
849  break;
850  }
851  case ESP_BLE_AD_TYPE_INT_RANGE:
852  // Avoid logging this as it's very verbose
853  break;
854  default: {
855  ESP_LOGV(TAG, "Unhandled type: advType: 0x%02x", record_type);
856  break;
857  }
858  }
859  }
860 }
861 std::string ESPBTDevice::address_str() const {
862  char mac[24];
863  snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2],
864  this->address_[3], this->address_[4], this->address_[5]);
865  return mac;
866 }
867 uint64_t ESPBTDevice::address_uint64() const { return ble_addr_to_uint64(this->address_); }
868 
870  ESP_LOGCONFIG(TAG, "BLE Tracker:");
871  ESP_LOGCONFIG(TAG, " Scan Duration: %u s", this->scan_duration_);
872  ESP_LOGCONFIG(TAG, " Scan Interval: %.1f ms", this->scan_interval_ * 0.625f);
873  ESP_LOGCONFIG(TAG, " Scan Window: %.1f ms", this->scan_window_ * 0.625f);
874  ESP_LOGCONFIG(TAG, " Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE");
875  ESP_LOGCONFIG(TAG, " Continuous Scanning: %s", this->scan_continuous_ ? "True" : "False");
876 }
877 
879  const uint64_t address = device.address_uint64();
880  for (auto &disc : this->already_discovered_) {
881  if (disc == address)
882  return;
883  }
884  this->already_discovered_.push_back(address);
885 
886  ESP_LOGD(TAG, "Found device %s RSSI=%d", device.address_str().c_str(), device.get_rssi());
887 
888  const char *address_type_s;
889  switch (device.get_address_type()) {
890  case BLE_ADDR_TYPE_PUBLIC:
891  address_type_s = "PUBLIC";
892  break;
893  case BLE_ADDR_TYPE_RANDOM:
894  address_type_s = "RANDOM";
895  break;
896  case BLE_ADDR_TYPE_RPA_PUBLIC:
897  address_type_s = "RPA_PUBLIC";
898  break;
899  case BLE_ADDR_TYPE_RPA_RANDOM:
900  address_type_s = "RPA_RANDOM";
901  break;
902  default:
903  address_type_s = "UNKNOWN";
904  break;
905  }
906 
907  ESP_LOGD(TAG, " Address Type: %s", address_type_s);
908  if (!device.get_name().empty()) {
909  ESP_LOGD(TAG, " Name: '%s'", device.get_name().c_str());
910  }
911  for (auto &tx_power : device.get_tx_powers()) {
912  ESP_LOGD(TAG, " TX Power: %d", tx_power);
913  }
914 }
915 
916 } // namespace esp32_ble_tracker
917 } // namespace esphome
918 
919 #endif
void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
void end_of_scan_()
Called when a scan ends.
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition: helpers.cpp:237
bool cancel_timeout(const std::string &name)
Cancel a timeout function.
Definition: component.cpp:72
void setup() override
Setup the FreeRTOS task and the Bluetooth stack.
void start_scan_(bool first)
Start a single scan by setting up the parameters and doing some esp-idf calls.
esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_buffer_[16]
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:68
void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param)
void add_on_state_callback(std::function< void(OTAState, float, uint8_t)> &&callback)
const std::vector< int8_t > & get_tx_powers() const
esp_ble_scan_params_t scan_params_
A structure holding the ESP BLE scan parameters.
static ESPBTUUID from_raw(const uint8_t *data)
void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
ESP32BLETracker * global_esp32_ble_tracker
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
Callback that will handle all GAP events and redistribute them to other callbacks.
struct esphome::esp32_ble_tracker::BLEEvent::@61::gattc_event gattc
Application App
Global storage of Application pointer - only one Application can exist.
esp_ble_addr_type_t get_address_type() const
union esphome::esp32_ble_tracker::BLEEvent::@61 event_
void gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param &param)
Called when a ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT event is received.
OTAComponent * global_ota_component
bool contains(uint8_t data1, uint8_t data2) const
struct esphome::esp32_ble_tracker::BLEEvent::@61::gap_event gap
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
Callback that will handle all GATTC events and redistribute them to other callbacks.
uint32_t scan_duration_
The interval in seconds to perform scans.
std::string size_t len
Definition: helpers.h:281
std::vector< uint64_t > already_discovered_
Vector of addresses that have already been printed in print_bt_device_info.
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:112
void gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param)
Called when a ESP_GAP_BLE_SCAN_RESULT_EVT event is received.
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address)
static ESPBTUUID from_uint16(uint16_t uuid)
static ESPBTUUID from_uint32(uint32_t uuid)
Definition: a4988.cpp:4
void print_bt_device_info(const ESPBTDevice &device)
const std::string & get_name() const
static bool ble_setup()
The FreeRTOS task managing the bluetooth interface.
bool operator==(const ESPBTUUID &uuid) const
std::string str_snprintf(const char *fmt, size_t len,...)
Definition: helpers.cpp:174
void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param &param)
Called when a ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT event is received.
static optional< ESPBLEiBeacon > from_manufacturer_data(const ServiceData &data)
std::vector< ESPBTDeviceListener * > listeners_
std::vector< ESPBTClient * > clients_
Client parameters.
void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param)
void gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param &param)
Called when a ESP_GAP_BLE_SCAN_START_COMPLETE_EVT event is received.
bool state
Definition: fan.h:34
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)