# 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:** ```cpp 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 ```cpp 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 ```cpp 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