mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Fix missing BLE GAP events causing RSSI sensor and beacon failures (#9138)
This commit is contained in:
		| @@ -324,14 +324,18 @@ 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) { | ||||
|         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()); | ||||
|             } | ||||
|         } 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) { | ||||
|             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 | ||||
| @@ -341,6 +345,48 @@ void ESP32BLE::loop() { | ||||
|               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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user