ESPHome 2026.3.0-dev
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
84bool ESP32BLE::is_active() { return this->state_ == BLE_COMPONENT_STATE_ACTIVE; }
85
86#ifdef USE_ESP32_BLE_ADVERTISING
88 this->advertising_init_();
89 if (!this->is_active())
90 return;
91 this->advertising_->start();
92}
93
94void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
95 this->advertising_init_();
96 this->advertising_->set_service_data(data);
97 this->advertising_start();
98}
99
100void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
101 this->advertising_init_();
102 this->advertising_->set_manufacturer_data(data);
103 this->advertising_start();
104}
105
106void ESP32BLE::advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name) {
107 // This method atomically updates both service data and device name inclusion in BLE advertising.
108 // When include_name is true, the device name is included in the advertising packet making it
109 // visible to passive BLE scanners. When false, the name is only visible in scan response
110 // (requires active scanning). This atomic operation ensures we only restart advertising once
111 // when changing both properties, avoiding the brief gap that would occur with separate calls.
112
113 this->advertising_init_();
114
115 if (include_name) {
116 // When including name, clear service data first to avoid packet overflow
117 this->advertising_->set_service_data(std::span<const uint8_t>{});
118 this->advertising_->set_include_name(true);
119 } else {
120 // When including service data, clear name first to avoid packet overflow
121 this->advertising_->set_include_name(false);
122 this->advertising_->set_service_data(data);
123 }
124
125 this->advertising_start();
126}
127
128void ESP32BLE::advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback) {
129 this->advertising_init_();
130 this->advertising_->register_raw_advertisement_callback(std::move(callback));
131}
132
134 this->advertising_init_();
135 this->advertising_->add_service_uuid(uuid);
136 this->advertising_start();
137}
138
140 this->advertising_init_();
141 this->advertising_->remove_service_uuid(uuid);
142 this->advertising_start();
143}
144#endif
145
147 esp_err_t err = nvs_flash_init();
148 if (err != ESP_OK) {
149 ESP_LOGE(TAG, "nvs_flash_init failed: %d", err);
150 return false;
151 }
152 return true;
153}
154
155#ifdef USE_ESP32_BLE_ADVERTISING
157 if (this->advertising_ != nullptr)
158 return;
159 this->advertising_ = new BLEAdvertising(this->advertising_cycle_time_); // NOLINT(cppcoreguidelines-owning-memory)
160
161 this->advertising_->set_scan_response(true);
162 this->advertising_->set_min_preferred_interval(0x06);
163 this->advertising_->set_appearance(this->appearance_);
164}
165#endif
166
168 esp_err_t err;
169#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
170 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
171 // start bt controller
172 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
173 esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
174 err = esp_bt_controller_init(&cfg);
175 if (err != ESP_OK) {
176 ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err));
177 return false;
178 }
179 while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE)
180 ;
181 }
182 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
183 err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
184 if (err != ESP_OK) {
185 ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err));
186 return false;
187 }
188 }
189 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
190 ESP_LOGE(TAG, "esp bt controller enable failed");
191 return false;
192 }
193 }
194
195 esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
196#else
197 esp_hosted_connect_to_slave(); // NOLINT
198
199 if (esp_hosted_bt_controller_init() != ESP_OK) {
200 ESP_LOGW(TAG, "esp_hosted_bt_controller_init failed");
201 return false;
202 }
203
204 if (esp_hosted_bt_controller_enable() != ESP_OK) {
205 ESP_LOGW(TAG, "esp_hosted_bt_controller_enable failed");
206 return false;
207 }
208
209 hosted_hci_bluedroid_open();
210
211 esp_bluedroid_hci_driver_operations_t operations = {
212 .send = hosted_hci_bluedroid_send,
213 .check_send_available = hosted_hci_bluedroid_check_send_available,
214 .register_host_callback = hosted_hci_bluedroid_register_host_callback,
215 };
216 esp_bluedroid_attach_hci_driver(&operations);
217#endif
218
219 err = esp_bluedroid_init();
220 if (err != ESP_OK) {
221 ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err);
222 return false;
223 }
224 err = esp_bluedroid_enable();
225 if (err != ESP_OK) {
226 ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err);
227 return false;
228 }
229
230#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
231 err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler);
232 if (err != ESP_OK) {
233 ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
234 return false;
235 }
236#endif
237
238#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
239 err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler);
240 if (err != ESP_OK) {
241 ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err);
242 return false;
243 }
244#endif
245
246#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
247 err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler);
248 if (err != ESP_OK) {
249 ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
250 return false;
251 }
252#endif
253
254 // BLE device names are limited to 20 characters
255 // Buffer: 20 chars + null terminator
256 constexpr size_t ble_name_max_len = 21;
257 char name_buffer[ble_name_max_len];
258 const char *device_name;
259
260 if (this->name_ != nullptr) {
262 // MAC address length: 12 hex chars + null terminator
263 constexpr size_t mac_address_len = 13;
264 // MAC address suffix length (last 6 characters of 12-char MAC address string)
265 constexpr size_t mac_address_suffix_len = 6;
266 char mac_addr[mac_address_len];
268 const char *mac_suffix_ptr = mac_addr + mac_address_suffix_len;
269 make_name_with_suffix_to(name_buffer, sizeof(name_buffer), this->name_, strlen(this->name_), '-', mac_suffix_ptr,
270 mac_address_suffix_len);
271 device_name = name_buffer;
272 } else {
273 device_name = this->name_;
274 }
275 } else {
276 const std::string &app_name = App.get_name();
277 size_t name_len = app_name.length();
278 if (name_len > 20) {
280 // Keep first 13 chars and last 7 chars (MAC suffix), remove middle
281 memcpy(name_buffer, app_name.c_str(), 13);
282 memcpy(name_buffer + 13, app_name.c_str() + name_len - 7, 7);
283 } else {
284 memcpy(name_buffer, app_name.c_str(), 20);
285 }
286 name_buffer[20] = '\0';
287 } else {
288 memcpy(name_buffer, app_name.c_str(), name_len + 1); // Include null terminator
289 }
290 device_name = name_buffer;
291 }
292
293 err = esp_ble_gap_set_device_name(device_name);
294 if (err != ESP_OK) {
295 ESP_LOGE(TAG, "esp_ble_gap_set_device_name failed: %d", err);
296 return false;
297 }
298
299 err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &(this->io_cap_), sizeof(esp_ble_io_cap_t));
300 if (err != ESP_OK) {
301 ESP_LOGE(TAG, "esp_ble_gap_set_security_param iocap_mode failed: %d", err);
302 return false;
303 }
304
305#ifdef ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
306 if (this->max_key_size_) {
307 err = esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &(this->max_key_size_), sizeof(uint8_t));
308 if (err != ESP_OK) {
309 ESP_LOGE(TAG, "esp_ble_gap_set_security_param max_key_size failed: %d", err);
310 return false;
311 }
312 }
313
314 if (this->min_key_size_) {
315 err = esp_ble_gap_set_security_param(ESP_BLE_SM_MIN_KEY_SIZE, &(this->min_key_size_), sizeof(uint8_t));
316 if (err != ESP_OK) {
317 ESP_LOGE(TAG, "esp_ble_gap_set_security_param min_key_size failed: %d", err);
318 return false;
319 }
320 }
321
322 if (this->auth_req_mode_) {
323 err = esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &(this->auth_req_mode_.value()),
324 sizeof(esp_ble_auth_req_t));
325 if (err != ESP_OK) {
326 ESP_LOGE(TAG, "esp_ble_gap_set_security_param authen_req_mode failed: %d", err);
327 return false;
328 }
329 }
330#endif // ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
331
332 // BLE takes some time to be fully set up, 200ms should be more than enough
333 delay(200); // NOLINT
334
335 return true;
336}
337
339 esp_err_t err = esp_bluedroid_disable();
340 if (err != ESP_OK) {
341 // ESP_ERR_INVALID_STATE means Bluedroid is already disabled, which is fine
342 if (err != ESP_ERR_INVALID_STATE) {
343 ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", err);
344 return false;
345 }
346 ESP_LOGD(TAG, "Already disabled");
347 }
348 err = esp_bluedroid_deinit();
349 if (err != ESP_OK) {
350 // ESP_ERR_INVALID_STATE means Bluedroid is already deinitialized, which is fine
351 if (err != ESP_ERR_INVALID_STATE) {
352 ESP_LOGE(TAG, "esp_bluedroid_deinit failed: %d", err);
353 return false;
354 }
355 ESP_LOGD(TAG, "Already deinitialized");
356 }
357
358#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
359 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
360 // stop bt controller
361 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
362 err = esp_bt_controller_disable();
363 if (err != ESP_OK) {
364 ESP_LOGE(TAG, "esp_bt_controller_disable failed: %s", esp_err_to_name(err));
365 return false;
366 }
367 while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED)
368 ;
369 }
370 if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
371 err = esp_bt_controller_deinit();
372 if (err != ESP_OK) {
373 ESP_LOGE(TAG, "esp_bt_controller_deinit failed: %s", esp_err_to_name(err));
374 return false;
375 }
376 }
377 if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
378 ESP_LOGE(TAG, "esp bt controller disable failed");
379 return false;
380 }
381 }
382#else
383 if (esp_hosted_bt_controller_disable() != ESP_OK) {
384 ESP_LOGW(TAG, "esp_hosted_bt_controller_disable failed");
385 return false;
386 }
387
388 if (esp_hosted_bt_controller_deinit(false) != ESP_OK) {
389 ESP_LOGW(TAG, "esp_hosted_bt_controller_deinit failed");
390 return false;
391 }
392
393 hosted_hci_bluedroid_close();
394#endif
395 return true;
396}
397
399 if (this->state_ != BLE_COMPONENT_STATE_ACTIVE) {
400 this->loop_handle_state_transition_not_active_();
401 return;
402 }
403
404 BLEEvent *ble_event = this->ble_events_.pop();
405 while (ble_event != nullptr) {
406 switch (ble_event->type_) {
407#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
408 case BLEEvent::GATTS: {
409 esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
410 esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
411 esp_ble_gatts_cb_param_t *param = &ble_event->event_.gatts.gatts_param;
412 ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
413 for (auto *gatts_handler : this->gatts_event_handlers_) {
414 gatts_handler->gatts_event_handler(event, gatts_if, param);
415 }
416 break;
417 }
418#endif
419#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
420 case BLEEvent::GATTC: {
421 esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
422 esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
423 esp_ble_gattc_cb_param_t *param = &ble_event->event_.gattc.gattc_param;
424 ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
425 for (auto *gattc_handler : this->gattc_event_handlers_) {
426 gattc_handler->gattc_event_handler(event, gattc_if, param);
427 }
428 break;
429 }
430#endif
431 case BLEEvent::GAP: {
432 esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
433 switch (gap_event) {
434 case ESP_GAP_BLE_SCAN_RESULT_EVT:
435#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
436 // Use the new scan event handler - no memcpy!
437 for (auto *scan_handler : this->gap_scan_event_handlers_) {
438 scan_handler->gap_scan_event_handler(ble_event->scan_result());
439 }
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;
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 for (auto *gap_handler : this->gap_event_handlers_) {
484 gap_handler->gap_event_handler(gap_event, param);
485 }
486 }
487#endif
488 break;
489
490 default:
491 // Unknown/unhandled event
492 ESP_LOGW(TAG, "Unhandled GAP event type in loop: %d", gap_event);
493 break;
494 }
495 break;
496 }
497 default:
498 break;
499 }
500 // Return the event to the pool
501 this->ble_event_pool_.release(ble_event);
502 ble_event = this->ble_events_.pop();
503 }
504#ifdef USE_ESP32_BLE_ADVERTISING
505 if (this->advertising_ != nullptr) {
506 this->advertising_->loop();
507 }
508#endif
509
510 // Log dropped events periodically
511 uint16_t dropped = this->ble_events_.get_and_reset_dropped_count();
512 if (dropped > 0) {
513 ESP_LOGW(TAG, "Dropped %u BLE events due to buffer overflow", dropped);
514 }
515}
516
517void ESP32BLE::loop_handle_state_transition_not_active_() {
518 // Caller ensures state_ != ACTIVE
519 if (this->state_ == BLE_COMPONENT_STATE_DISABLE) {
520 ESP_LOGD(TAG, "Disabling");
521
522#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
523 for (auto *ble_event_handler : this->ble_status_event_handlers_) {
524 ble_event_handler->ble_before_disabled_event_handler();
525 }
526#endif
527
528 if (!ble_dismantle_()) {
529 ESP_LOGE(TAG, "Could not be dismantled");
530 this->mark_failed();
531 return;
532 }
533 this->state_ = BLE_COMPONENT_STATE_DISABLED;
534 } else if (this->state_ == BLE_COMPONENT_STATE_ENABLE) {
535 ESP_LOGD(TAG, "Enabling");
536 this->state_ = BLE_COMPONENT_STATE_OFF;
537
538 if (!ble_setup_()) {
539 ESP_LOGE(TAG, "Could not be set up");
540 this->mark_failed();
541 return;
542 }
543
544 this->state_ = BLE_COMPONENT_STATE_ACTIVE;
545 }
546}
547
548// Helper function to load new event data based on type
549void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
550 event->load_gap_event(e, p);
551}
552
553#ifdef USE_ESP32_BLE_CLIENT
554void load_ble_event(BLEEvent *event, esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
555 event->load_gattc_event(e, i, p);
556}
557#endif
558
559#ifdef USE_ESP32_BLE_SERVER
560void load_ble_event(BLEEvent *event, esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
561 event->load_gatts_event(e, i, p);
562}
563#endif
564
565template<typename... Args> void enqueue_ble_event(Args... args) {
566 // Allocate an event from the pool
567 BLEEvent *event = global_ble->ble_event_pool_.allocate();
568 if (event == nullptr) {
569 // No events available - queue is full or we're out of memory
570 global_ble->ble_events_.increment_dropped_count();
571 return;
572 }
573
574 // Load new event data (replaces previous event)
575 load_ble_event(event, args...);
576
577 // Push the event to the queue
578 global_ble->ble_events_.push(event);
579 // Push always succeeds because we're the only producer and the pool ensures we never exceed queue size
580}
581
582// Explicit template instantiations for the friend function
583template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *);
584#ifdef USE_ESP32_BLE_SERVER
585template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *);
586#endif
587#ifdef USE_ESP32_BLE_CLIENT
588template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *);
589#endif
590
591void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
592 switch (event) {
593 // Queue GAP events that components need to handle
594 // Scanning events - used by esp32_ble_tracker
595 case ESP_GAP_BLE_SCAN_RESULT_EVT:
596 GAP_SCAN_COMPLETE_EVENTS:
597 // Advertising events - used by esp32_ble_beacon and esp32_ble server
598 GAP_ADV_COMPLETE_EVENTS:
599 // Connection events - used by ble_client
600 case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
601 enqueue_ble_event(event, param);
602 return;
603
604 // Security events - used by ble_client and bluetooth_proxy
605 // These are rare but interactive (pairing/bonding), so notify immediately
606 GAP_SECURITY_EVENTS:
607 enqueue_ble_event(event, param);
608 // Wake up main loop to process security event immediately
609#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
611#endif
612 return;
613
614 // Ignore these GAP events as they are not relevant for our use case
615 case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
616 case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
617 case ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT: // BLE 5.0 PHY update complete
618 case ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT: // BLE 5.0 channel selection algorithm
619 return;
620
621 default:
622 break;
623 }
624 ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event);
625}
626
627#ifdef USE_ESP32_BLE_SERVER
628void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
629 esp_ble_gatts_cb_param_t *param) {
630 enqueue_ble_event(event, gatts_if, param);
631 // Wake up main loop to process GATT event immediately
632#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
634#endif
635}
636#endif
637
638#ifdef USE_ESP32_BLE_CLIENT
639void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
640 esp_ble_gattc_cb_param_t *param) {
641 enqueue_ble_event(event, gattc_if, param);
642 // Wake up main loop to process GATT event immediately
643#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
645#endif
646}
647#endif
648
650
652 const uint8_t *mac_address = esp_bt_dev_get_address();
653 if (mac_address) {
654 const char *io_capability_s;
655 switch (this->io_cap_) {
656 case ESP_IO_CAP_OUT:
657 io_capability_s = "display_only";
658 break;
659 case ESP_IO_CAP_IO:
660 io_capability_s = "display_yes_no";
661 break;
662 case ESP_IO_CAP_IN:
663 io_capability_s = "keyboard_only";
664 break;
665 case ESP_IO_CAP_NONE:
666 io_capability_s = "none";
667 break;
668 case ESP_IO_CAP_KBDISP:
669 io_capability_s = "keyboard_display";
670 break;
671 default:
672 io_capability_s = "invalid";
673 break;
674 }
675
676 char mac_s[18];
677 format_mac_addr_upper(mac_address, mac_s);
678 ESP_LOGCONFIG(TAG,
679 "BLE:\n"
680 " MAC address: %s\n"
681 " IO Capability: %s",
682 mac_s, io_capability_s);
683
684#ifdef ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
685 const char *auth_req_mode_s = "<default>";
686 if (this->auth_req_mode_) {
687 switch (this->auth_req_mode_.value()) {
688 case AUTH_REQ_NO_BOND:
689 auth_req_mode_s = "no_bond";
690 break;
691 case AUTH_REQ_BOND:
692 auth_req_mode_s = "bond";
693 break;
694 case AUTH_REQ_MITM:
695 auth_req_mode_s = "mitm";
696 break;
698 auth_req_mode_s = "bond_mitm";
699 break;
700 case AUTH_REQ_SC_ONLY:
701 auth_req_mode_s = "sc_only";
702 break;
703 case AUTH_REQ_SC_BOND:
704 auth_req_mode_s = "sc_bond";
705 break;
706 case AUTH_REQ_SC_MITM:
707 auth_req_mode_s = "sc_mitm";
708 break;
710 auth_req_mode_s = "sc_mitm_bond";
711 break;
712 }
713 }
714
715 ESP_LOGCONFIG(TAG, " Auth Req Mode: %s", auth_req_mode_s);
716 if (this->max_key_size_ && this->min_key_size_) {
717 ESP_LOGCONFIG(TAG, " Key Size: %u - %u", this->min_key_size_, this->max_key_size_);
718 } else if (this->max_key_size_) {
719 ESP_LOGCONFIG(TAG, " Key Size: <default> - %u", this->max_key_size_);
720 } else if (this->min_key_size_) {
721 ESP_LOGCONFIG(TAG, " Key Size: %u - <default>", this->min_key_size_);
722 }
723#endif // ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS
724
725 } else {
726 ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled");
727 }
728}
729
730uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) {
731 uint64_t u = 0;
732 u |= uint64_t(address[0] & 0xFF) << 40;
733 u |= uint64_t(address[1] & 0xFF) << 32;
734 u |= uint64_t(address[2] & 0xFF) << 24;
735 u |= uint64_t(address[3] & 0xFF) << 16;
736 u |= uint64_t(address[4] & 0xFF) << 8;
737 u |= uint64_t(address[5] & 0xFF) << 0;
738 return u;
739}
740
741ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
742
743} // namespace esphome::esp32_ble
744
745#endif
uint8_t address
Definition bl0906.h:4
bool btInUse()
Definition ble.cpp:29
void wake_loop_threadsafe()
Wake the main event loop from another FreeRTOS task.
bool is_name_add_mac_suffix_enabled() const
const std::string & get_name() const
Get the name of this Application set by pre_setup().
void mark_failed()
Mark this component as failed.
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:100
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
Definition ble.cpp:591
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:639
friend void enqueue_ble_event(Args... args)
Definition ble.cpp:565
void advertising_register_raw_advertisement_callback(std::function< void(bool)> &&callback)
Definition ble.cpp:128
void advertising_set_service_data_and_name(std::span< const uint8_t > data, bool include_name)
Definition ble.cpp:106
void advertising_add_service_uuid(ESPBTUUID uuid)
Definition ble.cpp:133
void dump_config() override
Definition ble.cpp:651
void loop() override
Definition ble.cpp:398
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:628
void advertising_set_service_data(const std::vector< uint8_t > &data)
Definition ble.cpp:94
float get_setup_priority() const override
Definition ble.cpp:649
void setup() override
Definition ble.cpp:56
void advertising_remove_service_uuid(ESPBTUUID uuid)
Definition ble.cpp:139
value_type const & value() const
Definition optional.h:94
ESP32BLE * global_ble
Definition ble.cpp:741
void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p)
Definition ble.cpp:549
@ BLE_COMPONENT_STATE_DISABLE
BLE should be disabled on next loop.
Definition ble.h:72
@ BLE_COMPONENT_STATE_OFF
Nothing has been initialized yet.
Definition ble.h:70
@ BLE_COMPONENT_STATE_ENABLE
BLE should be enabled on next loop.
Definition ble.h:76
@ BLE_COMPONENT_STATE_DISABLED
BLE is disabled.
Definition ble.h:74
@ BLE_COMPONENT_STATE_ACTIVE
BLE is active.
Definition ble.h:78
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address)
Definition ble.cpp:730
@ AUTH_REQ_SC_MITM_BOND
Definition ble.h:64
@ AUTH_REQ_BOND_MITM
Definition ble.h:60
void enqueue_ble_event(Args... args)
Definition ble.cpp:565
constexpr float BLUETOOTH
Definition component.h:34
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:261
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:811
void HOT delay(uint32_t ms)
Definition core.cpp:27
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:1170