[wifi] Fix WiFi recovery after failed connection attempts (#12483)

This commit is contained in:
J. Nick Koston
2025-12-14 16:26:34 -06:00
committed by GitHub
parent fa5b14fad4
commit ffce80f96c
3 changed files with 32 additions and 5 deletions

View File

@@ -205,6 +205,21 @@ static constexpr uint32_t WIFI_COOLDOWN_DURATION_MS = 500;
/// While connecting, WiFi can't beacon the AP properly, so needs longer cooldown
static constexpr uint32_t WIFI_COOLDOWN_WITH_AP_ACTIVE_MS = 30000;
/// Timeout for WiFi scan operations
/// This is a fallback in case we don't receive a scan done callback from the WiFi driver.
/// Normal scans complete via callback; this only triggers if something goes wrong.
static constexpr uint32_t WIFI_SCAN_TIMEOUT_MS = 31000;
/// Timeout for WiFi connection attempts
/// This is a fallback in case we don't receive connection success/failure callbacks.
/// Some platforms (especially LibreTiny/Beken) can take 30-60 seconds to connect,
/// particularly with fast_connect enabled where no prior scan provides channel info.
/// Do not lower this value - connection failures are detected via callbacks, not timeout.
/// If this timeout fires prematurely while a connection is still in progress, it causes
/// cascading failures: the subsequent scan will also fail because the WiFi driver is
/// still busy with the previous connection attempt.
static constexpr uint32_t WIFI_CONNECT_TIMEOUT_MS = 46000;
static constexpr uint8_t get_max_retries_for_phase(WiFiRetryPhase phase) {
switch (phase) {
case WiFiRetryPhase::INITIAL_CONNECT:
@@ -1035,7 +1050,7 @@ __attribute__((noinline)) static void log_scan_result(const WiFiScanResult &res)
void WiFiComponent::check_scanning_finished() {
if (!this->scan_done_) {
if (millis() - this->action_started_ > 30000) {
if (millis() - this->action_started_ > WIFI_SCAN_TIMEOUT_MS) {
ESP_LOGE(TAG, "Scan timeout");
this->retry_connect();
}
@@ -1184,8 +1199,9 @@ void WiFiComponent::check_connecting_finished() {
}
uint32_t now = millis();
if (now - this->action_started_ > 30000) {
ESP_LOGW(TAG, "Connection timeout");
if (now - this->action_started_ > WIFI_CONNECT_TIMEOUT_MS) {
ESP_LOGW(TAG, "Connection timeout, aborting connection attempt");
this->wifi_disconnect_();
this->retry_connect();
return;
}
@@ -1405,6 +1421,10 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) {
// without disrupting the captive portal/improv connection
if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_()) {
this->restart_adapter();
} else {
// Even when skipping full restart, disconnect to clear driver state
// Without this, platforms like LibreTiny may think we're still connecting
this->wifi_disconnect_();
}
// Clear scan flag - we're starting a new retry cycle
this->did_scan_this_cycle_ = false;

View File

@@ -720,6 +720,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) {
ESP_LOGV(TAG, "STA stop");
s_sta_started = false;
s_sta_connecting = false;
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) {
const auto &it = data->data.sta_authmode_change;

View File

@@ -291,6 +291,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_
}
case ESPHOME_EVENT_ID_WIFI_STA_STOP: {
ESP_LOGV(TAG, "STA stop");
s_sta_connecting = false;
break;
}
case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: {
@@ -322,7 +323,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_
// wifi_sta_connect_status_() to return IDLE. The main loop then sees
// "Unknown connection status 0" (wifi_component.cpp check_connecting_finished)
// and calls retry_connect(), aborting a connection that may succeed moments later.
// Real connection failures will have ssid/bssid populated, or we'll hit the 30s timeout.
// Real connection failures will have ssid/bssid populated, or we'll hit the connection timeout.
if (it.ssid_len == 0 && s_sta_connecting) {
ESP_LOGV(TAG, "Ignoring disconnect event with empty ssid while connecting (reason=%s)",
get_disconnect_reason_str(it.reason));
@@ -527,7 +528,12 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
network::IPAddress WiFiComponent::wifi_soft_ap_ip() { return {WiFi.softAPIP()}; }
#endif // USE_WIFI_AP
bool WiFiComponent::wifi_disconnect_() { return WiFi.disconnect(); }
bool WiFiComponent::wifi_disconnect_() {
// Clear connecting flag first so disconnect events aren't ignored
// and wifi_sta_connect_status_() returns IDLE instead of CONNECTING
s_sta_connecting = false;
return WiFi.disconnect();
}
bssid_t WiFiComponent::wifi_bssid() {
bssid_t bssid{};