Skip to content

arch/[risc-v|xtensa]/espressif: reconnect Wi-Fi STA on AP-side disconnect - #19725

Merged
tmedicci merged 1 commit into
apache:masterfrom
FelipeMdeO:fix/espressif-wifi-reconnect-regression
Aug 7, 2026
Merged

arch/[risc-v|xtensa]/espressif: reconnect Wi-Fi STA on AP-side disconnect#19725
tmedicci merged 1 commit into
apache:masterfrom
FelipeMdeO:fix/espressif-wifi-reconnect-regression

Conversation

@FelipeMdeO

@FelipeMdeO FelipeMdeO commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

An ESP32 Wi-Fi station running NuttX never recovers from an AP-initiated
disconnection. Once the access point goes away — a reboot, a brief outage, a
deauthentication — the station drops the link and stays down forever. The
interface remains UP without RUNNING and only a manual
wapi psk + wapi essid brings it back. Nothing is logged, so in the field
this looks like the board "losing the network" for no reason.

This is a regression. The disconnect handler in esp_wifi_event_handler.c
reconnects only when the reported reason is WIFI_REASON_ASSOC_LEAVE:

case WIFI_EVENT_STA_DISCONNECTED:
    wlinfo("Wi-Fi station disconnected, reason: %u\n", reason);
    esp_wlan_sta_disconnect_hook();
    if (reason == WIFI_REASON_ASSOC_LEAVE)
      {
        work_queue(LPWORK, &g_wifi_reconnect_work, esp_reconnect_work_cb, NULL, 0);
      }
    break;

The reason field carries an 802.11 reason code, extended by ESP-IDF with
vendor values above 200 for locally detected conditions
(wifi_err_reason_t in esp_wifi_types_generic.h):

WIFI_REASON_AUTH_EXPIRE    = 2,    /* Authentication expired          */
WIFI_REASON_AUTH_LEAVE     = 3,    /* Deauthentication due to leaving */
WIFI_REASON_ASSOC_EXPIRE   = 4,    /* Association expired             */
WIFI_REASON_ASSOC_LEAVE    = 8,    /* Deassociated due to leaving     */
WIFI_REASON_BEACON_TIMEOUT = 200,  /* Beacon timeout                  */
WIFI_REASON_NO_AP_FOUND    = 201,  /* No AP found                     */

WIFI_REASON_ASSOC_LEAVE is the value the stack reports for a locally
initiated
disconnect — confirmed on hardware, since running wapi essid logs
reason: 8 because the tool disconnects before associating. So the surviving
branch covers the one case where reconnecting is not needed, and every
AP-initiated reason is left unhandled.

Before the Wi-Fi driver refactors the same handler used an explicit intent
flag, which is the rule ESP-IDF documents — reconnect on any reason unless the
disconnection was requested locally:

case WIFI_ADPT_EVT_STA_DISCONNECT:
    g_sta_connected = false;
    esp_wlan_sta_set_linkstatus(false);
    if (g_sta_reconnect)
      {
        ret = esp_wifi_connect();
      }
    break;

g_sta_reconnect was set true by esp_wifi_sta_connect() and false by
esp_wifi_sta_disconnect(), so the driver always knew whether it had asked to
leave. The refactors replaced that flag with the reason-code test:

Commit Date Chips
1f7c3a32e5 2025-08-21 ESP32-C3 / C6
20ff68bd650 2025-09-01 ESP32 / S2 / S3

The intent cannot be derived from the reason code: the reason is what the
other end reports, while the intent is state this driver owns. The correct
logic is still present in tree today, in
arch/risc-v/src/esp32c3-legacy/esp32c3_wifi_adapter.c, so the same chip
currently ships two drivers with opposite behaviour.

This PR restores the intent flag on both architectures. esp_reconnect_work_cb
re-checks the flag because the user may request ifdown between the work being
queued and the work running. Retry cadence is left to the event loop, matching
ESP-IDF: a failed attempt raises another STA_DISCONNECTED (reason 201) which
schedules the next one, self-throttled by the radio's scan time (~2.4 s
measured).

The failure_retry_cnt writes in esp_wifi_sta_disconnect() are left
untouched, so this PR only adds the flag there. A comment marks them as
having no documented effect: ESP-IDF states the field applies only when
scan_method is WIFI_ALL_CHANNEL_SCAN, and NuttX never selects it — the
only assignment of scan_method anywhere in the tree is
WIFI_FAST_SCAN in the legacy ESP32-C3 driver, and neither the common driver
nor the bundled HAL ever writes the field. Removing the now-redundant writes,
and revisiting whether that field is the right mechanism at all, is better
handled as a follow-up PR so this one stays focused on the regression.

Impact

  • Impact on user: yes, and this is the point. A station that loses its AP now
    reconnects by itself instead of staying offline until rebooted or manually
    re-associated.
  • Impact on hardware: all Espressif chips with Wi-Fi station support, on both
    arch/risc-v and arch/xtensa. Validated on ESP32-C3.
  • Impact on compatibility: a user-requested disconnect (ifdown,
    esp_wifi_sta_disconnect(false) from the ioctl path) still does not
    reconnect — that path is what the flag protects. No call sites changed:
    esp_wlan_netdev.c already passes false for the user disconnect and the
    four internal disconnects in esp_wifi_api.c already pass true.

Testing

Two boards on the same access point, in the same time window, so the trigger is
identical for both:

Device under test Reference
Board ESP32-C3-DevKit ESP32-C6-DevKit
Firmware NuttX, stock esp32c3-devkit:wifi defconfig ESP-IDF v6.0.1, examples/wifi/getting_started/station
Only changes CONFIG_DEBUG_WIRELESS_INFO=y + Wi-Fi credentials log the reason code; retry without a limit

The NuttX side runs the unmodified board defconfig — no application on top, so
the Wi-Fi driver is the only thing under test.

Reproduction: power the access point off for 5 minutes, then back on.

NuttX, ESP32-C3 — the AP disappears:

I (14465) wifi:bcn_timeout,ap_probe_send_start
I (14482) wifi:ap_probe_send over, reset wifi status to disassoc
I (14482) wifi:state: run -> init (0xc800)
esp_wifi_event_handler: Wi-Fi station disconnected, reason: 200
I (14483) wifi:<ba-del>idx:0, tid:0
I (14483) wifi:<ba-del>idx:1, tid:7

Nothing follows. No scan, no association attempt, no further log line — the
remaining entries are the radio tearing down block-ack state at the same
millisecond. The AP came back a few minutes later; 21 minutes after that the
board was still offline:

nsh> ifconfig
wlan0	Link encap:Ethernet HWaddr 80:65:99:2d:4e:3c at UP mtu 1500
	inet addr:192.168.15.144 DRaddr:192.168.15.1 Mask:255.255.255.0

UP without RUNNING: carrier down, no recovery.

ESP-IDF, ESP32-C6, same outage — note the same reason 200:

W (1121797) wifi station: STA_DISCONNECTED, reason: 200
I (1121797) wifi station: retry to connect to the AP (attempt 1)
W (1124217) wifi station: STA_DISCONNECTED, reason: 201
I (1124217) wifi station: retry to connect to the AP (attempt 2)
...
W (1473207) wifi station: STA_DISCONNECTED, reason: 201
I (1473207) wifi station: retry to connect to the AP (attempt 147)
I (1494777) wifi station: got ip:192.168.15.161

147 attempts spaced ~2.4 s apart, then an address as soon as the AP was serving
again. Both stacks received the same event; only the handling differs.

After this change the behavior is the same in NuttX side and ESP-IDF side.

Reviewers who want to reproduce either side need only two boards and a power
switch on their access point.

Related reports that may share this root cause, though neither was diagnosed:
https://github.com/apache/nuttx/issues/19137a

…nect

The disconnect handler only reconnects when the reported reason is
WIFI_REASON_ASSOC_LEAVE, so an AP-initiated deauth (beacon timeout, auth or
assoc expire) leaves the station down forever.  Restore the intent flag the
driver used before 1f7c3a3 and 20ff68b, matching the ESP-IDF rule of
reconnecting unless the disconnection was requested locally.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
@FelipeMdeO
FelipeMdeO marked this pull request as ready for review August 6, 2026 21:23
@acassis
acassis requested a review from xiaoxiang781216 August 6, 2026 22:13
@github-actions github-actions Bot added Arch: risc-v Issues related to the RISC-V (32-bit or 64-bit) architecture Arch: xtensa Issues related to the Xtensa architecture Size: S The size of the change in this PR is small labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@tmedicci tmedicci left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @FelipeMdeO . We're running our internal CI to verify it hasn't broken anything.

(Please don't merge it yet)

@fdcavalcanti fdcavalcanti left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is ok, thanks for the change.

@tmedicci
tmedicci merged commit a1b9bed into apache:master Aug 7, 2026
54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: risc-v Issues related to the RISC-V (32-bit or 64-bit) architecture Arch: xtensa Issues related to the Xtensa architecture Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants