feat: add data manager system for background weather/calendar fetching

This commit is contained in:
2026-08-02 21:20:25 +02:00
parent a6a769fc3e
commit 2cff48a2ac
6 changed files with 214 additions and 69 deletions
+93
View File
@@ -0,0 +1,93 @@
#include "data_manager.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
namespace {
constexpr unsigned long REFRESH_INTERVAL_MS = 10UL * 60UL * 1000UL;
constexpr uint32_t TASK_STACK_WORDS = 16384; // 64 KB — JSON parsing needs headroom
// --- Shared state between background task and main loop ---
// The task is the only writer of s_weather/s_calendar; the main loop is the
// only reader. Access to the data structs is serialized via s_mux (a
// portMUX_TYPE critical section which, on ESP32, suspends task switches on
// the current core). Simple flag/counter reads (bool/uint32_t) are atomic
// on 32-bit Xtensa and only need `volatile`.
static WeatherData s_weather;
static CalendarData s_calendar;
static volatile bool s_weatherOk = false;
static volatile bool s_calendarOk = false;
static volatile bool s_loading = false;
static volatile uint32_t s_dataVersion = 0;
static volatile unsigned long s_lastFetchMs = 0;
static volatile bool s_refreshRequested = false;
static portMUX_TYPE s_mux = portMUX_INITIALIZER_UNLOCKED;
static void dataFetchTask(void *param) {
(void)param;
while (true) {
bool needRefresh = s_refreshRequested ||
s_lastFetchMs == 0 ||
(millis() - s_lastFetchMs) >= REFRESH_INTERVAL_MS;
if (needRefresh) {
s_refreshRequested = false;
s_loading = true;
WeatherData w = fetchWeather();
CalendarData c = fetchCalendar();
portENTER_CRITICAL(&s_mux);
if (w.valid) {
s_weather = std::move(w);
s_weatherOk = true;
} else {
s_weatherOk = false;
}
if (c.valid) {
s_calendar = std::move(c);
s_calendarOk = true;
} else {
s_calendarOk = false;
}
s_dataVersion++;
s_lastFetchMs = millis();
s_loading = false;
portEXIT_CRITICAL(&s_mux);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
} // namespace
void dataManager_begin() {
s_lastFetchMs = 0; // ensure first iteration fetches immediately
xTaskCreatePinnedToCore(dataFetchTask, "dataFetch", TASK_STACK_WORDS,
nullptr, 1, nullptr, 1);
}
void dataManager_tick() {}
void dataManager_triggerRefresh() {
s_refreshRequested = true;
}
void dataManager_getWeather(WeatherData &out) {
portENTER_CRITICAL(&s_mux);
out = s_weather;
portEXIT_CRITICAL(&s_mux);
}
void dataManager_getCalendar(CalendarData &out) {
portENTER_CRITICAL(&s_mux);
out = s_calendar;
portEXIT_CRITICAL(&s_mux);
}
bool dataManager_isWeatherOk() { return s_weatherOk; }
bool dataManager_isCalendarOk() { return s_calendarOk; }
bool dataManager_isLoading() { return s_loading; }
uint32_t dataManager_dataVersion() { return s_dataVersion; }
unsigned long dataManager_lastFetchMs() { return s_lastFetchMs; }
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <Arduino.h>
#include "api/weather_api.h"
#include "api/calendar_api.h"
// Centralized async data manager for weather + calendar.
//
// A background FreeRTOS task fetches weather and calendar data every 10
// minutes (or on manual trigger). Results are cached in memory and exposed
// to the UI screens via thread-safe getters.
//
// Screens poll dataManager_dataVersion() in their tick_fn to detect changes
// and re-render from cache. Navigation between screens never blocks on a
// fetch — views always render instantly from whatever is cached.
// Create the background task and trigger the initial fetch. Call once in setup().
void dataManager_begin();
// No-op for now (background task runs independently). Reserved for future use.
void dataManager_tick();
// Request a manual refresh (non-blocking). The background task picks this up
// within ~1 second.
void dataManager_triggerRefresh();
// Thread-safe copy of the cached weather/calendar data into `out`.
void dataManager_getWeather(WeatherData &out);
void dataManager_getCalendar(CalendarData &out);
// Status flags (safe to read from the main loop without a mutex).
bool dataManager_isWeatherOk();
bool dataManager_isCalendarOk();
bool dataManager_isLoading();
uint32_t dataManager_dataVersion();
unsigned long dataManager_lastFetchMs();