fix: C1 C2 H1 H2 H3 — stability fixes for long-running operation
C1 (critical): Add 15s watchdog for WEATHER_REQUESTING state — resets to IDLE if TCP connection hangs silently, preventing permanent weather death C2 (critical): Fix millis() rollover in all retry timers — replace unsafe `millis() >= nextRetryTime` with subtraction-safe `(millis() - nextRetryTime) < 0x80000000UL` in RetryConfig and WiFiRetryConfig; fix boot guard with static flag instead of raw millis() comparison H1 (high): Fix DST last-Sunday formula — was using incorrect year-only heuristic; now derives weekday of the 31st from current day's tm_wday: `weekdayOf31 = (tm_wday + (31 - day)) % 7`. Verified: March 2026 = 29th ✓ H2 (high): Replace String+= with snprintf+sendContent in handleAPITime, handleAPIStatus, handleAPIDebug, handleAPIWeather — eliminates permanent heap fragmentation from JS polling every second H3 (high): Add volatile to weatherState and ntpState — shared between ESPAsyncTCP callbacks and main loop; prevents stale register-cached reads RAM: 37,268 bytes (46%) — reduced from 37,560 due to String elimination Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
eeec0155c3
commit
78bbd96f45
@@ -50,9 +50,9 @@ NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 60000);
|
||||
ESP8266WebServer server(80);
|
||||
ESP8266HTTPUpdateServer httpUpdater;
|
||||
|
||||
// State machines
|
||||
WeatherState weatherState = WEATHER_IDLE;
|
||||
NTPState ntpState = NTP_IDLE;
|
||||
// State machines — volatile: written from ESPAsyncTCP callbacks, read in main loop
|
||||
volatile WeatherState weatherState = WEATHER_IDLE;
|
||||
volatile NTPState ntpState = NTP_IDLE;
|
||||
WiFiConnectionState wifiConnState = WIFI_CONN_IDLE;
|
||||
|
||||
// Retry configurations
|
||||
@@ -92,6 +92,7 @@ SunTimes sunTimes;
|
||||
uint8_t displayMode = 0;
|
||||
unsigned long lastModeSwitch = 0;
|
||||
unsigned long lastWeatherUpdate = 0;
|
||||
unsigned long weatherRequestStart = 0; // Tracks when WEATHER_REQUESTING began (TCP hang watchdog)
|
||||
|
||||
// Dissolve transition state
|
||||
bool inTransition = false;
|
||||
@@ -311,14 +312,23 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Watchdog: reset if WEATHER_REQUESTING stuck >15s (TCP hang / half-open connection)
|
||||
if (weatherState == WEATHER_REQUESTING && (millis() - weatherRequestStart) > 15000UL) {
|
||||
Serial.println("Weather request timeout (TCP hang) — resetting state");
|
||||
weatherState = WEATHER_IDLE;
|
||||
weatherRetry.scheduleRetry();
|
||||
}
|
||||
|
||||
// Check for weather retry
|
||||
if (weatherRetry.isRetryTime() && weatherState == WEATHER_IDLE) {
|
||||
Serial.println("Weather retry time reached, attempting retry...");
|
||||
fetchWeatherAsync();
|
||||
}
|
||||
|
||||
// Update weather periodically
|
||||
if (config.weather_enabled && millis() > 10000) {
|
||||
// Update weather periodically (static flag avoids millis() > 10000 rollover trap)
|
||||
static bool weatherBootReady = false;
|
||||
if (!weatherBootReady && millis() > 10000UL) weatherBootReady = true;
|
||||
if (config.weather_enabled && weatherBootReady) {
|
||||
unsigned long weatherInterval = config.weather_interval * 1000UL;
|
||||
if (millis() - lastWeatherUpdate > weatherInterval || lastWeatherUpdate == 0) {
|
||||
if (timeClient.isTimeSet() && weatherState == WEATHER_IDLE && !weatherRetry.isRetryTime()) {
|
||||
|
||||
Reference in New Issue
Block a user