Files
esp8266-weather-clock-opens…/docs/v1.9.1_HYBRID_FIX.md
T
Alex PetrochenkoandClaude Opus 4.5 abf3a513c9 docs: update documentation for v1.9.2
- CHANGELOG.md: Add v1.9.2 WiFi resilience changes
- README.md: Update journey section, memory stats, project structure
- docs/v1.9.1_HYBRID_FIX.md: Minor formatting fixes
- docs/v1.9.2_WIFI_RESILIENCE.md: New detailed documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 09:19:19 +00:00

7.4 KiB

v1.9.1 - Hybrid Async Fix

Problem in v1.9.0

Symptoms:

  • Display shows blank screen ~10 seconds after boot
  • "DNS resolution failed" errors in logs
  • Time appears only after 10+ seconds

Root Cause:

void setup() {
  loadConfig();
  setupWiFi();              // Returns IMMEDIATELY (async)
  setupOTA();               // WiFi NOT ready!
  setupWebServer();         // WiFi NOT ready!
  testInternetConnectivity(); // WiFi NOT ready! -> "DNS resolution failed"
}

WiFi became fully asynchronous, but this is wrong for setup():

  • OTA, web server, NTP require a ready WiFi connection
  • testInternetConnectivity() ran before WiFi connected
  • Time on display appeared only when async WiFi finally connected

Solution: Hybrid Model

Phase WiFi Mode Blocking Reason
setup() Synchronous 10 sec Required for OTA/web/NTP initialization
loop() Asynchronous 0 sec Don't freeze on reconnect

Code Changes

1. setupWiFi() - Now Synchronous

void ICACHE_FLASH_ATTR setupWiFi() {
  Serial.println("WiFi Setup - Synchronous for initial connection");

  WiFi.hostname(config.hostname);

  if (strlen(config.ssid) > 0) {
    WiFi.mode(WIFI_STA);
    WiFi.begin(config.ssid, config.password);

    // SYNCHRONOUS wait (max 10 seconds)
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 20) {
      delay(500);
      showNumber(attempts, false);  // Show progress on display
      attempts++;
    }

    if (WiFi.status() == WL_CONNECTED) {
      // WiFi ready for OTA/web/NTP!
      showIP();
      wifiConnState = WIFI_CONN_CONNECTED;
      return;
    }
  }

  // Fallback to WiFiManager if credentials didn't work
  // ...
}

2. loop() - Async Reconnect

void loop() {
  // WiFi reconnection (async, non-blocking)
  static unsigned long lastWiFiCheck = 0;
  if (millis() - lastWiFiCheck > 5000) {
    if (WiFi.status() != WL_CONNECTED && wifiConnState == WIFI_CONN_CONNECTED) {
      Serial.println("WiFi disconnected, attempting async reconnect...");
      WiFi.begin();  // Async reconnect
      wifiConnState = WIFI_CONN_CONNECTING;
      wifiConnectStart = millis();
    }
    lastWiFiCheck = millis();
  }

  processWiFiConnection();  // Async reconnect handling

  // Other async operations
  processNTPResponse();
  fetchWeatherAsync();
  // ...
}

Test Results

Before (v1.9.0)

[0-5s]   -> Display init
[5-15s]  -> WiFi connecting (async, setup() returns immediately)
[15-20s] -> OTA/web init WITHOUT WiFi -> Errors!
[20s]    -> testInternetConnectivity() WITHOUT WiFi -> "DNS resolution failed"
[15-25s] -> WiFi finally connects (async)
[25-30s] -> NTP sync begins

Display blank for 10+ seconds
"DNS resolution failed" errors

After (v1.9.1)

[0-5s]   -> Display init + startup animation
[5-15s]  -> WiFi connection (SYNCHRONOUS, setup() waits)
            WiFi connected!
[15-20s] -> OTA/web/NTP init WITH WiFi
            Internet test: PASSED
            No DNS errors!
[20-30s] -> First async NTP sync
            Time synced!

Display shows time immediately after WiFi connects (~15 sec)
No "DNS resolution failed" errors
Proper initialization order

Startup Timeline

+----------------------------------------------------------+
| SETUP PHASE (Synchronous WiFi)                           |
+----------------------------------------------------------+
|                                                          |
|  [0s]  +-------------+                                   |
|        |  Display    |  Startup animation                |
|  [5s]  |    Init     |  "Weather Clock v1.9.1"           |
|        +-------------+                                   |
|                                                          |
|  [5s]  +---------------------------------+               |
|        |  WiFi Connect (SYNCHRONOUS)     |               |
|        |  - Connecting to network...     |               |
| [15s]  |  - Connected! IP assigned       |               |
|        +---------------------------------+               |
|               |                                          |
|        WiFi is READY here                                |
|               |                                          |
| [15s]  +---------------------------------+               |
|        |  OTA Init      (needs WiFi)     |               |
|        |  Web Server    (needs WiFi)     |               |
| [20s]  |  NTP Client    (needs WiFi)     |               |
|        |  Internet Test (needs WiFi)     |               |
|        +---------------------------------+               |
|                                                          |
| [20s]  Setup complete! -> loop() starts                  |
|                                                          |
+----------------------------------------------------------+

+----------------------------------------------------------+
| LOOP PHASE (Async Operations)                            |
+----------------------------------------------------------+
|                                                          |
|  [Every loop]  +------------------------+                |
|                |  WiFi Health Check     |                |
|                |  (every 5 sec)         |                |
|                |  If disconnected:      |                |
|                |  -> Async reconnect    |                |
|                +------------------------+                |
|                                                          |
|  [Every loop]  +------------------------+                |
|                |  Async NTP Processing  |                |
|                |  (non-blocking)        |                |
|                +------------------------+                |
|                                                          |
|  [Every 30m]   +------------------------+                |
|                |  Async Weather Fetch   |                |
|                |  (non-blocking)        |                |
|                +------------------------+                |
|                                                          |
|  Loop time: <1ms (no blocking!)                          |
|                                                          |
+----------------------------------------------------------+

Benefits of Hybrid Approach

In setup():

  1. Correct initialization order - WiFi -> OTA -> web -> NTP
  2. No DNS errors - internet connectivity test runs AFTER WiFi
  3. Predictable behavior - setup() completes when everything is ready
  4. Display shows time immediately - no need to wait for async WiFi

In loop():

  1. No freeze on reconnect - async handling of WiFi loss
  2. Async NTP - doesn't block loop
  3. Async weather - doesn't block loop
  4. Exponential backoff - smart retry on errors
  5. Loop <1ms - always responsive device

Memory

Resource v1.9.0 v1.9.1 Change
RAM 37,516 37,644 +128 bytes
IRAM 61,987 61,987 0 bytes
Flash 408,540 408,844 +304 bytes

Minimal memory changes (+0.3%) for critical UX improvement.

Conclusion

v1.9.1 implements the ideal balance:

  • Setup: Synchronous for reliable initialization
  • Loop: Asynchronous for responsiveness

Result:

  • Display shows time after 15 sec (instead of 25+ sec)
  • No "DNS resolution failed" errors
  • Correct startup order
  • Device doesn't freeze on WiFi loss during operation

Status: Production ready