mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
Fix missing BLE GAP events causing RSSI sensor and beacon failures (#9138)
This commit is contained in:
parent
24587fe875
commit
6dfb9eba61
@ -324,23 +324,69 @@ void ESP32BLE::loop() {
|
||||
}
|
||||
case BLEEvent::GAP: {
|
||||
esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
|
||||
if (gap_event == ESP_GAP_BLE_SCAN_RESULT_EVT) {
|
||||
// Use the new scan event handler - no memcpy!
|
||||
for (auto *scan_handler : this->gap_scan_event_handlers_) {
|
||||
scan_handler->gap_scan_event_handler(ble_event->scan_result());
|
||||
}
|
||||
} else if (gap_event == ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT ||
|
||||
gap_event == ESP_GAP_BLE_SCAN_START_COMPLETE_EVT ||
|
||||
gap_event == ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT) {
|
||||
// All three scan complete events have the same structure with just status
|
||||
// The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
|
||||
// This is verified at compile-time by static_assert checks in ble_event.h
|
||||
// The struct already contains our copy of the status (copied in BLEEvent constructor)
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
|
||||
}
|
||||
switch (gap_event) {
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||
// Use the new scan event handler - no memcpy!
|
||||
for (auto *scan_handler : this->gap_scan_event_handlers_) {
|
||||
scan_handler->gap_scan_event_handler(ble_event->scan_result());
|
||||
}
|
||||
break;
|
||||
|
||||
// Scan complete events
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||
// All three scan complete events have the same structure with just status
|
||||
// The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
|
||||
// This is verified at compile-time by static_assert checks in ble_event.h
|
||||
// The struct already contains our copy of the status (copied in BLEEvent constructor)
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
|
||||
}
|
||||
break;
|
||||
|
||||
// Advertising complete events
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
// All advertising complete events have the same structure with just status
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete));
|
||||
}
|
||||
break;
|
||||
|
||||
// RSSI complete event
|
||||
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete));
|
||||
}
|
||||
break;
|
||||
|
||||
// Security events
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT:
|
||||
case ESP_GAP_BLE_NC_REQ_EVT:
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown/unhandled event
|
||||
ESP_LOGW(TAG, "Unhandled GAP event type in loop: %d", gap_event);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -399,11 +445,26 @@ template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gat
|
||||
|
||||
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||
switch (event) {
|
||||
// Only queue the 4 GAP events we actually handle
|
||||
// Queue GAP events that components need to handle
|
||||
// Scanning events - used by esp32_ble_tracker
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||
// Advertising events - used by esp32_ble_beacon and esp32_ble server
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
// Connection events - used by ble_client
|
||||
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
|
||||
// Security events - used by ble_client and bluetooth_proxy
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT:
|
||||
case ESP_GAP_BLE_NC_REQ_EVT:
|
||||
enqueue_ble_event(event, param);
|
||||
return;
|
||||
|
||||
|
@ -24,16 +24,45 @@ static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param) == si
|
||||
"ESP-IDF scan_stop_cmpl structure has unexpected size");
|
||||
|
||||
// Verify the status field is at offset 0 (first member)
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) ==
|
||||
offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl),
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) == 0,
|
||||
"status must be first member of scan_param_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) ==
|
||||
offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl),
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) == 0,
|
||||
"status must be first member of scan_start_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) ==
|
||||
offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl),
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) == 0,
|
||||
"status must be first member of scan_stop_cmpl");
|
||||
|
||||
// Compile-time verification for advertising complete events
|
||||
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_data_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||
"ESP-IDF adv_data_cmpl structure has unexpected size");
|
||||
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_rsp_data_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||
"ESP-IDF scan_rsp_data_cmpl structure has unexpected size");
|
||||
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_data_raw_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||
"ESP-IDF adv_data_raw_cmpl structure has unexpected size");
|
||||
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_start_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||
"ESP-IDF adv_start_cmpl structure has unexpected size");
|
||||
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_stop_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||
"ESP-IDF adv_stop_cmpl structure has unexpected size");
|
||||
|
||||
// Verify the status field is at offset 0 for advertising events
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_data_cmpl.status) == 0,
|
||||
"status must be first member of adv_data_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_rsp_data_cmpl.status) == 0,
|
||||
"status must be first member of scan_rsp_data_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_data_raw_cmpl.status) == 0,
|
||||
"status must be first member of adv_data_raw_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_start_cmpl.status) == 0,
|
||||
"status must be first member of adv_start_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_stop_cmpl.status) == 0,
|
||||
"status must be first member of adv_stop_cmpl");
|
||||
|
||||
// Compile-time verification for RSSI complete event structure
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.status) == 0,
|
||||
"status must be first member of read_rssi_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.rssi) == sizeof(esp_bt_status_t),
|
||||
"rssi must immediately follow status in read_rssi_cmpl");
|
||||
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t),
|
||||
"remote_addr must follow rssi in read_rssi_cmpl");
|
||||
|
||||
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
|
||||
// This class stores each event with minimal memory usage.
|
||||
// GAP events (99% of traffic) don't have the vector overhead.
|
||||
@ -67,6 +96,17 @@ class BLEEvent {
|
||||
GATTS,
|
||||
};
|
||||
|
||||
// Type definitions for cleaner method signatures
|
||||
struct StatusOnlyData {
|
||||
esp_bt_status_t status;
|
||||
};
|
||||
|
||||
struct RSSICompleteData {
|
||||
esp_bt_status_t status;
|
||||
int8_t rssi;
|
||||
esp_bd_addr_t remote_addr;
|
||||
};
|
||||
|
||||
// Constructor for GAP events - no external allocations needed
|
||||
BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
||||
this->type_ = GAP;
|
||||
@ -147,12 +187,21 @@ class BLEEvent {
|
||||
struct gap_event {
|
||||
esp_gap_ble_cb_event_t gap_event;
|
||||
union {
|
||||
BLEScanResult scan_result; // 73 bytes
|
||||
BLEScanResult scan_result; // 73 bytes - Used by: esp32_ble_tracker
|
||||
// This matches ESP-IDF's scan complete event structures
|
||||
// All three (scan_param_cmpl, scan_start_cmpl, scan_stop_cmpl) have identical layout
|
||||
struct {
|
||||
esp_bt_status_t status;
|
||||
} scan_complete; // 1 byte
|
||||
// Used by: esp32_ble_tracker
|
||||
StatusOnlyData scan_complete; // 1 byte
|
||||
// Advertising complete events all have same structure
|
||||
// Used by: esp32_ble_beacon, esp32_ble server components
|
||||
// ADV_DATA_SET, SCAN_RSP_DATA_SET, ADV_DATA_RAW_SET, ADV_START, ADV_STOP
|
||||
StatusOnlyData adv_complete; // 1 byte
|
||||
// RSSI complete event
|
||||
// Used by: ble_client (ble_rssi_sensor component)
|
||||
RSSICompleteData read_rssi_complete; // 8 bytes
|
||||
// Security events - we store the full security union
|
||||
// Used by: ble_client (automation), bluetooth_proxy, esp32_ble_client
|
||||
esp_ble_sec_t security; // Variable size, but fits within scan_result size
|
||||
};
|
||||
} gap; // 80 bytes total
|
||||
|
||||
@ -180,6 +229,9 @@ class BLEEvent {
|
||||
esp_gap_ble_cb_event_t gap_event_type() const { return event_.gap.gap_event; }
|
||||
const BLEScanResult &scan_result() const { return event_.gap.scan_result; }
|
||||
esp_bt_status_t scan_complete_status() const { return event_.gap.scan_complete.status; }
|
||||
esp_bt_status_t adv_complete_status() const { return event_.gap.adv_complete.status; }
|
||||
const RSSICompleteData &read_rssi_complete() const { return event_.gap.read_rssi_complete; }
|
||||
const esp_ble_sec_t &security() const { return event_.gap.security; }
|
||||
|
||||
private:
|
||||
// Initialize GAP event data
|
||||
@ -215,8 +267,47 @@ class BLEEvent {
|
||||
this->event_.gap.scan_complete.status = p->scan_stop_cmpl.status;
|
||||
break;
|
||||
|
||||
// Advertising complete events - all have same structure with just status
|
||||
// Used by: esp32_ble_beacon, esp32_ble server components
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
this->event_.gap.adv_complete.status = p->adv_data_cmpl.status;
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||
this->event_.gap.adv_complete.status = p->scan_rsp_data_cmpl.status;
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: // Used by: esp32_ble_beacon
|
||||
this->event_.gap.adv_complete.status = p->adv_data_raw_cmpl.status;
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: // Used by: esp32_ble_beacon
|
||||
this->event_.gap.adv_complete.status = p->adv_start_cmpl.status;
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: // Used by: esp32_ble_beacon
|
||||
this->event_.gap.adv_complete.status = p->adv_stop_cmpl.status;
|
||||
break;
|
||||
|
||||
// RSSI complete event
|
||||
// Used by: ble_client (ble_rssi_sensor)
|
||||
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
|
||||
this->event_.gap.read_rssi_complete.status = p->read_rssi_cmpl.status;
|
||||
this->event_.gap.read_rssi_complete.rssi = p->read_rssi_cmpl.rssi;
|
||||
memcpy(this->event_.gap.read_rssi_complete.remote_addr, p->read_rssi_cmpl.remote_addr, sizeof(esp_bd_addr_t));
|
||||
break;
|
||||
|
||||
// Security events - copy the entire security union
|
||||
// Used by: ble_client, bluetooth_proxy, esp32_ble_client
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT: // Used by: bluetooth_proxy, esp32_ble_client
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT: // Used by: esp32_ble_client
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // Used by: ble_client automation
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT: // Used by: ble_client automation
|
||||
case ESP_GAP_BLE_NC_REQ_EVT: // Used by: ble_client automation
|
||||
memcpy(&this->event_.gap.security, &p->ble_security, sizeof(esp_ble_sec_t));
|
||||
break;
|
||||
|
||||
default:
|
||||
// We only handle 4 GAP event types, others are dropped
|
||||
// We only store data for GAP events that components currently use
|
||||
// Unknown events still get queued and logged in ble.cpp:375 as
|
||||
// "Unhandled GAP event type in loop" - this helps identify new events
|
||||
// that components might need in the future
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -295,6 +386,13 @@ class BLEEvent {
|
||||
}
|
||||
};
|
||||
|
||||
// Verify the gap_event struct hasn't grown beyond expected size
|
||||
// The gap member in the union should be 80 bytes (including the gap_event enum)
|
||||
static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)) <= 80, "gap_event struct has grown beyond 80 bytes");
|
||||
|
||||
// Verify esp_ble_sec_t fits within our union
|
||||
static_assert(sizeof(esp_ble_sec_t) <= 73, "esp_ble_sec_t is larger than BLEScanResult");
|
||||
|
||||
// BLEEvent total size: 84 bytes (80 byte union + 1 byte type + 3 bytes padding)
|
||||
|
||||
} // namespace esp32_ble
|
||||
|
Loading…
x
Reference in New Issue
Block a user