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

219 lines
7.4 KiB
Markdown

# 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