diff --git a/P4_Display_Firmware/Changelog.txt b/P4_Display_Firmware/Changelog.txt index 6a082c9..e5a952f 100644 --- a/P4_Display_Firmware/Changelog.txt +++ b/P4_Display_Firmware/Changelog.txt @@ -1,3 +1,12 @@ +Version 1.10.1: +- Die beiden Verlaufskurven teilen sich jetzt eine gemeinsame Skala. In 1.10.0 zoomte jede + Kurve auf ihre eigenen Werte - dadurch saß jede mittig in ihrem eigenen Fenster, und + Wasser bei 70 Grad lag genauso hoch wie Dampf bei 0. Die Höhe sagte nichts mehr aus. +- Die Skala richtet sich nach den Werten beider Kessel zusammen. Der wärmere Kessel liegt + damit sichtbar höher, und die Skala bleibt so eng, wie es die Daten zulassen: Stehen beide + dicht beieinander, greift die Mindestspanne von vier Grad und schon Zehntelgrade sind zu + erkennen. + Version 1.10.0: - Die Verlaufskurven im Dashboard zeigen jetzt wirklich einen Verlauf. Ihr Ausschnitt hing bisher am Sollwert - sechs Grad darunter bis drei darüber. Das funktioniert nur, solange diff --git a/P4_Display_Firmware/ui.cpp b/P4_Display_Firmware/ui.cpp index 4ae5186..616acb7 100644 --- a/P4_Display_Firmware/ui.cpp +++ b/P4_Display_Firmware/ui.cpp @@ -1108,26 +1108,38 @@ static lv_obj_t* tempField(lv_obj_t* parent, const char* title, lv_color_t accen // Flache Verlaufskurve unter einer Temperatur. Zeigt, ob die Regelung haelt - das ist die // eigentliche Frage an eine PID-Regelung, und eine Zahl kann sie grundsaetzlich nicht // beantworten. Gespeist wird sie aus derselben Sekundenabtastung wie die Verlaufsseite. -// Ausschnitt der Verlaufskurve an die tatsaechlich vorhandenen Werte anpassen. +// Gemeinsamer Ausschnitt fuer BEIDE Verlaufskurven. // -// Frueher spannte er sich um den SOLLWERT: sechs Grad darunter bis drei darueber. Das -// funktioniert nur, solange der Kessel schon nahe am Ziel ist. Heizt er noch auf oder ist -// er ganz aus, liegt jeder Messwert unterhalb des Fensters und wird auf den unteren Rand -// geklemmt - beide Kurven lagen dann flach auf derselben Hoehe und bewegten sich nicht, -// obwohl der eine Kessel bei 80 Grad stand und der andere bei 0. -static void spark_range_fit(lv_obj_t* ch, lv_chart_series_t* ser) { +// Zwei Anlaeufe waren vorher daneben. Erst hing der Ausschnitt am Sollwert - dann lag beim +// Aufheizen jeder Messwert unterhalb des Fensters und wurde auf den unteren Rand geklemmt. +// Danach zoomte jede Kurve auf ihre eigenen Werte - dann sass jede mittig in ihrem eigenen +// Fenster, und Wasser bei 70 Grad lag genauso hoch wie Dampf bei 0. Die Hoehe sagte nichts. +// +// Jetzt teilen sich beide Kurven eine Skala, die sich nach den Werten beider zusammen +// richtet. Damit ist die Hoehe wieder vergleichbar - der waermere Kessel liegt sichtbar +// hoeher -, und die Skala bleibt so eng, wie es die Daten zulassen: Stehen beide Kessel +// dicht beieinander, greift die Mindestspanne und schon Zehntelgrade sind zu sehen. +static int32_t g_sparkMin = INT32_MIN; // zuletzt gesetzter Ausschnitt, fuer beide gleich +static int32_t g_sparkMax = INT32_MIN; + +static void spark_scan(lv_obj_t* ch, lv_chart_series_t* ser, int32_t* mn, int32_t* mx) { if (!ch || !ser) return; int32_t* pts = lv_chart_get_y_array(ch, ser); if (!pts) return; uint32_t n = lv_chart_get_point_count(ch); - int32_t mn = INT32_MAX, mx = INT32_MIN; for (uint32_t i = 0; i < n; i++) { if (pts[i] == LV_CHART_POINT_NONE) continue; - if (pts[i] < mn) mn = pts[i]; - if (pts[i] > mx) mx = pts[i]; + if (pts[i] < *mn) *mn = pts[i]; + if (pts[i] > *mx) *mx = pts[i]; } +} + +static void spark_ranges_fit() { + int32_t mn = INT32_MAX, mx = INT32_MIN; + spark_scan(g_sparkW, g_sparkSerW, &mn, &mx); + spark_scan(g_sparkD, g_sparkSerD, &mn, &mx); if (mn > mx) return; // noch kein einziger Wert - if (mx - mn < DASH_SPARK_MIN_SPAN) { // ruhige Regelung: Kurve mittig halten + if (mx - mn < DASH_SPARK_MIN_SPAN) { // beide dicht beieinander: mittig halten int32_t mitte = (mn + mx) / 2; mn = mitte - DASH_SPARK_MIN_SPAN / 2; mx = mitte + DASH_SPARK_MIN_SPAN / 2; @@ -1138,17 +1150,12 @@ static void spark_range_fit(lv_obj_t* ch, lv_chart_series_t* ser) { } // Nur bei echter Aenderung setzen - lv_chart_set_range macht das Diagramm sonst bei // jeder Zustandsmeldung ungueltig, auch wenn sich der Ausschnitt gar nicht ruehrt. - // Den zuletzt gesetzten Ausschnitt merkt sich die Kurve selbst; abfragen kann man ihn - // bei LVGL nicht. - int32_t* merk = (int32_t*)lv_obj_get_user_data(ch); - if (merk && merk[0] == mn && merk[1] == mx) return; - if (!merk) { - merk = (int32_t*)lv_malloc(2 * sizeof(int32_t)); - if (!merk) { lv_chart_set_range(ch, LV_CHART_AXIS_PRIMARY_Y, mn, mx); return; } - lv_obj_set_user_data(ch, merk); - } - merk[0] = mn; merk[1] = mx; - lv_chart_set_range(ch, LV_CHART_AXIS_PRIMARY_Y, mn, mx); + // Abfragen laesst sich der gesetzte Ausschnitt bei LVGL nicht, also wird er gemerkt. + if (mn == g_sparkMin && mx == g_sparkMax) return; + g_sparkMin = mn; + g_sparkMax = mx; + if (g_sparkW) lv_chart_set_range(g_sparkW, LV_CHART_AXIS_PRIMARY_Y, mn, mx); + if (g_sparkD) lv_chart_set_range(g_sparkD, LV_CHART_AXIS_PRIMARY_Y, mn, mx); } static lv_obj_t* tempSpark(lv_obj_t* parent, lv_color_t col, lv_chart_series_t** ser, @@ -1179,8 +1186,7 @@ static lv_obj_t* tempSpark(lv_obj_t* parent, lv_color_t col, lv_chart_series_t** // ersten Sekundenprobe eine leere Flaeche. (void)soll; lv_chart_set_all_value(ch, *ser, (int32_t)(jetzt * 10.0f)); - spark_range_fit(ch, *ser); - return ch; + return ch; // Ausschnitt setzt spark_ranges_fit(), sobald beide Kurven stehen } // Temperaturbereich gemaess aktiver Darstellung (neu) aufbauen - auch beim Umschalten @@ -1193,6 +1199,7 @@ static void build_temp_row_content() { bulbTempW = bulbTempD = nullptr; g_sparkW = g_sparkD = nullptr; g_sparkSerW = g_sparkSerD = nullptr; + g_sparkMin = g_sparkMax = INT32_MIN; // gemerkter Ausschnitt gilt fuer die alten Kurven g_readyBand = g_readyBandLbl = nullptr; const lv_color_t teal = lv_color_hex(0x80cbc4); @@ -1221,6 +1228,7 @@ static void build_temp_row_content() { lv_obj_t* cd = tempField(g_tempRow, "DAMPF", teal, &lblTempD, &lblSetD, &barDutyD, 1, &lv_font_maven_pro_bold_96, false, 0); g_sparkD = tempSpark(cd, COL_ACCENT, &g_sparkSerD, g_state.tempD, g_state.setD); + spark_ranges_fit(); break; } @@ -1720,16 +1728,13 @@ static void chart_duty_btn_cb(lv_event_t*) { } chart_redraw(); - // Verlaufskurven des Dashboards (Darstellung "Verlauf") mitfuehren. Der Ausschnitt - // richtet sich nach den vorhandenen Werten, siehe spark_range_fit(). - if (g_sparkSerW && g_sparkW) { + // Verlaufskurven des Dashboards (Darstellung "Verlauf") mitfuehren. Beide teilen sich + // eine Skala, siehe spark_ranges_fit(). + if (g_sparkSerW && g_sparkW) lv_chart_set_next_value(g_sparkW, g_sparkSerW, (int32_t)(g_state.tempW * 10.0f)); - spark_range_fit(g_sparkW, g_sparkSerW); - } - if (g_sparkSerD && g_sparkD) { + if (g_sparkSerD && g_sparkD) lv_chart_set_next_value(g_sparkD, g_sparkSerD, (int32_t)(g_state.tempD * 10.0f)); - spark_range_fit(g_sparkD, g_sparkSerD); - } + spark_ranges_fit(); // gemeinsame Skala, damit die Hoehen vergleichbar bleiben } static void build_chart(lv_obj_t* p) { lv_obj_clear_flag(p, LV_OBJ_FLAG_SCROLLABLE);