ESPHome 2026.4.0
Loading...
Searching...
No Matches
ble.cpp
Go to the documentation of this file.
1#include "ble.h"
2
3#ifdef USE_ESP32
4
7#include "esphome/core/log.h"
8
9#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
10#include <esp_bt.h>
11#else
12extern "C" {
13#include <esp_hosted.h>
14#include <esp_hosted_misc.h>
15#include <esp_hosted_bluedroid.h>
16}
17#endif
18#include <esp_bt_device.h>
19#include <esp_bt_main.h>
20#include <esp_gap_ble_api.h>
21#include <freertos/FreeRTOS.h>
22#include <freertos/FreeRTOSConfig.h>
23#include <freertos/task.h>
24#include <nvs_flash.h>
25
26#ifdef USE_ARDUINO
27// Prevent Arduino from releasing BT memory at startup (esp32-hal-misc.c).
28// Without this, esp_bt_controller_init() fails with ESP_ERR_INVALID_STATE.
29extern "C" bool btInUse() { return true; } // NOLINT(readability-identifier-naming)
30#endif
31
33
34static const char *const TAG = "esp32_ble";
35
36// GAP event groups for deduplication across gap_event_handler and dispatch_gap_event_
37#define GAP_SCAN_COMPLETE_EVENTS \
38 case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: \
39 case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: \
40 case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT
41
42#define GAP_ADV_COMPLETE_EVENTS \
43 case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: \
44 case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: \
45 case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: \
46 case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: \
47 case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT
48
49#define GAP_SECURITY_EVENTS \
50 case ESP_GAP_BLE_AUTH_CMPL_EVT: \
51 case ESP_GAP_BLE_SEC_REQ_EVT: \
52 case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: \
53 case ESP_GAP_BLE_PASSKEY_REQ_EVT: \
54 case ESP_GAP_BLE_NC_REQ_EVT
55
57 global_ble = this;
58 if (!ble_pre_setup_()) {
59 ESP_LOGE(TAG, "BLE could not be prepared for configuration");
60 this->mark_failed();
61 return;
62 }
63
64 this->state_ = BLE_COMPONENT_STATE_DISABLED;
65 if (this->enable_on_boot_) {
66 this->enable();
67 }
68}
69
71 if (this->state_ != BLE_COMPONENT_STATE_DISABLED)
72 return;
73
74 this->state_ = BLE_COMPONENT_STATE_ENABLE;
75}
76
78 if (this->state_ == BLE_COMPONENT_STATE_DISABLED)
79 return;
80
81 this->state_ = BLE_COMPONENT_STATE_DISABLE;
82}
83
84#ifdef USE_ESP32_BLE_ADVERTISING
86 this->advertising_init_();
87 if (!this->is_active())
88 return;
89 this->advertising_->start();
90}
91
92void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
93 this->advertising_init_();
94 this->advertising_->set_service_data(data);
95 this->advertising_start();
96}
97
98void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
99 this->advertising_init_();
100 this->advertising_->set_manufacturer_data(data);
101 this->advertising_start();
102}
103
104void ESP32BLE::advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name) {
105 // This method atomically updates both service data and device name inclusion in BLE advertising.
106 // When include_name is true, the device name is included in the advertising packet making it
107 // visible to passive BLE scanners. When false, the name is only visible in scan response
108 // (requires active scanning). This atomic operation ensures we only restart advertising once
109 // when changing both properties, avoiding the brief gap that would occur with separate calls.
110
111 this->advertising_init_();
112
113 if (include_name) {
114 // When including name, clear service data first to avoid packet overflow
115 this->advertising_->set_service_data(std::span<const uint8_t>{});
116 this->advertising_->set_include_name(true);
117 } else {
118 // When including service data, clear name first to avoid packet overflow
119 this->advertising_->set_include_name(false);
120 this->advertising_->set_service_data(data);
121 }
122
123 this->advertising_start();
124}
125
126void ESP32BLE::advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback) {
127 this->advertising_init_();
128 this->advertising_->register_raw_advertisement_callback(std::move(callback));
129}
130
132 this->advertising_init_();
133 this->advertising_->add_service_uuid(uuid);
134 this->advertising_start();
135}
136
138 this->advertising_init_();
139 this->advertising_->remove_service_uuid(uuid);
140 this->advertising_start();
141}
142#endif
143
145 esp_err_t err = nvs_flash_init();
146 if (err != ESP_OK) {
147 ESP_LOGE(TAG, "nvs_flash_init failed: %d", err);
148 return false;
149 }
150 return true;
151}
152
153#ifdef USE_ESP32_BLE_ADVERTISING
155 if (this->advertising_ != nullptr)
156 return;
157 this->advertising_ = new BLEAdvertising(this->advertising_cycle_time_); // NOLINT(cppcoreguidelines-owning-memory)
158
159 this->advertising_->set_scan_response(true);
160 this->advertising_->set_min_preferred_interval(0x06);
161 this->advertising_->set_appearance(this->appearance_);
162}
163#endif
164
166 esp_err_t err;
167#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
168 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
169 // start bt controller
170 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
171 esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
172 err = esp_bt_controller_init(&cfg);
173 if (err != ESP_OK) {
174 ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err));
175 return false;
176 }
177 while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE)
178 ;
179 }
180 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
181 err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
182 if (err != ESP_OK) {
183 ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err));
184 return false;
185 }
186 }
187 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
188 ESP_LOGE(TAG, "esp bt controller enable failed");
189 return false;
190 }
191 }
192
193 esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
194#else
195 esp_hosted_connect_to_slave(); // NOLINT
196
197 if (esp_hosted_bt_controller_init() != ESP_OK) {
198 ESP_LOGW(TAG, "esp_hosted_bt_controller_init failed");
199 return false;
200 }
201
202 if (esp_hosted_bt_controller_enable() != ESP_OK) {
203 ESP_LOGW(TAG, "esp_hosted_bt_controller_enable failed");
204 return false;
205 }
206
207 hosted_hci_bluedroid_open();
208
209 esp_bluedroid_hci_driver_operations_t operations = {
210 .send = hosted_hci_bluedroid_send,
211 .check_send_available = hosted_hci_bluedroid_check_send_available,
212 .register_host_callback = hosted_hci_bluedroid_register_host_callback,
213 };
214 esp_bluedroid_attach_hci_driver(&operations);
215#endif
216
217 err = esp_bluedroid_init();
218 if (err != ESP_OK) {
219 ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err);
220 return false;
221 }
222 err = esp_bluedroid_enable();
223 if (err != ESP_OK) {
224 ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err);
225 return false;
226 }
227
228#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
229 err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler);
230 if (err != ESP_OK) {
231 ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
232 return false;
233 }
234#endif
235
236#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
237 err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler);
238 if (err != ESP_OK) {
239 ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err);
240 return false;
241 }
242#endif
243
244#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
245 err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler);
246 if (err != ESP_OK) {
247 ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
248 return false;
249 }
250#endif
251
252 // BLE device names are limited to 20 characters
253 // Buffer: 20 chars + null terminator
254 constexpr size_t ble_name_max_len = 21;
255 char name_buffer[ble_name_max_len];
256 const char *device_name;
257
258 if (this->name_ != nullptr) {
260 // MAC address length: 12 hex chars + null terminator
261 constexpr size_t mac_address_len = 13;
262 // MAC address suffix length (last 6 characters of 12-char MAC address string)
263 constexpr size_t mac_address_suffix_len = 6;
264 char mac_addr[mac_address_len];
266 const char *mac_suffix_ptr = mac_addr + mac_address_suffix_len;
267 make_name_with_suffix_to(name_buffer, sizeof(name_buffer), this->name_, strlen(this->name_), '-', mac_suffix_ptr,
268 mac_address_suffix_len);
269 device_name = name_buffer;
270 } else {
271 device_name = this->name_;
272 }
273 } else {
274 const auto &app_name = App.get_name();
275 size_t name_len = app_name.length();
276 if (name_len > 20) {
278 // Keep first 13 chars and last 7 chars (MAC suffix), remove middle
279 memcpy(name_buffer, app_name.c_str(), 13);
280 memcpy(name_buffer + 13, app_name.c_str() + name_len - 7, 7);
281 } else {
282 memcpy(name_buffer, app_name.c_str(), 20);
283 }
284 name_buffer[20] = '\0';
285 } else {
286 memcpy(name_buffer, app_name.c_str(), name_len + 1); // Include null terminator
287 }
288 device_name = name_buffer;
289 }
290
291 err = esp_ble_gap_set_device_name(device_name);
292 if (err != ESP_OK) {
293 ESP_LOGE(TAG, "esp_ble_gap_set_device_name failed: %d", err);
294 return false;
295 }
296
297 err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &(this->io_cap_), sizeof(esp_ble_io_cap_t));
298 if (err != ESP_OK) {
299 ESP_LOGE(TAG, "esp_ble_gap_set_security_param iocap_mode failed: %d", err);
300 return false;
301 }
302
303#ifdef ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
304 if (this->max_key_size_) {
305 err = esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &(this->max_key_size_), sizeof(uint8_t));
306 if (err != ESP_OK) {
307 ESP_LOGE(TAG, "esp_ble_gap_set_security_param max_key_size failed: %d", err);
308 return false;
309 }
310 }
311
312 if (this->min_key_size_) {
313 err = esp_ble_gap_set_security_param(ESP_BLE_SM_MIN_KEY_SIZE, &(this->min_key_size_), sizeof(uint8_t));
314 if (err != ESP_OK) {
315 ESP_LOGE(TAG, "esp_ble_gap_set_security_param min_key_size failed: %d", err);
316 return false;
317 }
318 }
319
320 if (this->auth_req_mode_) {
321 err = esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &(this->auth_req_mode_.value()),
322 sizeof(esp_ble_auth_req_t));
323 if (err != ESP_OK) {
324 ESP_LOGE(TAG, "esp_ble_gap_set_security_param authen_req_mode failed: %d", err);
325 return false;
326 }
327 }
328#endif // ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
329
330 // BLE takes some time to be fully set up, 200ms should be more than enough
331 delay(200); // NOLINT
332
333 return true;
334}
335
337 esp_err_t err = esp_bluedroid_disable();
338 if (err != ESP_OK) {
339 // ESP_ERR_INVALID_STATE means Bluedroid is already disabled, which is fine
340 if (err != ESP_ERR_INVALID_STATE) {
341 ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", err);
342 return false;
343 }
344 ESP_LOGD(TAG, "Already disabled");
345 }
346 err = esp_bluedroid_deinit();
347 if (err != ESP_OK) {
348 // ESP_ERR_INVALID_STATE means Bluedroid is already deinitialized, which is fine
349 if (err != ESP_ERR_INVALID_STATE) {
350 ESP_LOGE(TAG, "esp_bluedroid_deinit failed: %d", err);
351 return false;
352 }
353 ESP_LOGD(TAG, "Already deinitialized");
354 }
355
356#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
357 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
358 // stop bt controller
359 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
360 err = esp_bt_controller_disable();
361 if (err != ESP_OK) {
362 ESP_LOGE(TAG, "esp_bt_controller_disable failed: %s", esp_err_to_name(err));
363 return false;
364 }
365 while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED)
366 ;
367 }
368 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
369 err = esp_bt_controller_deinit();
370 if (err != ESP_OK) {
371 ESP_LOGE(TAG, "esp_bt_controller_deinit failed: %s", esp_err_to_name(err));
372 return false;
373 }
374 }
375 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
376 ESP_LOGE(TAG, "esp bt controller disable failed");
377 return false;
378 }
379 }
380#else
381 if (esp_hosted_bt_controller_disable() != ESP_OK) {
382 ESP_LOGW(TAG, "esp_hosted_bt_controller_disable failed");
383 return false;
384 }
385
386 if (esp_hosted_bt_controller_deinit(false) != ESP_OK) {
387 ESP_LOGW(TAG, "esp_hosted_bt_controller_deinit failed");
388 return false;
389 }
390
391 hosted_hci_bluedroid_close();
392#endif
393 return true;
394}
395
397 if (this->state_ != BLE_COMPONENT_STATE_ACTIVE) {
398 this->loop_handle_state_transition_not_active_();
399 return;
400 }
401
402#ifdef USE_ESP32_BLE_ADVERTISING
403 if (this->advertising_ != nullptr) {
404 this->advertising_->loop();
405 }
406#endif
407
408 BLEEvent *ble_event = this->ble_events_.pop();
409 if (ble_event == nullptr)
410 return;
411
412 do {
413 switch (ble_event->type_) {
414#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
415 case BLEEvent::GATTS: {
416 esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
417 esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
418 esp_ble_gatts_cb_param_t *param = &ble_event->event_.gatts.gatts_param;
419 ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
420 this->gatts_event_callbacks_.call(event, gatts_if, param);
421 break;
422 }
423#endif
424#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
425 case BLEEvent::GATTC: {
426 esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
427 esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
428 esp_ble_gattc_cb_param_t *param = &ble_event->event_.gattc.gattc_param;
429 ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
430 this->gattc_event_callbacks_.call(event, gattc_if, param);
431 break;
432 }
433#endif
434 case BLEEvent::GAP: {
435 esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
436 switch (gap_event) {
437 case ESP_GAP_BLE_SCAN_RESULT_EVT:
438#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
439 this->gap_scan_event_callbacks_.call(ble_event->scan_result());
440#endif
441 break;
442
443 // Scan complete events
444 GAP_SCAN_COMPLETE_EVENTS:
445 // Advertising complete events
446 GAP_ADV_COMPLETE_EVENTS:
447 // RSSI complete event
448 case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
449 // Security events
450 GAP_SECURITY_EVENTS:
451 ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
452#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
453 {
454 esp_ble_gap_cb_param_t *param = NULL;
455 // clang-format off
456 switch (gap_event) {
457 // All three scan complete events have the same structure with just status
458 // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
459 // This is verified at compile-time by static_assert checks in ble_event.h
460 // The struct already contains our copy of the status (copied in BLEEvent constructor)
461 GAP_SCAN_COMPLETE_EVENTS:
462 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete);
463 break;
464
465 // All advertising complete events have the same structure with just status
466 GAP_ADV_COMPLETE_EVENTS:
467 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete);
468 break;
469
470 case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
471 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete);
472 break;
473
474 GAP_SECURITY_EVENTS:
475 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security);
476 break;
477
478 default:
479 break;
480 }
481 // clang-format on
482 // Dispatch to all registered handlers
483 this->gap_event_callbacks_.call(gap_event, param);
484 }
485#endif
486 break;
487
488 default:
489 // Unknown/unhandled event
490 ESP_LOGW(TAG, "Unhandled GAP event type in loop: %d", gap_event);
491 break;
492 }
493 break;
494 }
495 default:
496 break;
497 }
498 // Return the event to the pool
499 this->ble_event_pool_.release(ble_event);
500 } while ((ble_event = this->ble_events_.pop()) != nullptr);
501
502 // Log dropped events - only reachable when events were processed.
503 // Drops only occur when the queue is full, and only this loop drains it,
504 // so if pop() returned nullptr above we can skip this check (saves a memw).
505 uint16_t dropped = this->ble_events_.get_and_reset_dropped_count();
506 if (dropped > 0) {
507 ESP_LOGW(TAG, "Dropped %u BLE events due to buffer overflow", dropped);
508 }
509}
510
511void ESP32BLE::loop_handle_state_transition_not_active_() {
512 // Caller ensures state_ != ACTIVE
513 if (this->state_ == BLE_COMPONENT_STATE_DISABLE) {
514 ESP_LOGD(TAG, "Disabling");
515
516#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
517 this->ble_status_event_callbacks_.call();
518#endif
519
520 if (!ble_dismantle_()) {
521 ESP_LOGE(TAG, "Could not be dismantled");
522 this->mark_failed();
523 return;
524 }
525 this->state_ = BLE_COMPONENT_STATE_DISABLED;
526 } else if (this->state_ == BLE_COMPONENT_STATE_ENABLE) {
527 ESP_LOGD(TAG, "Enabling");
528 this->state_ = BLE_COMPONENT_STATE_OFF;
529
530 if (!ble_setup_()) {
531 ESP_LOGE(TAG, "Could not be set up");
532 this->mark_failed();
533 return;
534 }
535
536 this->state_ = BLE_COMPONENT_STATE_ACTIVE;
537 }
538}
539
540// Helper function to load new event data based on type
541void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
542 event->load_gap_event(e, p);
543}
544
545#ifdef USE_ESP32_BLE_CLIENT
546void load_ble_event(BLEEvent *event, esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
547 event->load_gattc_event(e, i, p);
548}
549#endif
550
551#ifdef USE_ESP32_BLE_SERVER
552void load_ble_event(BLEEvent *event, esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
553 event->load_gatts_event(e, i, p);
554}
555#endif
556
557template<typename... Args> void enqueue_ble_event(Args... args) {
558 // Allocate an event from the pool
559 BLEEvent *event = global_ble->ble_event_pool_.allocate();
560 if (event == nullptr) {
561 // No events available - queue is full or we're out of memory
562 global_ble->ble_events_.increment_dropped_count();
563 return;
564 }
565
566 // Load new event data (replaces previous event)
567 load_ble_event(event, args...);
568
569 // Push the event to the queue
570 // Push always succeeds: pool is sized to queue capacity (N-1), so if
571 // allocate() returned non-null, the queue is guaranteed to have room.
572 global_ble->ble_events_.push(event);
573}
574
575// Explicit template instantiations for the friend function
576template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *);
577#ifdef USE_ESP32_BLE_SERVER
578template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *);
579#endif
580#ifdef USE_ESP32_BLE_CLIENT
581template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *);
582#endif
583
584void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
585 switch (event) {
586 // Queue GAP events that components need to handle
587 // Scanning events - used by esp32_ble_tracker
588 case ESP_GAP_BLE_SCAN_RESULT_EVT:
589 GAP_SCAN_COMPLETE_EVENTS:
590 // Advertising events - used by esp32_ble_beacon and esp32_ble server
591 GAP_ADV_COMPLETE_EVENTS:
592 // Connection events - used by ble_client
593 case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
594 enqueue_ble_event(event, param);
595 return;
596
597 // Security events - used by ble_client and bluetooth_proxy
598 // These are rare but interactive (pairing/bonding), so notify immediately
599 GAP_SECURITY_EVENTS:
600 enqueue_ble_event(event, param);
601 // Wake up main loop to process security event immediately
603 return;
604
605 // Ignore these GAP events as they are not relevant for our use case
606 case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
607 case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
608 case ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT: // BLE 5.0 PHY update complete
609 case ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT: // BLE 5.0 channel selection algorithm
610 return;
611
612 default:
613 break;
614 }
615 ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event);
616}
617
618#ifdef USE_ESP32_BLE_SERVER
619void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
620 esp_ble_gatts_cb_param_t *param) {
621 enqueue_ble_event(event, gatts_if, param);
622 // Wake up main loop to process GATT event immediately
624}
625#endif
626
627#ifdef USE_ESP32_BLE_CLIENT
628void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
629 esp_ble_gattc_cb_param_t *param) {
630 enqueue_ble_event(event, gattc_if, param);
631 // Wake up main loop to process GATT event immediately
633}
634#endif
635
637
639 const uint8_t *mac_address = esp_bt_dev_get_address();
640 if (mac_address) {
641 const char *io_capability_s;
642 switch (this->io_cap_) {
643 case ESP_IO_CAP_OUT:
644 io_capability_s = "display_only";
645 break;
646 case ESP_IO_CAP_IO:
647 io_capability_s = "display_yes_no";
648 break;
649 case ESP_IO_CAP_IN:
650 io_capability_s = "keyboard_only";
651 break;
652 case ESP_IO_CAP_NONE:
653 io_capability_s = "none";
654 break;
655 case ESP_IO_CAP_KBDISP:
656 io_capability_s = "keyboard_display";
657 break;
658 default:
659 io_capability_s = "invalid";
660 break;
661 }
662
663 char mac_s[18];
664 format_mac_addr_upper(mac_address, mac_s);
665 ESP_LOGCONFIG(TAG,
666 "BLE:\n"
667 " MAC address: %s\n"
668 " IO Capability: %s",
669 mac_s, io_capability_s);
670
671#ifdef ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
672 const char *auth_req_mode_s = "<default>";
673 if (this->auth_req_mode_) {
674 switch (this->auth_req_mode_.value()) {
675 case AUTH_REQ_NO_BOND:
676 auth_req_mode_s = "no_bond";
677 break;
678 case AUTH_REQ_BOND:
679 auth_req_mode_s = "bond";
680 break;
681 case AUTH_REQ_MITM:
682 auth_req_mode_s = "mitm";
683 break;
685 auth_req_mode_s = "bond_mitm";
686 break;
687 case AUTH_REQ_SC_ONLY:
688 auth_req_mode_s = "sc_only";
689 break;
690 case AUTH_REQ_SC_BOND:
691 auth_req_mode_s = "sc_bond";
692 break;
693 case AUTH_REQ_SC_MITM:
694 auth_req_mode_s = "sc_mitm";
695 break;
697 auth_req_mode_s = "sc_mitm_bond";
698 break;
699 }
700 }
701
702 ESP_LOGCONFIG(TAG, " Auth Req Mode: %s", auth_req_mode_s);
703 if (this->max_key_size_ && this->min_key_size_) {
704 ESP_LOGCONFIG(TAG, " Key Size: %u - %u", this->min_key_size_, this->max_key_size_);
705 } else if (this->max_key_size_) {
706 ESP_LOGCONFIG(TAG, " Key Size: <default> - %u", this->max_key_size_);
707 } else if (this->min_key_size_) {
708 ESP_LOGCONFIG(TAG, " Key Size: %u - <default>", this->min_key_size_);
709 }
710#endif // ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
711
712 } else {
713 ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled");
714 }
715}
716
717ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
718
719} // namespace esphome::esp32_ble
720
721#endif
bool btInUse()
Definition ble.cpp:29
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
void wake_loop_threadsafe()
Wake the main event loop from another thread or callback.
bool is_name_add_mac_suffix_enabled() const
void mark_failed()
Mark this component as failed.
constexpr size_type length() const
Definition string_ref.h:75
void set_manufacturer_data(const std::vector< uint8_t > &data)
void set_scan_response(bool scan_response)
void set_include_name(bool include_name)
void set_min_preferred_interval(uint16_t interval)
void set_service_data(const std::vector< uint8_t > &data)
void register_raw_advertisement_callback(std::function< void(bool)> &&callback)
void set_appearance(uint16_t appearance)
union esphome::esp32_ble::BLEEvent::@78 event_
struct esphome::esp32_ble::BLEEvent::@78::gatts_event gatts
struct esphome::esp32_ble::BLEEvent::@78::gap_event gap
struct esphome::esp32_ble::BLEEvent::@78::gattc_event gattc
void advertising_set_manufacturer_data(const std::vector< uint8_t > &data)
Definition ble.cpp:98
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
Definition ble.cpp:584
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
Definition ble.cpp:628
friend void enqueue_ble_event(Args... args)
Definition ble.cpp:557
void advertising_register_raw_advertisement_callback(std::function< void(bool)> &&callback)
Definition ble.cpp:126
void advertising_set_service_data_and_name(std::span< const uint8_t > data, bool include_name)
Definition ble.cpp:104
void advertising_add_service_uuid(ESPBTUUID uuid)
Definition ble.cpp:131
void dump_config() override
Definition ble.cpp:638
void loop() override
Definition ble.cpp:396
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
Definition ble.cpp:619
void advertising_set_service_data(const std::vector< uint8_t > &data)
Definition ble.cpp:92
ESPHOME_ALWAYS_INLINE bool is_active()
Definition ble.h:107
float get_setup_priority() const override
Definition ble.cpp:636
void setup() override
Definition ble.cpp:56
void advertising_remove_service_uuid(ESPBTUUID uuid)
Definition ble.cpp:137
ESP32BLE * global_ble
Definition ble.cpp:717
void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p)
Definition ble.cpp:541
@ BLE_COMPONENT_STATE_DISABLE
BLE should be disabled on next loop.
Definition ble.h:81
@ BLE_COMPONENT_STATE_OFF
Nothing has been initialized yet.
Definition ble.h:79
@ BLE_COMPONENT_STATE_ENABLE
BLE should be enabled on next loop.
Definition ble.h:85
@ BLE_COMPONENT_STATE_DISABLED
BLE is disabled.
Definition ble.h:83
@ BLE_COMPONENT_STATE_ACTIVE
BLE is active.
Definition ble.h:87
@ AUTH_REQ_SC_MITM_BOND
Definition ble.h:73
@ AUTH_REQ_BOND_MITM
Definition ble.h:69
void enqueue_ble_event(Args... args)
Definition ble.cpp:557
constexpr float BLUETOOTH
Definition component.h:45
size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep, const char *suffix_ptr, size_t suffix_len)
Zero-allocation version: format name + separator + suffix directly into buffer.
Definition helpers.cpp:300
const char int const __FlashStringHelper va_list args
Definition log.h:74
void get_mac_address_into_buffer(std::span< char, MAC_ADDRESS_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in lowercase hex notation.
Definition helpers.cpp:850
void HOT delay(uint32_t ms)
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)
Definition helpers.h:1420