mirror of
https://github.com/esphome/esphome.git
synced 2025-07-29 06:36:45 +00:00
Fix missing BLE GAP events causing RSSI sensor and beacon failures (#9138)
This commit is contained in:
parent
a9e1a4cef3
commit
9c90ca297a
@ -324,23 +324,69 @@ void ESP32BLE::loop() {
|
|||||||
}
|
}
|
||||||
case BLEEvent::GAP: {
|
case BLEEvent::GAP: {
|
||||||
esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
|
esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
|
||||||
if (gap_event == ESP_GAP_BLE_SCAN_RESULT_EVT) {
|
switch (gap_event) {
|
||||||
// Use the new scan event handler - no memcpy!
|
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||||
for (auto *scan_handler : this->gap_scan_event_handlers_) {
|
// Use the new scan event handler - no memcpy!
|
||||||
scan_handler->gap_scan_event_handler(ble_event->scan_result());
|
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 ||
|
break;
|
||||||
gap_event == ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT) {
|
|
||||||
// All three scan complete events have the same structure with just status
|
// Scan complete events
|
||||||
// The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
|
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||||
// This is verified at compile-time by static_assert checks in ble_event.h
|
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||||
// The struct already contains our copy of the status (copied in BLEEvent constructor)
|
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
// All three scan complete events have the same structure with just status
|
||||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
// The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
|
||||||
gap_handler->gap_event_handler(
|
// This is verified at compile-time by static_assert checks in ble_event.h
|
||||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
|
// 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;
|
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) {
|
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||||
switch (event) {
|
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_RESULT_EVT:
|
||||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||||
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||||
case ESP_GAP_BLE_SCAN_STOP_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);
|
enqueue_ble_event(event, param);
|
||||||
return;
|
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");
|
"ESP-IDF scan_stop_cmpl structure has unexpected size");
|
||||||
|
|
||||||
// Verify the status field is at offset 0 (first member)
|
// Verify the status field is at offset 0 (first member)
|
||||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) ==
|
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) == 0,
|
||||||
offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl),
|
|
||||||
"status must be first member of scan_param_cmpl");
|
"status must be first member of scan_param_cmpl");
|
||||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) ==
|
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) == 0,
|
||||||
offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl),
|
|
||||||
"status must be first member of scan_start_cmpl");
|
"status must be first member of scan_start_cmpl");
|
||||||
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) ==
|
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) == 0,
|
||||||
offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl),
|
|
||||||
"status must be first member of scan_stop_cmpl");
|
"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().
|
// 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.
|
// This class stores each event with minimal memory usage.
|
||||||
// GAP events (99% of traffic) don't have the vector overhead.
|
// GAP events (99% of traffic) don't have the vector overhead.
|
||||||
@ -67,6 +96,17 @@ class BLEEvent {
|
|||||||
GATTS,
|
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
|
// Constructor for GAP events - no external allocations needed
|
||||||
BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
||||||
this->type_ = GAP;
|
this->type_ = GAP;
|
||||||
@ -147,12 +187,21 @@ class BLEEvent {
|
|||||||
struct gap_event {
|
struct gap_event {
|
||||||
esp_gap_ble_cb_event_t gap_event;
|
esp_gap_ble_cb_event_t gap_event;
|
||||||
union {
|
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
|
// This matches ESP-IDF's scan complete event structures
|
||||||
// All three (scan_param_cmpl, scan_start_cmpl, scan_stop_cmpl) have identical layout
|
// All three (scan_param_cmpl, scan_start_cmpl, scan_stop_cmpl) have identical layout
|
||||||
struct {
|
// Used by: esp32_ble_tracker
|
||||||
esp_bt_status_t status;
|
StatusOnlyData scan_complete; // 1 byte
|
||||||
} 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
|
} 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; }
|
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; }
|
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 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:
|
private:
|
||||||
// Initialize GAP event data
|
// Initialize GAP event data
|
||||||
@ -215,8 +267,47 @@ class BLEEvent {
|
|||||||
this->event_.gap.scan_complete.status = p->scan_stop_cmpl.status;
|
this->event_.gap.scan_complete.status = p->scan_stop_cmpl.status;
|
||||||
break;
|
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:
|
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;
|
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)
|
// BLEEvent total size: 84 bytes (80 byte union + 1 byte type + 3 bytes padding)
|
||||||
|
|
||||||
} // namespace esp32_ble
|
} // namespace esp32_ble
|
||||||
|
Loading…
x
Reference in New Issue
Block a user