Summary
On a network where several access points advertise the same SSID (any mesh/multi-AP setup), WiFi provisioning associates successfully but never obtains an IP address, so setup never completes. The mobile app sits on "Verifying" indefinitely, and because setup is never marked complete, every reboot returns to the WiFi setup flow.
The root cause is that wifi_config.sta.scan_method is left at the esp-idf default of WIFI_FAST_SCAN, which joins the first AP that answers for the SSID rather than the strongest one.
Environment
- StackChan CoreS3 kit, factory firmware flashed via M5Burner (2026-08-18)
- Code references below are against
main @ b72b3ed
- Home mesh, WPA2-PSK, 2.4 GHz available, multiple nodes broadcasting one SSID
Symptom
Serial log during provisioning (SSID and BSSIDs anonymized). The credentials arrive from the app, the device associates — and then nothing:
I (54340) StackChanWifiStation: Setting WiFi configuration SSID: MyMesh
W (54350) wifi:Password length matches WPA2 standards, authmode threshold changes from OPEN to WPA2
I (54570) wifi:state: init -> auth (0xb0)
I (54580) wifi:state: auth -> assoc (0x0)
I (54610) wifi:state: assoc -> run (0x10)
I (54650) wifi:connected with MyMesh, aid = 8, channel 1, BW20, bssid = AA:BB:CC:44:55:66
I (54650) wifi:security: WPA2-PSK, phy: bgn, rssi: -86, cipher(pairwise:0x3, group:0x3), pmf:0
I (54650) wifi:pm start, type: 1
...
I (60780) SystemInfo: free sram: 97291 minimal sram: 95623
I (70780) SystemInfo: free sram: 97291 minimal sram: 95623
I (80780) SystemInfo: free sram: 97291 minimal sram: 95623
I (90780) SystemInfo: free sram: 97291 minimal sram: 95623
Note rssi: -86, and that no IP_EVENT_STA_GOT_IP ever fires — StackChanWifiStation::IpEventHandler never logs Got IP:, and no esp_netif DHCP output appears. The device stays associated with no address until it is rebooted.
On an earlier attempt on the same network it picked a different node:
I (75529) wifi:connected with MyMesh, aid = 11, channel 6, BW20, bssid = AA:BB:CC:11:22:33
I (75529) wifi:security: WPA2-PSK, phy: bgn, rssi: -51
So across two boots the device attached to two different nodes — one at −51 dBm, one at −86 dBm — with no relationship to signal strength.
Analysis
In firmware/main/hal/utils/wifi_connect/wifi_station.cc, AddAuth() zeroes the config and sets only SSID and password:
wifi_config_t wifi_config;
bzero(&wifi_config, sizeof(wifi_config));
memcpy(wifi_config.sta.ssid, ssid.c_str(), ssid.length());
memcpy(wifi_config.sta.password, password.c_str(), password.length());
Zeroing leaves scan_method as WIFI_FAST_SCAN (value 0), which per the esp-idf docs ends the scan as soon as a matching SSID is found. On a single-router network this is harmless. On a mesh it selects an essentially arbitrary node — in the log above, a node at −86 dBm while a −51 dBm node was in range.
At −86 dBm, association succeeds (management frames are low-rate and robust) but the DHCP exchange does not survive the link, which produces exactly the observed "associated forever, no IP" state.
Two factors compound it:
MAX_RECONNECT_COUNT is 1 (line 10), so the device gives up after a single retry rather than trying other nodes.
- Because setup never succeeds, the credentials are stored but never marked valid, so each boot re-enters provisioning (
SsidManager: SSID MyMesh already exists, overwrite it).
Cross-check: a minimal Arduino sketch flashed to the same device, joining the same SSID with the same password, obtained a DHCP lease immediately and reached the public internet. Arduino's WiFi.begin() sweeps all channels and sorts by signal, which is consistent with this diagnosis.
Suggested fix
--- a/firmware/main/hal/utils/wifi_connect/wifi_station.cc
+++ b/firmware/main/hal/utils/wifi_connect/wifi_station.cc
@@
-#define MAX_RECONNECT_COUNT 1
+#define MAX_RECONNECT_COUNT 5
@@
memcpy(wifi_config.sta.password, password.c_str(), password.length());
+ // Several nodes may advertise the same SSID on a mesh. WIFI_FAST_SCAN joins
+ // the first node that answers, which can be a distant one that associates
+ // but cannot complete DHCP. Scan all channels and pick the strongest.
+ wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
+ wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL;
+
ESP_LOGI(TAG, "Setting WiFi configuration SSID: %s", ssid.c_str());
It would also help users diagnose this if WIFI_EVENT_STA_DISCONNECTED logged the disconnect reason code, and if a DHCP timeout surfaced a distinct error in the app rather than an indefinite "Verifying" state.
Unrelated minor note
WifiConfigServer logs the WiFi password in plaintext on the serial console during provisioning:
[WifiConfigServer] get wifi config: MyMesh / <password in clear text>
Low severity since it needs physical USB access, but it is easy to remove and worth doing.
Summary
On a network where several access points advertise the same SSID (any mesh/multi-AP setup), WiFi provisioning associates successfully but never obtains an IP address, so setup never completes. The mobile app sits on "Verifying" indefinitely, and because setup is never marked complete, every reboot returns to the WiFi setup flow.
The root cause is that
wifi_config.sta.scan_methodis left at the esp-idf default ofWIFI_FAST_SCAN, which joins the first AP that answers for the SSID rather than the strongest one.Environment
main@b72b3edSymptom
Serial log during provisioning (SSID and BSSIDs anonymized). The credentials arrive from the app, the device associates — and then nothing:
Note
rssi: -86, and that noIP_EVENT_STA_GOT_IPever fires —StackChanWifiStation::IpEventHandlernever logsGot IP:, and noesp_netifDHCP output appears. The device stays associated with no address until it is rebooted.On an earlier attempt on the same network it picked a different node:
So across two boots the device attached to two different nodes — one at −51 dBm, one at −86 dBm — with no relationship to signal strength.
Analysis
In
firmware/main/hal/utils/wifi_connect/wifi_station.cc,AddAuth()zeroes the config and sets only SSID and password:Zeroing leaves
scan_methodasWIFI_FAST_SCAN(value 0), which per the esp-idf docs ends the scan as soon as a matching SSID is found. On a single-router network this is harmless. On a mesh it selects an essentially arbitrary node — in the log above, a node at −86 dBm while a −51 dBm node was in range.At −86 dBm, association succeeds (management frames are low-rate and robust) but the DHCP exchange does not survive the link, which produces exactly the observed "associated forever, no IP" state.
Two factors compound it:
MAX_RECONNECT_COUNTis1(line 10), so the device gives up after a single retry rather than trying other nodes.SsidManager: SSID MyMesh already exists, overwrite it).Cross-check: a minimal Arduino sketch flashed to the same device, joining the same SSID with the same password, obtained a DHCP lease immediately and reached the public internet. Arduino's
WiFi.begin()sweeps all channels and sorts by signal, which is consistent with this diagnosis.Suggested fix
It would also help users diagnose this if
WIFI_EVENT_STA_DISCONNECTEDlogged the disconnect reason code, and if a DHCP timeout surfaced a distinct error in the app rather than an indefinite "Verifying" state.Unrelated minor note
WifiConfigServerlogs the WiFi password in plaintext on the serial console during provisioning:Low severity since it needs physical USB access, but it is easy to remove and worth doing.