From 22ad0f2f2d06ce2de40e9f5b258363972329f570 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Jan 2026 23:09:45 -1000 Subject: [PATCH 1/2] handle race --- esphome/components/wifi/wifi_component.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index b95def4121..b9b084168c 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1968,18 +1968,13 @@ void WiFiComponent::clear_roaming_state_() { } void WiFiComponent::check_roaming_(uint32_t now) { - // Guard: must have valid RSSI reading - int8_t current_rssi = this->wifi_rssi(); - if (current_rssi == WIFI_RSSI_DISCONNECTED) - return; - // Guard: not for hidden networks (may not appear in scan) const WiFiAP *selected = this->get_selected_sta_(); if (selected == nullptr || selected->get_hidden()) return; this->roaming_last_check_ = now; - ESP_LOGD(TAG, "Roaming: scanning for better AP (current RSSI %d dBm)", current_rssi); + ESP_LOGD(TAG, "Roaming: scanning for better AP (current RSSI %d dBm)", this->wifi_rssi()); this->roaming_scan_active_ = true; this->wifi_scan_start_(this->passive_scan_); } @@ -1996,6 +1991,13 @@ void WiFiComponent::process_roaming_scan_() { // Get current connection info bssid_t current_bssid = this->wifi_bssid(); int8_t current_rssi = this->wifi_rssi(); + + // Guard: must still be connected (RSSI may have become invalid during scan) + if (current_rssi == WIFI_RSSI_DISCONNECTED) { + this->release_scan_results_(); + return; + } + char ssid_buf[SSID_BUFFER_SIZE]; const char *current_ssid = this->wifi_ssid_to(ssid_buf); From 2ab27a6ae23be76c217e13a553e7ad58eb1bf481 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Jan 2026 23:11:30 -1000 Subject: [PATCH 2/2] avoid inlining expensive vector ops --- esphome/components/wifi/wifi_component.cpp | 18 ++++++++++++++++++ esphome/components/wifi/wifi_component.h | 18 ++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index b9b084168c..c3727ee520 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1545,6 +1545,12 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { return false; // Did not start scan, can proceed with connection } +void WiFiComponent::clear_all_bssid_priorities_() { + if (!this->sta_priorities_.empty()) { + decltype(this->sta_priorities_)().swap(this->sta_priorities_); + } +} + /// Clear BSSID priority tracking if all priorities are at minimum (saves memory) /// At minimum priority, all BSSIDs are equally bad, so priority tracking is useless /// Called after successful connection or after failed connection attempts @@ -1967,6 +1973,18 @@ void WiFiComponent::clear_roaming_state_() { this->roaming_connect_active_ = false; } +void WiFiComponent::release_scan_results_() { + if (!this->keep_scan_results_) { +#ifdef USE_RP2040 + // std::vector - use swap trick since shrink_to_fit is non-binding + decltype(this->scan_result_)().swap(this->scan_result_); +#else + // FixedVector::shrink_to_fit() actually frees all memory + this->scan_result_.shrink_to_fit(); +#endif + } +} + void WiFiComponent::check_roaming_(uint32_t now) { // Guard: not for hidden networks (may not appear in scan) const WiFiAP *selected = this->get_selected_sta_(); diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 56d8357f59..c6015fc9eb 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -502,11 +502,7 @@ class WiFiComponent : public Component { /// Log failed connection and decrease BSSID priority to avoid repeated attempts void log_and_adjust_priority_for_failed_connect_(); /// Clear all BSSID priority penalties (e.g., after successful connection) - void clear_all_bssid_priorities_() { - if (!this->sta_priorities_.empty()) { - decltype(this->sta_priorities_)().swap(this->sta_priorities_); - } - } + void clear_all_bssid_priorities_(); /// Clear BSSID priority tracking if all priorities are at minimum (saves memory) void clear_priorities_if_all_min_(); /// Advance to next target (AP/SSID) within current phase, or increment retry counter @@ -576,17 +572,7 @@ class WiFiComponent : public Component { void clear_roaming_state_(); /// Free scan results memory unless a component needs them - void release_scan_results_() { - if (!this->keep_scan_results_) { -#ifdef USE_RP2040 - // std::vector - use swap trick since shrink_to_fit is non-binding - decltype(this->scan_result_)().swap(this->scan_result_); -#else - // FixedVector::shrink_to_fit() actually frees all memory - this->scan_result_.shrink_to_fit(); -#endif - } - } + void release_scan_results_(); #ifdef USE_ESP8266 static void wifi_event_callback(System_Event_t *event);