After a successful connect via SDK-cached credentials (Try 1) or the
WiFiManager captive portal, only the SSID was saved to EEPROM. On the next
boot config.password was empty, so setupWiFi() and the reconnect loop called
WiFi.begin(ssid) as if the network were open and failed with
WL_WRONG_PASSWORD.
v1.9.6 masked this: with an empty password the retry loop fell back to a
bare WiFi.begin(), which reuses the SDK-stored credentials. Commit 02834ba
(v1.9.7) replaced that fallback with WiFi.begin(config.ssid), and v1.9.9
(M2) added the same call to setupWiFi(), so every version after 1.9.6
never reconnects after a reboot when the device was set up through the
portal.
Read the password back with WiFi.psk() and store it next to the SSID in
all three places that sync the SSID. Devices already in this state recover
by entering the password once in the web UI (fallback AP after ~2.5 min).
Verified: builds with PlatformIO (espressif8266 core 3.1.2, esp01_1m).
Fixes #12
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
192 lines
5.9 KiB
C++
192 lines
5.9 KiB
C++
/*
|
|
* wifi_manager.cpp - WiFi connection management
|
|
* TJ-56-654 Weather Clock v1.9.3
|
|
*/
|
|
|
|
#include "globals.h"
|
|
#include <WiFiManager.h>
|
|
|
|
// Async WiFi - Process connection (call in loop)
|
|
void ICACHE_FLASH_ATTR processWiFiConnection() {
|
|
if (wifiConnState != WIFI_CONN_CONNECTING) return;
|
|
|
|
// Check connection status
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
wifiConnState = WIFI_CONN_CONNECTED;
|
|
wifiRetry.reset();
|
|
internetConnected = true;
|
|
Serial.println("\nWiFi connected!");
|
|
Serial.print("SSID: ");
|
|
Serial.println(WiFi.SSID());
|
|
Serial.print("IP: ");
|
|
Serial.println(WiFi.localIP());
|
|
|
|
// Disable fallback AP if it was enabled
|
|
if (WiFi.getMode() == WIFI_AP_STA) {
|
|
Serial.println("Disabling fallback AP (back to STA mode)");
|
|
WiFi.softAPdisconnect(true);
|
|
WiFi.mode(WIFI_STA);
|
|
}
|
|
|
|
// Sync connected SSID + password to config
|
|
if (strlen(config.ssid) == 0) {
|
|
safeStringCopy(WiFi.SSID(), config.ssid, sizeof(config.ssid));
|
|
safeStringCopy(WiFi.psk(), config.password, sizeof(config.password));
|
|
saveConfig();
|
|
}
|
|
|
|
showIP();
|
|
return;
|
|
}
|
|
|
|
// Check timeout for this attempt
|
|
if (millis() - wifiConnectStart > WIFI_TIMEOUT_MS) {
|
|
wifiRetry.scheduleRetry();
|
|
unsigned long nextRetryMs = wifiRetry.getBackoffDelay();
|
|
|
|
Serial.printf("\nWiFi connection failed. Retry in %lu seconds\n", nextRetryMs / 1000);
|
|
|
|
wifiConnState = WIFI_CONN_FAILED;
|
|
internetConnected = false;
|
|
|
|
showNoWiFi(nextRetryMs / 1000);
|
|
return;
|
|
}
|
|
|
|
// Still connecting
|
|
static unsigned long lastDot = 0;
|
|
if (millis() - lastDot > 500) {
|
|
Serial.print(".");
|
|
lastDot = millis();
|
|
}
|
|
}
|
|
|
|
// WiFi setup (SYNCHRONOUS in setup(), async reconnect in loop())
|
|
void ICACHE_FLASH_ATTR setupWiFi() {
|
|
Serial.println("WiFi Setup - Synchronous for initial connection");
|
|
|
|
WiFi.hostname(config.hostname);
|
|
WiFi.mode(WIFI_STA);
|
|
|
|
// Try 1: Use WiFi.begin() without params - only when no SSID is configured.
|
|
// Skipped if user has a saved SSID: SDK-cached credentials may include open
|
|
// networks (e.g. public hotspots) that would be preferred over the user's
|
|
// network, and a successful connect would overwrite config.ssid (issue #3).
|
|
if (strlen(config.ssid) == 0) {
|
|
Serial.println("No SSID configured, trying SDK-stored credentials...");
|
|
WiFi.begin();
|
|
|
|
// SYNCHRONOUS wait for connection (max 10 seconds)
|
|
Serial.print("Connecting to WiFi");
|
|
int attempts = 0;
|
|
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
|
|
showWiFiConnecting(attempts);
|
|
delay(500);
|
|
Serial.print(".");
|
|
attempts++;
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
Serial.println("\nWiFi connected!");
|
|
Serial.print("SSID: ");
|
|
Serial.println(WiFi.SSID());
|
|
Serial.print("IP: ");
|
|
Serial.println(WiFi.localIP());
|
|
Serial.print("Gateway: ");
|
|
Serial.println(WiFi.gatewayIP());
|
|
Serial.print("DNS: ");
|
|
Serial.println(WiFi.dnsIP());
|
|
|
|
// Persist the password too, not just the SSID. The SDK keeps the
|
|
// password in its own flash area, but every later boot reads
|
|
// config.password: an empty one is treated as an open network
|
|
// (WiFi.begin(ssid)) and fails with WL_WRONG_PASSWORD (issue #12).
|
|
safeStringCopy(WiFi.SSID(), config.ssid, sizeof(config.ssid));
|
|
safeStringCopy(WiFi.psk(), config.password, sizeof(config.password));
|
|
saveConfig();
|
|
|
|
showIP();
|
|
wifiConnState = WIFI_CONN_CONNECTED;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Try 2: If we have EEPROM credentials, try those (M2: support open networks)
|
|
if (strlen(config.ssid) > 0) {
|
|
Serial.println("\nTrying EEPROM credentials...");
|
|
if (strlen(config.password) > 0) {
|
|
WiFi.begin(config.ssid, config.password);
|
|
} else {
|
|
WiFi.begin(config.ssid); // open network — no password
|
|
}
|
|
|
|
int attempts = 0;
|
|
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
|
|
showWiFiConnecting(attempts);
|
|
delay(500);
|
|
Serial.print(".");
|
|
attempts++;
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
Serial.println("\nWiFi connected via EEPROM credentials!");
|
|
showIP();
|
|
wifiConnState = WIFI_CONN_CONNECTED;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No stored credentials - use WiFiManager
|
|
if (strlen(config.ssid) == 0) {
|
|
Serial.println("\nNo saved credentials, using WiFiManager...");
|
|
WiFiManager wifiManager;
|
|
wifiManager.setConfigPortalTimeout(180);
|
|
|
|
// Show AP mode indicator
|
|
display.clearDisplay();
|
|
display.setTextSize(1);
|
|
display.setTextColor(SSD1306_WHITE);
|
|
display.setCursor(25, 15);
|
|
display.print("Setup Mode");
|
|
display.setTextSize(1);
|
|
display.setCursor(10, 35);
|
|
display.print("Connect to WiFi:");
|
|
display.setCursor(10, 48);
|
|
display.print("TJ56654-Setup");
|
|
display.display();
|
|
|
|
Serial.println("Attempting WiFiManager auto-connect...");
|
|
if (!wifiManager.autoConnect("TJ56654-Setup", "12345678")) {
|
|
Serial.println("WiFi connection failed. Starting fallback AP...");
|
|
WiFi.mode(WIFI_AP);
|
|
WiFi.softAP("TJ56654-Clock", "12345678");
|
|
Serial.print("Fallback AP IP: ");
|
|
Serial.println(WiFi.softAPIP());
|
|
wifiConnState = WIFI_CONN_CONNECTED;
|
|
return;
|
|
}
|
|
|
|
Serial.println("WiFi connected via WiFiManager!");
|
|
Serial.print("SSID: ");
|
|
Serial.println(WiFi.SSID());
|
|
Serial.print("IP: ");
|
|
Serial.println(WiFi.localIP());
|
|
|
|
// Save both SSID and password (issue #12): WiFiManager stores them in the
|
|
// SDK flash, but the next boot connects from config.* only.
|
|
safeStringCopy(WiFi.SSID(), config.ssid, sizeof(config.ssid));
|
|
safeStringCopy(WiFi.psk(), config.password, sizeof(config.password));
|
|
saveConfig();
|
|
|
|
showIP();
|
|
wifiConnState = WIFI_CONN_CONNECTED;
|
|
return;
|
|
}
|
|
|
|
// We have credentials but WiFi is not available
|
|
Serial.println("\nWiFi not available. Will retry in background.");
|
|
wifiConnState = WIFI_CONN_FAILED;
|
|
wifiRetry.scheduleRetry();
|
|
showNoWiFi(wifiRetry.getBackoffDelay() / 1000);
|
|
}
|