From fd6ec5371b784ce6b326fe8fe42421d22a0dec45 Mon Sep 17 00:00:00 2001 From: raw-designs Date: Sun, 12 Jul 2026 21:16:37 +0200 Subject: [PATCH] =?UTF-8?q?fix(P4):=20Verlaufs-Historie=20ins=20PSRAM=20ve?= =?UTF-8?q?rlagern=20=E2=80=94=20interner=20RAM=20lief=20=C3=BCber?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die 60-min-Ringpuffer der Verlauf-Seite (~29 KB) lagen als statische Arrays im internen RAM und sprengten das Board-Limit (Linker: 107 % dynamischer Speicher, Build brach ab). Historie jetzt als ein Block per heap_caps_malloc im PSRAM (Fallback MALLOC_CAP_8BIT), wie die LVGL-Framebuffer; alle Zugriffe gegen Allokationsfehler abgesichert (Chart bliebe dann leer). Co-Authored-By: Claude Fable 5 --- JC_Display_Firmware/Changelog.txt | 2 ++ JC_Display_Firmware/ui.cpp | 48 ++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/JC_Display_Firmware/Changelog.txt b/JC_Display_Firmware/Changelog.txt index 9e04031..f0597cc 100644 --- a/JC_Display_Firmware/Changelog.txt +++ b/JC_Display_Firmware/Changelog.txt @@ -14,6 +14,8 @@ Version 1.0.11: ebenfalls P4-lokal im NVS. - Feinere Kurven: Die Temperatur-Historie wird jetzt in Zehntelgrad geführt und gezeichnet (vorher ganzzahlig gerundet) — PID-Schwingungen um ±1 °C sind damit sauber ablesbar. + - Die ~29 KB Historie liegen im PSRAM (heap_caps_malloc, wie die LVGL-Framebuffer) — + als statische Arrays sprengten sie den internen RAM (Linker: 107 % belegt). - Shot-Zusammenfassung: Karte auf 560 px verbreitert und Spaltenabstand zwischen Dauer/Gewicht/Flow deutlich vergrößert (28 px statt 10 px) — die drei Werte standen zu nahe beisammen und waren schlecht lesbar. diff --git a/JC_Display_Firmware/ui.cpp b/JC_Display_Firmware/ui.cpp index fdf615f..6c3c93d 100644 --- a/JC_Display_Firmware/ui.cpp +++ b/JC_Display_Firmware/ui.cpp @@ -17,6 +17,7 @@ #include #include #include // Dashboard-Design P4-lokal merken (NVS) +#include // Verlaufs-Historie im PSRAM allozieren (interner RAM ist knapp) #include "ui.h" #include "theme.h" #include "config.h" @@ -124,11 +125,17 @@ static lv_obj_t* g_axHi = nullptr; static lv_obj_t* g_axMid = nullptr; static lv static const uint16_t CHART_WIN_MIN[4] = { 2, 10, 30, 60 }; // waehlbare Fenster (Minuten) #define CHART_HIST_MAX 3600 // Historie: 60 min à 1 s #define CHART_HIST_NONE INT16_MIN // Markierung "keine Probe" -static int16_t g_histW[CHART_HIST_MAX]; // Zehntelgrad -static int16_t g_histD[CHART_HIST_MAX]; -static int16_t g_histC[CHART_HIST_MAX]; -static uint8_t g_histDutyW[CHART_HIST_MAX]; // Heizleistung (%) -static uint8_t g_histDutyD[CHART_HIST_MAX]; +// Die ~29 KB Historie liegen im PSRAM (heap_caps_malloc in ui_init, wie die LVGL- +// Framebuffer) - als statische Arrays wuerden sie den knappen internen RAM sprengen +// (Board-Limit war vor diesem Feature bereits zu ~98 % belegt). +struct ChartHist { + int16_t w[CHART_HIST_MAX]; // Wasser (Zehntelgrad) + int16_t d[CHART_HIST_MAX]; // Dampf (Zehntelgrad) + int16_t c[CHART_HIST_MAX]; // Zusatzsensor (Zehntelgrad; CHART_HIST_NONE = aus) + uint8_t dutyW[CHART_HIST_MAX]; // Heizleistung Wasser (%) + uint8_t dutyD[CHART_HIST_MAX]; // Heizleistung Dampf (%) +}; +static ChartHist* g_hist = nullptr; static uint16_t g_histPos = 0, g_histFill = 0; static uint8_t g_chartWin = 0; // Index in CHART_WIN_MIN (P4-lokal in NVS, "chartwin") static bool g_chartDuty = false; // Heizleistung als Zusatzkurven (NVS "chartduty") @@ -1471,7 +1478,7 @@ static void build_chart(lv_obj_t* p) { // Y-Achse ueber das sichtbare Fenster (Werte < 30 Grad = kalter/inaktiver Sensor werden // fuer die Skala ignoriert) und positioniert die gestrichelten Soll-Linien. static void chart_redraw() { - if (!g_chart) return; + if (!g_chart || !g_hist) return; const int stride = (int)CHART_WIN_MIN[g_chartWin] * 60 / 120; // Sekunden je Chart-Punkt int32_t lo = INT32_MAX, hi = INT32_MIN; // Zehntelgrad bool anyW = false, anyD = false; @@ -1482,11 +1489,11 @@ static void chart_redraw() { if (age < (int)g_histFill) { int idx = (int)g_histPos - 1 - age; if (idx < 0) idx += CHART_HIST_MAX; - vW = g_histW[idx]; - vD = g_histD[idx]; - if (g_histC[idx] != CHART_HIST_NONE) vC = g_histC[idx]; - dW = g_histDutyW[idx]; - dD = g_histDutyD[idx]; + vW = g_hist->w[idx]; + vD = g_hist->d[idx]; + if (g_hist->c[idx] != CHART_HIST_NONE) vC = g_hist->c[idx]; + dW = g_hist->dutyW[idx]; + dD = g_hist->dutyD[idx]; if (vW > 300) { anyW = true; if (vW < lo) lo = vW; if (vW > hi) hi = vW; } if (vD > 300) { anyD = true; if (vD < lo) lo = vD; if (vD > hi) hi = vD; } if (vC != LV_CHART_POINT_NONE && vC > 300) { if (vC < lo) lo = vC; if (vC > hi) hi = vC; } @@ -1540,15 +1547,15 @@ static void chart_redraw() { lv_chart_refresh(g_chart); } static void chart_sample_cb(lv_timer_t*) { - if (!g_chart) return; + if (!g_chart || !g_hist) return; // 1-s-Probe in die 60-min-Historie (Ringpuffer); Anzeige zeichnet chart_redraw bool caseOn = g_state.caseSensorEnabled && g_state.hasCaseTemp; - g_histW[g_histPos] = (int16_t)(g_state.tempW * 10.0f + 0.5f); - g_histD[g_histPos] = (int16_t)(g_state.tempD * 10.0f + 0.5f); - g_histC[g_histPos] = caseOn ? (int16_t)(g_state.caseTemp * 10.0f + 0.5f) : CHART_HIST_NONE; + g_hist->w[g_histPos] = (int16_t)(g_state.tempW * 10.0f + 0.5f); + g_hist->d[g_histPos] = (int16_t)(g_state.tempD * 10.0f + 0.5f); + g_hist->c[g_histPos] = caseOn ? (int16_t)(g_state.caseTemp * 10.0f + 0.5f) : CHART_HIST_NONE; float dw = g_state.dutyW, dd = g_state.dutyD; - g_histDutyW[g_histPos] = (uint8_t)(dw < 0.0f ? 0 : (dw > 100.0f ? 100 : (int)(dw + 0.5f))); - g_histDutyD[g_histPos] = (uint8_t)(dd < 0.0f ? 0 : (dd > 100.0f ? 100 : (int)(dd + 0.5f))); + g_hist->dutyW[g_histPos] = (uint8_t)(dw < 0.0f ? 0 : (dw > 100.0f ? 100 : (int)(dw + 0.5f))); + g_hist->dutyD[g_histPos] = (uint8_t)(dd < 0.0f ? 0 : (dd > 100.0f ? 100 : (int)(dd + 0.5f))); g_histPos = (uint16_t)((g_histPos + 1) % CHART_HIST_MAX); if (g_histFill < CHART_HIST_MAX) g_histFill++; @@ -2789,6 +2796,13 @@ void ui_init(ProtocolClient* client) { g_client = client; // Gespeicherte Darstellungs-Einstellungen laden (P4-lokal; Design-Standard: Rundinstrumente) + // Verlaufs-Historie (60 min à 1 s, ~29 KB) im PSRAM anlegen - wie die LVGL-Framebuffer. + // Schlaegt die Allokation fehl, bleibt das Chart schlicht leer (alle Zugriffe geprueft). + if (!g_hist) { + g_hist = (ChartHist*)heap_caps_malloc(sizeof(ChartHist), MALLOC_CAP_SPIRAM); + if (!g_hist) g_hist = (ChartHist*)heap_caps_malloc(sizeof(ChartHist), MALLOC_CAP_8BIT); + } + { Preferences prefs; prefs.begin("ui", false); g_tempDesign = prefs.getUChar("tempdsgn", TD_GAUGE); g_brewLiveEnabled = prefs.getBool("brewlive", true);