mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[esp32_ble_tracker] Remove unnecessary STOPPED scanner state to reduce latency (#10055)
This commit is contained in:
		| @@ -185,9 +185,6 @@ void ESP32BLETracker::loop() { | |||||||
|       ESP_LOGW(TAG, "Dropped %zu BLE scan results due to buffer overflow", dropped); |       ESP_LOGW(TAG, "Dropped %zu BLE scan results due to buffer overflow", dropped); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   if (this->scanner_state_ == ScannerState::STOPPED) { |  | ||||||
|     this->end_of_scan_();  // Change state to IDLE |  | ||||||
|   } |  | ||||||
|   if (this->scanner_state_ == ScannerState::FAILED || |   if (this->scanner_state_ == ScannerState::FAILED || | ||||||
|       (this->scan_set_param_failed_ && this->scanner_state_ == ScannerState::RUNNING)) { |       (this->scan_set_param_failed_ && this->scanner_state_ == ScannerState::RUNNING)) { | ||||||
|     this->stop_scan_(); |     this->stop_scan_(); | ||||||
| @@ -278,8 +275,6 @@ void ESP32BLETracker::stop_scan_() { | |||||||
|       ESP_LOGE(TAG, "Scan is starting while trying to stop."); |       ESP_LOGE(TAG, "Scan is starting while trying to stop."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::STOPPING) { |     } else if (this->scanner_state_ == ScannerState::STOPPING) { | ||||||
|       ESP_LOGE(TAG, "Scan is already stopping while trying to stop."); |       ESP_LOGE(TAG, "Scan is already stopping while trying to stop."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::STOPPED) { |  | ||||||
|       ESP_LOGE(TAG, "Scan is already stopped while trying to stop."); |  | ||||||
|     } |     } | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -306,8 +301,6 @@ void ESP32BLETracker::start_scan_(bool first) { | |||||||
|       ESP_LOGE(TAG, "Cannot start scan while already stopping."); |       ESP_LOGE(TAG, "Cannot start scan while already stopping."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::FAILED) { |     } else if (this->scanner_state_ == ScannerState::FAILED) { | ||||||
|       ESP_LOGE(TAG, "Cannot start scan while already failed."); |       ESP_LOGE(TAG, "Cannot start scan while already failed."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::STOPPED) { |  | ||||||
|       ESP_LOGE(TAG, "Cannot start scan while already stopped."); |  | ||||||
|     } |     } | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -342,21 +335,6 @@ void ESP32BLETracker::start_scan_(bool first) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLETracker::end_of_scan_() { |  | ||||||
|   // The lock must be held when calling this function. |  | ||||||
|   if (this->scanner_state_ != ScannerState::STOPPED) { |  | ||||||
|     ESP_LOGE(TAG, "end_of_scan_ called while scanner is not stopped."); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   ESP_LOGD(TAG, "End of scan, set scanner state to IDLE."); |  | ||||||
|   this->already_discovered_.clear(); |  | ||||||
|   this->cancel_timeout("scan"); |  | ||||||
|  |  | ||||||
|   for (auto *listener : this->listeners_) |  | ||||||
|     listener->on_scan_end(); |  | ||||||
|   this->set_scanner_state_(ScannerState::IDLE); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ESP32BLETracker::register_client(ESPBTClient *client) { | void ESP32BLETracker::register_client(ESPBTClient *client) { | ||||||
|   client->app_id = ++this->app_id_; |   client->app_id = ++this->app_id_; | ||||||
|   this->clients_.push_back(client); |   this->clients_.push_back(client); | ||||||
| @@ -389,6 +367,8 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { | void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { | ||||||
|  |   // Note: This handler is called from the main loop context, not directly from the BT task. | ||||||
|  |   // The esp32_ble component queues events via enqueue_ble_event() and processes them in loop(). | ||||||
|   switch (event) { |   switch (event) { | ||||||
|     case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: |     case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: | ||||||
|       this->gap_scan_set_param_complete_(param->scan_param_cmpl); |       this->gap_scan_set_param_complete_(param->scan_param_cmpl); | ||||||
| @@ -409,11 +389,13 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga | |||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) { | void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) { | ||||||
|  |   // Note: This handler is called from the main loop context via esp32_ble's event queue. | ||||||
|  |   // However, we still use a lock-free ring buffer to batch results efficiently. | ||||||
|   ESP_LOGV(TAG, "gap_scan_result - event %d", scan_result.search_evt); |   ESP_LOGV(TAG, "gap_scan_result - event %d", scan_result.search_evt); | ||||||
|  |  | ||||||
|   if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { |   if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { | ||||||
|     // Lock-free SPSC ring buffer write (Producer side) |     // Ring buffer write (Producer side) | ||||||
|     // This runs in the ESP-IDF Bluetooth stack callback thread |     // Even though we're in the main loop, the ring buffer design allows efficient batching | ||||||
|     // IMPORTANT: Only this thread writes to ring_write_index_ |     // IMPORTANT: Only this thread writes to ring_write_index_ | ||||||
|  |  | ||||||
|     // Load our own index with relaxed ordering (we're the only writer) |     // Load our own index with relaxed ordering (we're the only writer) | ||||||
| @@ -445,15 +427,15 @@ void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) { | |||||||
|         ESP_LOGE(TAG, "Scan was in failed state when scan completed."); |         ESP_LOGE(TAG, "Scan was in failed state when scan completed."); | ||||||
|       } else if (this->scanner_state_ == ScannerState::IDLE) { |       } else if (this->scanner_state_ == ScannerState::IDLE) { | ||||||
|         ESP_LOGE(TAG, "Scan was idle when scan completed."); |         ESP_LOGE(TAG, "Scan was idle when scan completed."); | ||||||
|       } else if (this->scanner_state_ == ScannerState::STOPPED) { |  | ||||||
|         ESP_LOGE(TAG, "Scan was stopped when scan completed."); |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this->set_scanner_state_(ScannerState::STOPPED); |     // Scan completed naturally, perform cleanup and transition to IDLE | ||||||
|  |     this->cleanup_scan_state_(false); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { | void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { | ||||||
|  |   // Called from main loop context via gap_event_handler after being queued from BT task | ||||||
|   ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status); |   ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status); | ||||||
|   if (param.status == ESP_BT_STATUS_DONE) { |   if (param.status == ESP_BT_STATUS_DONE) { | ||||||
|     this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS; |     this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS; | ||||||
| @@ -463,6 +445,7 @@ void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t: | |||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m) { | void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m) { | ||||||
|  |   // Called from main loop context via gap_event_handler after being queued from BT task | ||||||
|   ESP_LOGV(TAG, "gap_scan_start_complete - status %d", param.status); |   ESP_LOGV(TAG, "gap_scan_start_complete - status %d", param.status); | ||||||
|   this->scan_start_failed_ = param.status; |   this->scan_start_failed_ = param.status; | ||||||
|   if (this->scanner_state_ != ScannerState::STARTING) { |   if (this->scanner_state_ != ScannerState::STARTING) { | ||||||
| @@ -474,8 +457,6 @@ void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble | |||||||
|       ESP_LOGE(TAG, "Scan was in failed state when start complete."); |       ESP_LOGE(TAG, "Scan was in failed state when start complete."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::IDLE) { |     } else if (this->scanner_state_ == ScannerState::IDLE) { | ||||||
|       ESP_LOGE(TAG, "Scan was idle when start complete."); |       ESP_LOGE(TAG, "Scan was idle when start complete."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::STOPPED) { |  | ||||||
|       ESP_LOGE(TAG, "Scan was stopped when start complete."); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   if (param.status == ESP_BT_STATUS_SUCCESS) { |   if (param.status == ESP_BT_STATUS_SUCCESS) { | ||||||
| @@ -490,6 +471,8 @@ void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble | |||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m) { | void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m) { | ||||||
|  |   // Called from main loop context via gap_event_handler after being queued from BT task | ||||||
|  |   // This allows us to safely transition to IDLE state and perform cleanup without race conditions | ||||||
|   ESP_LOGV(TAG, "gap_scan_stop_complete - status %d", param.status); |   ESP_LOGV(TAG, "gap_scan_stop_complete - status %d", param.status); | ||||||
|   if (this->scanner_state_ != ScannerState::STOPPING) { |   if (this->scanner_state_ != ScannerState::STOPPING) { | ||||||
|     if (this->scanner_state_ == ScannerState::RUNNING) { |     if (this->scanner_state_ == ScannerState::RUNNING) { | ||||||
| @@ -500,11 +483,11 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_ | |||||||
|       ESP_LOGE(TAG, "Scan was in failed state when stop complete."); |       ESP_LOGE(TAG, "Scan was in failed state when stop complete."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::IDLE) { |     } else if (this->scanner_state_ == ScannerState::IDLE) { | ||||||
|       ESP_LOGE(TAG, "Scan was idle when stop complete."); |       ESP_LOGE(TAG, "Scan was idle when stop complete."); | ||||||
|     } else if (this->scanner_state_ == ScannerState::STOPPED) { |  | ||||||
|       ESP_LOGE(TAG, "Scan was stopped when stop complete."); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   this->set_scanner_state_(ScannerState::STOPPED); |  | ||||||
|  |   // Perform cleanup and transition to IDLE | ||||||
|  |   this->cleanup_scan_state_(true); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
| @@ -794,9 +777,6 @@ void ESP32BLETracker::dump_config() { | |||||||
|     case ScannerState::STOPPING: |     case ScannerState::STOPPING: | ||||||
|       ESP_LOGCONFIG(TAG, "  Scanner State: STOPPING"); |       ESP_LOGCONFIG(TAG, "  Scanner State: STOPPING"); | ||||||
|       break; |       break; | ||||||
|     case ScannerState::STOPPED: |  | ||||||
|       ESP_LOGCONFIG(TAG, "  Scanner State: STOPPED"); |  | ||||||
|       break; |  | ||||||
|     case ScannerState::FAILED: |     case ScannerState::FAILED: | ||||||
|       ESP_LOGCONFIG(TAG, "  Scanner State: FAILED"); |       ESP_LOGCONFIG(TAG, "  Scanner State: FAILED"); | ||||||
|       break; |       break; | ||||||
| @@ -881,6 +861,17 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const { | |||||||
| } | } | ||||||
| #endif  // USE_ESP32_BLE_DEVICE | #endif  // USE_ESP32_BLE_DEVICE | ||||||
|  |  | ||||||
|  | void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) { | ||||||
|  |   ESP_LOGD(TAG, "Scan %scomplete, set scanner state to IDLE.", is_stop_complete ? "stop " : ""); | ||||||
|  |   this->already_discovered_.clear(); | ||||||
|  |   this->cancel_timeout("scan"); | ||||||
|  |  | ||||||
|  |   for (auto *listener : this->listeners_) | ||||||
|  |     listener->on_scan_end(); | ||||||
|  |  | ||||||
|  |   this->set_scanner_state_(ScannerState::IDLE); | ||||||
|  | } | ||||||
|  |  | ||||||
| }  // namespace esphome::esp32_ble_tracker | }  // namespace esphome::esp32_ble_tracker | ||||||
|  |  | ||||||
| #endif  // USE_ESP32 | #endif  // USE_ESP32 | ||||||
|   | |||||||
| @@ -158,18 +158,16 @@ enum class ClientState : uint8_t { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| enum class ScannerState { | enum class ScannerState { | ||||||
|   // Scanner is idle, init state, set from the main loop when processing STOPPED |   // Scanner is idle, init state | ||||||
|   IDLE, |   IDLE, | ||||||
|   // Scanner is starting, set from the main loop only |   // Scanner is starting | ||||||
|   STARTING, |   STARTING, | ||||||
|   // Scanner is running, set from the ESP callback only |   // Scanner is running | ||||||
|   RUNNING, |   RUNNING, | ||||||
|   // Scanner failed to start, set from the ESP callback only |   // Scanner failed to start | ||||||
|   FAILED, |   FAILED, | ||||||
|   // Scanner is stopping, set from the main loop only |   // Scanner is stopping | ||||||
|   STOPPING, |   STOPPING, | ||||||
|   // Scanner is stopped, set from the ESP callback only |  | ||||||
|   STOPPED, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum class ConnectionType : uint8_t { | enum class ConnectionType : uint8_t { | ||||||
| @@ -262,8 +260,6 @@ class ESP32BLETracker : public Component, | |||||||
|   void stop_scan_(); |   void stop_scan_(); | ||||||
|   /// Start a single scan by setting up the parameters and doing some esp-idf calls. |   /// Start a single scan by setting up the parameters and doing some esp-idf calls. | ||||||
|   void start_scan_(bool first); |   void start_scan_(bool first); | ||||||
|   /// Called when a scan ends |  | ||||||
|   void end_of_scan_(); |  | ||||||
|   /// Called when a `ESP_GAP_BLE_SCAN_RESULT_EVT` event is received. |   /// Called when a `ESP_GAP_BLE_SCAN_RESULT_EVT` event is received. | ||||||
|   void gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); |   void gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); | ||||||
|   /// Called when a `ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT` event is received. |   /// Called when a `ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT` event is received. | ||||||
| @@ -274,6 +270,8 @@ class ESP32BLETracker : public Component, | |||||||
|   void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m); |   void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m); | ||||||
|   /// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed. |   /// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed. | ||||||
|   void set_scanner_state_(ScannerState state); |   void set_scanner_state_(ScannerState state); | ||||||
|  |   /// Common cleanup logic when transitioning scanner to IDLE state | ||||||
|  |   void cleanup_scan_state_(bool is_stop_complete); | ||||||
|  |  | ||||||
|   uint8_t app_id_{0}; |   uint8_t app_id_{0}; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user