Fix TWDT starvation, vsync race, and UI responsiveness from review
Three bugs and two UX defects found on review, plus hardening: - The control loop fed the task watchdog then slept exactly the watchdog period (both 10s), tripping the TWDT fractionally late on every single iteration. Sleep is now sliced 1s with a reset per slice. Also set CONFIG_ESP_TASK_WDT_PANIC=y - the sdkconfig comment claimed a hung task panics the chip, but nothing actually made that true. - The LVGL flush called draw_bitmap (which only queues the framebuffer switch) and released LVGL immediately, letting it render the next frame into the buffer still being scanned out - the exact PRD 8.2 race the double-buffer design exists to prevent. display_present_frame() now blocks on a vsync handshake until the swap has latched. - WiFi reconnect slept up to 30s inside the system event handler, stalling all queued WiFi/IP events. Moved to an esp_timer. - The setpoint label, arc, and mode/room lines rendered the control loop's copy of state, which refreshes every 10s - every knob detent looked dead for up to 10 seconds. The UI now reads settings live; when the fallback ladder clamps the setpoint it shows both values. Room-select focus now shows which room is being selected. - z2m ISO_8601_local timestamps carry a UTC offset the parser ignored, which would shift apparent reading age by the local offset (5-6h in the US) and permanently trip the fallback ladder. Offset is now honoured. - Suppressed the cosmetic "control loop stopped" fault in the window between relay_init and the control task's first call-in at boot. - Explicit pullups on the encoder A/B lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d2e4c974dd
commit
4c7b120473
10 files changed
+206
-27
No files matched your search
+23
-8
@@ -9,6 +9,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_sntp.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
@@ -24,18 +25,31 @@ static EventGroupHandle_t s_events;
|
||||
static volatile bool s_connected;
|
||||
static int s_retry_ms = RETRY_MIN_MS;
|
||||
static bool s_sntp_started;
|
||||
static esp_timer_handle_t s_reconnect_timer;
|
||||
|
||||
static void reconnect_timer_cb(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
esp_wifi_connect();
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule the retry on a timer rather than sleeping here. This runs on the
|
||||
* shared system event task — a vTaskDelay would stall every queued WiFi/IP
|
||||
* event behind it (up to 30s at full backoff) and can overflow the default
|
||||
* event queue during an outage.
|
||||
*/
|
||||
static void schedule_reconnect(void)
|
||||
{
|
||||
ESP_LOGW(TAG, "reconnecting in %d ms", s_retry_ms);
|
||||
vTaskDelay(pdMS_TO_TICKS(s_retry_ms));
|
||||
|
||||
esp_timer_stop(s_reconnect_timer); /* no-op if not running */
|
||||
esp_timer_start_once(s_reconnect_timer, (uint64_t)s_retry_ms * 1000);
|
||||
|
||||
s_retry_ms *= 2;
|
||||
if (s_retry_ms > RETRY_MAX_MS) {
|
||||
s_retry_ms = RETRY_MAX_MS;
|
||||
}
|
||||
|
||||
esp_wifi_connect();
|
||||
}
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t base,
|
||||
@@ -49,11 +63,6 @@ static void event_handler(void *arg, esp_event_base_t base,
|
||||
} else if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
s_connected = false;
|
||||
xEventGroupClearBits(s_events, CONNECTED_BIT);
|
||||
/*
|
||||
* Runs on the event task, so the delay in schedule_reconnect() blocks
|
||||
* only WiFi event processing — never the control loop, which lives on
|
||||
* its own task and does not consult connection state anyway.
|
||||
*/
|
||||
schedule_reconnect();
|
||||
|
||||
} else if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
|
||||
@@ -92,6 +101,12 @@ esp_err_t wifi_manager_init(void)
|
||||
setenv("TZ", APP_TIMEZONE, 1);
|
||||
tzset();
|
||||
|
||||
const esp_timer_create_args_t timer_args = {
|
||||
.callback = reconnect_timer_cb,
|
||||
.name = "wifi_retry",
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &s_reconnect_timer));
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
@@ -103,10 +103,37 @@ static bool parse_sensor_age(const cJSON *root, uint32_t *age_s)
|
||||
seen = (time_t)(ls->valuedouble / 1000.0);
|
||||
} else if (cJSON_IsString(ls) && ls->valuestring) {
|
||||
struct tm tm = {0};
|
||||
if (!strptime(ls->valuestring, "%Y-%m-%dT%H:%M:%S", &tm)) {
|
||||
const char *rest = strptime(ls->valuestring, "%Y-%m-%dT%H:%M:%S", &tm);
|
||||
if (!rest) {
|
||||
return false;
|
||||
}
|
||||
seen = (time_t)tm_to_epoch_utc(&tm);
|
||||
int64_t epoch = tm_to_epoch_utc(&tm);
|
||||
|
||||
/*
|
||||
* Honour the offset suffix. z2m's ISO_8601 setting emits UTC ("Z"),
|
||||
* but ISO_8601_local emits local time with an offset ("-05:00").
|
||||
* Ignoring that suffix would shift every reading's apparent age by
|
||||
* the local offset — on a US install that is five or six hours, which
|
||||
* makes live sensors look stale and trips the fallback ladder
|
||||
* permanently. local = UTC + offset, so subtract it.
|
||||
*/
|
||||
if (*rest == '.') { /* fractional seconds */
|
||||
rest++;
|
||||
while (*rest >= '0' && *rest <= '9') {
|
||||
rest++;
|
||||
}
|
||||
}
|
||||
if (*rest == '+' || *rest == '-') {
|
||||
int sign = (*rest == '-') ? -1 : 1;
|
||||
int oh = 0, om = 0;
|
||||
if (sscanf(rest + 1, "%2d:%2d", &oh, &om) < 1) {
|
||||
return false;
|
||||
}
|
||||
epoch -= (int64_t)sign * (oh * 3600 + om * 60);
|
||||
}
|
||||
/* 'Z' or nothing: already UTC. */
|
||||
|
||||
seen = (time_t)epoch;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user