ESPHome 2026.5.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 suffix length (last 6 characters of 12-char MAC address string)
261 constexpr size_t mac_address_suffix_len = 6;
262 char mac_addr[MAC_ADDRESS_BUFFER_SIZE];
264 const char *mac_suffix_ptr = mac_addr + mac_address_suffix_len;
265 make_name_with_suffix_to(name_buffer, sizeof(name_buffer), this->name_, strlen(this->name_), '-', mac_suffix_ptr,
266 mac_address_suffix_len);
267 device_name = name_buffer;
268 } else {
269 device_name = this->name_;
270 }
271 } else {
272 const auto &app_name = App.get_name();
273 size_t name_len = app_name.length();
274 if (name_len > 20) {
276 // Keep first 13 chars and last 7 chars (MAC suffix), remove middle
277 memcpy(name_buffer, app_name.c_str(), 13);
278 memcpy(name_buffer + 13, app_name.c_str() + name_len - 7, 7);
279 } else {
280 memcpy(name_buffer, app_name.c_str(), 20);
281 }
282 name_buffer[20] = '\0';
283 } else {
284 memcpy(name_buffer, app_name.c_str(), name_len + 1); // Include null terminator
285 }
286 device_name = name_buffer;
287 }
288
289 err = esp_ble_gap_set_device_name(device_name);
290 if (err != ESP_OK) {
291 ESP_LOGE(TAG, "esp_ble_gap_set_device_name failed: %d", err);
292 return false;
293 }
294
295 err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &(this->io_cap_), sizeof(esp_ble_io_cap_t));
296 if (err != ESP_OK) {
297 ESP_LOGE(TAG, "esp_ble_gap_set_security_param iocap_mode failed: %d", err);
298 return false;
299 }
300
301#ifdef ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
302 if (this->max_key_size_) {
303 err = esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &(this->max_key_size_), sizeof(uint8_t));
304 if (err != ESP_OK) {
305 ESP_LOGE(TAG, "esp_ble_gap_set_security_param max_key_size failed: %d", err);
306 return false;
307 }
308 }
309
310 if (this->min_key_size_) {
311 err = esp_ble_gap_set_security_param(ESP_BLE_SM_MIN_KEY_SIZE, &(this->min_key_size_), sizeof(uint8_t));
312 if (err != ESP_OK) {
313 ESP_LOGE(TAG, "esp_ble_gap_set_security_param min_key_size failed: %d", err);
314 return false;
315 }
316 }
317
318 if (this->auth_req_mode_) {
319 err = esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &(this->auth_req_mode_.value()),
320 sizeof(esp_ble_auth_req_t));
321 if (err != ESP_OK) {
322 ESP_LOGE(TAG, "esp_ble_gap_set_security_param authen_req_mode failed: %d", err);
323 return false;
324 }
325 }
326#endif // ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
327
328 // BLE takes some time to be fully set up, 200ms should be more than enough
329 delay(200); // NOLINT
330
331 return true;
332}
333
335 esp_err_t err = esp_bluedroid_disable();
336 if (err != ESP_OK) {
337 // ESP_ERR_INVALID_STATE means Bluedroid is already disabled, which is fine
338 if (err != ESP_ERR_INVALID_STATE) {
339 ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", err);
340 return false;
341 }
342 ESP_LOGD(TAG, "Already disabled");
343 }
344 err = esp_bluedroid_deinit();
345 if (err != ESP_OK) {
346 // ESP_ERR_INVALID_STATE means Bluedroid is already deinitialized, which is fine
347 if (err != ESP_ERR_INVALID_STATE) {
348 ESP_LOGE(TAG, "esp_bluedroid_deinit failed: %d", err);
349 return false;
350 }
351 ESP_LOGD(TAG, "Already deinitialized");
352 }
353
354#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
355 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
356 // stop bt controller
357 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
358 err = esp_bt_controller_disable();
359 if (err != ESP_OK) {
360 ESP_LOGE(TAG, "esp_bt_controller_disable failed: %s", esp_err_to_name(err));
361 return false;
362 }
363 while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED)
364 ;
365 }
366 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
367 err = esp_bt_controller_deinit();
368 if (err != ESP_OK) {
369 ESP_LOGE(TAG, "esp_bt_controller_deinit failed: %s", esp_err_to_name(err));
370 return false;
371 }
372 }
373 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
374 ESP_LOGE(TAG, "esp bt controller disable failed");
375 return false;
376 }
377 }
378#else
379 if (esp_hosted_bt_controller_disable() != ESP_OK) {
380 ESP_LOGW(TAG, "esp_hosted_bt_controller_disable failed");
381 return false;
382 }
383
384 if (esp_hosted_bt_controller_deinit(false) != ESP_OK) {
385 ESP_LOGW(TAG, "esp_hosted_bt_controller_deinit failed");
386 return false;
387 }
388
389 hosted_hci_bluedroid_close();
390#endif
391 return true;
392}
393
395 if (this->state_ != BLE_COMPONENT_STATE_ACTIVE) {
396 this->loop_handle_state_transition_not_active_();
397 return;
398 }
399
400#ifdef USE_ESP32_BLE_ADVERTISING
401 if (this->advertising_ != nullptr) {
402 this->advertising_->loop();
403 }
404#endif
405
406 BLEEvent *ble_event = this->ble_events_.pop();
407 if (ble_event == nullptr)
408 return;
409
410 do {
411 switch (ble_event->type_) {
412#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
413 case BLEEvent::GATTS: {
414 esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
415 esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
416 esp_ble_gatts_cb_param_t *param = &ble_event->event_.gatts.gatts_param;
417 ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
418 this->gatts_event_callbacks_.call(event, gatts_if, param);
419 break;
420 }
421#endif
422#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
423 case BLEEvent::GATTC: {
424 esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
425 esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
426 esp_ble_gattc_cb_param_t *param = &ble_event->event_.gattc.gattc_param;
427 ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
428 this->gattc_event_callbacks_.call(event, gattc_if, param);
429 break;
430 }
431#endif
432 case BLEEvent::GAP: {
433 esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
434 switch (gap_event) {
435 case ESP_GAP_BLE_SCAN_RESULT_EVT:
436#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
437 this->gap_scan_event_callbacks_.call(ble_event->scan_result());
438#endif
439 break;
440
441 // Scan complete events
442 GAP_SCAN_COMPLETE_EVENTS:
443 // Advertising complete events
444 GAP_ADV_COMPLETE_EVENTS:
445 // RSSI complete event
446 case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
447 // Security events
448 GAP_SECURITY_EVENTS:
449 ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
450#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
451 {
452 esp_ble_gap_cb_param_t *param = NULL;
453 // clang-format off
454 switch (gap_event) {
455 // All three scan complete events have the same structure with just status
456 // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
457 // This is verified at compile-time by static_assert checks in ble_event.h
458 // The struct already contains our copy of the status (copied in BLEEvent constructor)
459 GAP_SCAN_COMPLETE_EVENTS:
460 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete);
461 break;
462
463 // All advertising complete events have the same structure with just status
464 GAP_ADV_COMPLETE_EVENTS:
465 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete);
466 break;
467
468 case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
469 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete);
470 break;
471
472 GAP_SECURITY_EVENTS:
473 param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security);
474 break;
475
476 default:
477 break;
478 }
479 // clang-format on
480 // Dispatch to all registered handlers
481 this->gap_event_callbacks_.call(gap_event, param);
482 }
483#endif
484 break;
485
486 default:
487 // Unknown/unhandled event
488 ESP_LOGW(TAG, "Unhandled GAP event type in loop: %d", gap_event);
489 break;
490 }
491 break;
492 }
493 default:
494 break;
495 }
496 // Return the event to the pool
497 this->ble_event_pool_.release(ble_event);
498 } while ((ble_event = this->ble_events_.pop()) != nullptr);
499
500 // Log dropped events - only reachable when events were processed.
501 // Drops only occur when the queue is full, and only this loop drains it,
502 // so if pop() returned nullptr above we can skip this check (saves a memw).
503 uint16_t dropped = this->ble_events_.get_and_reset_dropped_count();
504 if (dropped > 0) {
505 ESP_LOGW(TAG, "Dropped %u BLE events due to buffer overflow", dropped);
506 }
507}
508
509void ESP32BLE::loop_handle_state_transition_not_active_() {
510 // Caller ensures state_ != ACTIVE
511 if (this->state_ == BLE_COMPONENT_STATE_DISABLE) {
512 ESP_LOGD(TAG, "Disabling");
513
514#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
515 this->ble_status_event_callbacks_.call();
516#endif
517
518 if (!ble_dismantle_()) {
519 ESP_LOGE(TAG, "Could not be dismantled");
520 this->mark_failed();
521 return;
522 }
523 this->state_ = BLE_COMPONENT_STATE_DISABLED;
524 } else if (this->state_ == BLE_COMPONENT_STATE_ENABLE) {
525 ESP_LOGD(TAG, "Enabling");
526 this->state_ = BLE_COMPONENT_STATE_OFF;
527
528 if (!ble_setup_()) {
529 ESP_LOGE(TAG, "Could not be set up");
530 this->mark_failed();
531 return;
532 }
533
534 this->state_ = BLE_COMPONENT_STATE_ACTIVE;
535 }
536}
537
538// Helper function to load new event data based on type
539void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
540 event->load_gap_event(e, p);
541}
542
543#ifdef USE_ESP32_BLE_CLIENT
544void load_ble_event(BLEEvent *event, esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
545 event->load_gattc_event(e, i, p);
546}
547#endif
548
549#ifdef USE_ESP32_BLE_SERVER
550void load_ble_event(BLEEvent *event, esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
551 event->load_gatts_event(e, i, p);
552}
553#endif
554
555template<typename... Args> void enqueue_ble_event(Args... args) {
556 // Allocate an event from the pool
557 BLEEvent *event = global_ble->ble_event_pool_.allocate();
558 if (event == nullptr) {
559 // No events available - queue is full or we're out of memory
560 global_ble->ble_events_.increment_dropped_count();
561 return;
562 }
563
564 // Load new event data (replaces previous event)
565 load_ble_event(event, args...);
566
567 // Push the event to the queue
568 // Push always succeeds: pool is sized to queue capacity (N-1), so if
569 // allocate() returned non-null, the queue is guaranteed to have room.
570 global_ble->ble_events_.push(event);
571}
572
573// Explicit template instantiations for the friend function
574template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *);
575#ifdef USE_ESP32_BLE_SERVER
576template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *);
577#endif
578#ifdef USE_ESP32_BLE_CLIENT
579template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *);
580#endif
581
582void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
583 switch (event) {
584 // Queue GAP events that components need to handle
585 // Scanning events - used by esp32_ble_tracker
586 case ESP_GAP_BLE_SCAN_RESULT_EVT:
587 GAP_SCAN_COMPLETE_EVENTS:
588 // Advertising events - used by esp32_ble_beacon and esp32_ble server
589 GAP_ADV_COMPLETE_EVENTS:
590 // Connection events - used by ble_client
591 case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
592 enqueue_ble_event(event, param);
593 return;
594
595 // Security events - used by ble_client and bluetooth_proxy
596 // These are rare but interactive (pairing/bonding), so notify immediately
597 GAP_SECURITY_EVENTS:
598 enqueue_ble_event(event, param);
599 // Wake up main loop to process security event immediately
601 return;
602
603 // Ignore these GAP events as they are not relevant for our use case
604 case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
605 case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
606 case ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT: // BLE 5.0 PHY update complete
607 case ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT: // BLE 5.0 channel selection algorithm
608 return;
609
610 default:
611 break;
612 }
613 ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event);
614}
615
616#ifdef USE_ESP32_BLE_SERVER
617void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
618 esp_ble_gatts_cb_param_t *param) {
619 enqueue_ble_event(event, gatts_if, param);
620 // Wake up main loop to process GATT event immediately
622}
623#endif
624
625#ifdef USE_ESP32_BLE_CLIENT
626void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
627 esp_ble_gattc_cb_param_t *param) {
628 enqueue_ble_event(event, gattc_if, param);
629 // Wake up main loop to process GATT event immediately
631}
632#endif
633
635
637 const uint8_t *mac_address = esp_bt_dev_get_address();
638 if (mac_address) {
639 const char *io_capability_s;
640 switch (this->io_cap_) {
641 case ESP_IO_CAP_OUT:
642 io_capability_s = "display_only";
643 break;
644 case ESP_IO_CAP_IO:
645 io_capability_s = "display_yes_no";
646 break;
647 case ESP_IO_CAP_IN:
648 io_capability_s = "keyboard_only";
649 break;
650 case ESP_IO_CAP_NONE:
651 io_capability_s = "none";
652 break;
653 case ESP_IO_CAP_KBDISP:
654 io_capability_s = "keyboard_display";
655 break;
656 default:
657 io_capability_s = "invalid";
658 break;
659 }
660
661 char mac_s[18];
662 format_mac_addr_upper(mac_address, mac_s);
663 ESP_LOGCONFIG(TAG,
664 "BLE:\n"
665 " MAC address: %s\n"
666 " IO Capability: %s",
667 mac_s, io_capability_s);
668#ifdef USE_ESP32_BLE_PSRAM
669 ESP_LOGCONFIG(TAG, " PSRAM BLE allocation: enabled");
670#endif
671
672#ifdef ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
673 const char *auth_req_mode_s = "<default>";
674 if (this->auth_req_mode_) {
675 switch (this->auth_req_mode_.value()) {
676 case AUTH_REQ_NO_BOND:
677 auth_req_mode_s = "no_bond";
678 break;
679 case AUTH_REQ_BOND:
680 auth_req_mode_s = "bond";
681 break;
682 case AUTH_REQ_MITM:
683 auth_req_mode_s = "mitm";
684 break;
686 auth_req_mode_s = "bond_mitm";
687 break;
688 case AUTH_REQ_SC_ONLY:
689 auth_req_mode_s = "sc_only";
690 break;
691 case AUTH_REQ_SC_BOND:
692 auth_req_mode_s = "sc_bond";
693 break;
694 case AUTH_REQ_SC_MITM:
695 auth_req_mode_s = "sc_mitm";
696 break;
698 auth_req_mode_s = "sc_mitm_bond";
699 break;
700 }
701 }
702
703 ESP_LOGCONFIG(TAG, " Auth Req Mode: %s", auth_req_mode_s);
704 if (this->max_key_size_ && this->min_key_size_) {
705 ESP_LOGCONFIG(TAG, " Key Size: %u - %u", this->min_key_size_, this->max_key_size_);
706 } else if (this->max_key_size_) {
707 ESP_LOGCONFIG(TAG, " Key Size: <default> - %u", this->max_key_size_);
708 } else if (this->min_key_size_) {
709 ESP_LOGCONFIG(TAG, " Key Size: %u - <default>", this->min_key_size_);
710 }
711#endif // ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
712
713 } else {
714 ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled");
715 }
716}
717
718ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
719
720} // namespace esphome::esp32_ble
721
722#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:582
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:626
friend void enqueue_ble_event(Args... args)
Definition ble.cpp:555
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:636
void loop() override
Definition ble.cpp:394
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:617
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:634
void setup() override
Definition ble.cpp:56
void advertising_remove_service_uuid(ESPBTUUID uuid)
Definition ble.cpp:137
ESP32BLE * global_ble
Definition ble.cpp:718
void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p)
Definition ble.cpp:539
@ 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:555
constexpr float BLUETOOTH
Definition component.h:46
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:242
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:745
void HOT delay(uint32_t ms)
Definition hal.cpp:82
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:1453