Die Seiten sind als einspaltige Stapel für 800x480 entstanden. Auf 1280x720
bleibt daneben rund 40 % der Fläche leer, während unten Karten aus dem Bild
fallen - und Scrollen ist auf diesem Panel teuer, weil ohne Doppelpufferung
jedes Bild komplett neu entsteht.
- Neue Helfer page_grid() und grid_at() legen eine Seite als LVGL-Raster an.
- build_brew() ordnet die vier Karten in zwei Spalten: links was den Bezug
startet (Pre-Infusion, Brew-by-Time), rechts was ihn beendet
(Brew-by-Weight, Dampf-Timer).
- Der Speichern-Knopf steht frei unter allen Karten statt in einer davon -
er speichert die ganze Seite.
- Nur für WS_PANEL_7H; die JC-Panels bleiben einspaltig.
Beide Panel-Varianten mit arduino-cli gegengebaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ohne Doppelpufferung entsteht beim Scrollen jedes Bild komplett neu
(1280x676 in 24 Bit, per Software). Am stärksten fällt das beim Ausrollen
nach dem Loslassen auf: Dort bewegt sich das Bild weiter, ohne dass ein
Finger es führt, und jedes Stocken ist unmittelbar zu sehen.
LV_OBJ_FLAG_SCROLL_MOMENTUM ist für die scrollbaren Container deshalb
abgeschaltet - die Liste folgt nur noch dem Finger. Nur für WS_PANEL_7H;
die JC-Panels haben das Tempoproblem nicht und bleiben unverändert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ursache war der zweite Zeichenpuffer aus 1.6.25. esp_lcd_dpi_panel_draw_bitmap
lehnt eine Übertragung ab, solange die vorige noch läuft:
ESP_RETURN_ON_FALSE(xSemaphoreTake(dpi_panel->draw_sem, 0) == pdTRUE,
ESP_ERR_INVALID_STATE, ...)
Mit zwei Puffern schickt LVGL die nächste Übertragung sofort los, statt auf
lv_disp_flush_ready zu warten. Die abgelehnte Übertragung fällt ersatzlos
aus, und der betroffene Bildbereich behält seinen alten oder noch nicht
geschriebenen Inhalt - sichtbar als blaue Zwischenbilder.
Der zweite Zeichenpuffer ist entfernt, lvgl_port_v9.c ist damit wieder
unverändert gegenüber der Hersteller-Portierung. Der eigentliche Gewinn
bleibt: Der Zwischenpuffer hat weiterhin volle Bildhöhe, ein Bildlauf
braucht einen Durchgang statt sechs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nach der Umstellung auf einen Bildspeicher war der Bildlauf sehr zäh. Grund
war der Zwischenpuffer: Mit 120 Zeilen brauchte ein Bildlauf über die ganze
Seite sechs Durchgänge, jeder mit eigener Übertragung in den Bildspeicher.
- CONFIG_EXAMPLE_LVGL_PORT_BUF_HEIGHT auf LCD_V_RES: ein Durchgang statt
sechs.
- Zweiter Zeichenpuffer (LVGL_PORT_SECOND_DRAW_BUFFER), damit LVGL den
nächsten Bereich zeichnen kann, während der vorige übertragen wird. Dafür
eine kleine, markierte Änderung in lvgl_port_v9.c - der Hersteller-Port
legt dort nur einen Puffer an.
Kosten 2 x 2,8 MB PSRAM. Das Startverhalten bleibt unberührt: Es handelt
sich um Zeichenpuffer, nicht um Bildspeicher der Anzeige - es bleibt bei
einem Bildspeicher.
Beide Panel-Varianten mit arduino-cli gegengebaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mit dem Prüfbild bei num_fbs = 1 startet das Panel zuverlässig, mit zwei
Bildspeichern nicht. Das war der letzte verbliebene Unterschied zum
funktionierenden Werks-Testsketch - und die Ursache des ganzen
Startproblems: Der Hochlauf meldete durchgehend Erfolg, das Panel blieb
dunkel, erst ein Reset half.
- WS7_SINGLE_FB schaltet die Doppelpufferung für das Waveshare-Panel ab
(EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE = 0). LVGL zeichnet dann in einen
Zwischenpuffer von 120 Zeilen im PSRAM, der per DMA in den einen
Bildspeicher übertragen wird.
- num_fbs folgt wieder LVGL_PORT_LCD_BUFFER_NUMS, das in diesem Modus 1
ist - die Sonderbehandlung im Prüfbild-Modus entfällt.
- WS7_PARTIAL_REFRESH ist damit wirkungslos und als solches vermerkt.
- PSRAM-Bedarf sinkt von 5,4 MB auf 2,8 MB.
Preis ist mögliches Tearing bei schnellen Bildwechseln.
Beide Panel-Varianten mit arduino-cli gegengebaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7JsiDJ1QbNFLDz5chf8Jt
Der Selbst-Neustart aus 1.6.22 reicht nicht - ein Software-Neustart setzt
offenbar nicht dasselbe zurück wie die Reset-Taste.
Damit zurück zu dem einen Unterschied zum funktionierenden Werks-Testsketch,
der bisher nie überprüft wurde: dsi-test.ino setzt num_fbs = 1, die Firmware
nutzt LVGL_PORT_LCD_BUFFER_NUMS = 2. Der Prüfbild-Test lief bislang ebenfalls
mit zwei Speichern und war damit nie ein echter Nachbau - er wich an genau
der offenen Stelle ab.
Mit WS7_TEST_PATTERN = 1 wird num_fbs jetzt fest auf 1 gesetzt. Erscheinen
die Farbbalken damit zuverlässig, liegt es an der Anzahl der Bildspeicher.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7JsiDJ1QbNFLDz5chf8Jt
Der DSI-Ab- und Wiederaufbau aus 1.6.21 hat nicht geholfen, sondern das
Bild um einen Schritt verschlechtert: Einschalten - gar nichts, erster
Reset - nur Beleuchtung, zweiter Reset - Bild. Ein Neuaufbau im laufenden
Betrieb ist einem echten Chip-Reset also nicht gleichwertig.
WS7_DSI_RESTART steht wieder auf 0, der Schalter bleibt dokumentiert.
Stattdessen der Weg, den die Messungen stützen: Nach einem Kaltstart
startet der P4 sich genau einmal selbst neu, sobald der erste Hochlauf
durch ist. Ein Reset hat in jedem Versuch zuverlässig geholfen.
- Bedingung ist esp_reset_reason() == ESP_RST_POWERON, beim zweiten
Durchlauf greift sie nicht mehr - keine Schleife möglich.
- Der Frühstart-Zähler der Absturzsicherung wird vorher zurückgesetzt,
damit der gewollte Neustart nicht in den Update-Modus führt.
- Schalter WS7_COLD_BOOT_RESTART, kostet ~3 s beim Einschalten.
Das ist eine Umgehung, keine Erklärung: Warum das Panel den ersten Anlauf
nicht annimmt, bleibt offen - der Hochlauf meldet durchgehend Erfolg.
Beide Panel-Varianten mit arduino-cli gegengebaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7JsiDJ1QbNFLDz5chf8Jt
Der Hochlauf lief zuletzt vollständig fehlerfrei durch - Bridge zweimal
eingestellt, Panel zweimal geweckt, alle I2C-Bausteine erreichbar - und
trotzdem kam beim ersten Einschalten kein Bild. Erst ein Reset half, und
der zuverlässig.
Es fehlte also kein Befehl mehr, sondern der Vorgang selbst: Ein Reset
unterscheidet sich an dieser Stelle in genau einem Punkt von unserem
Ablauf - die DSI-Verbindung geht einmal weg und neu auf, während Bridge
und Panel bereits versorgt und eingestellt sind. Die Wiederholungen aus
1.6.19/1.6.20 liefen dagegen über eine durchgehend bestehende Verbindung.
- Der DSI-Aufbau (Bus, Kommandokanal, Bildausgabe, Weckbefehle) steckt
jetzt in ws7_dsi_bringup() und ist damit wiederholbar.
- Nach dem ersten Durchlauf werden esp_lcd_panel_del(),
esp_lcd_panel_io_del() und esp_lcd_del_dsi_bus() aufgerufen und die
Strecke neu angelegt. Schalter WS7_DSI_RESTART, Pause
WS7_DSI_RESTART_MS.
Beide Panel-Varianten mit arduino-cli gegengebaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7JsiDJ1QbNFLDz5chf8Jt
Beobachtung am Gerät: Beim Einschalten geht nach 2-3 s die Beleuchtung an,
dann kommt nichts. Nach einem Reset geht die Beleuchtung kurz aus, danach
erscheint Beleuchtung samt Bild - reproduzierbar in zwei von zwei Fällen.
Dass die Beleuchtung ausgeht, zeigt: Die Bridge folgt dem DSI-Signal. Und
dass der zweite Durchlauf klappt, zeigt warum - die Bridge hatte ihre
Grundeinstellung aus dem ersten Durchlauf noch und fand diesmal ein
DSI-Signal vor. Beim ersten Start bekommt sie ihre Einstellung dagegen,
bevor die DSI-Strecke überhaupt existiert.
ws7_bridge_core_init() ist aus ws7_bridge_pre_init() herausgelöst und wird
jetzt ein zweites Mal aufgerufen, sobald esp_lcd_panel_init() läuft -
dasselbe Vorgehen wie bei ws7_panel_wake() in 1.6.19. Die Register sind
wiederholbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7JsiDJ1QbNFLDz5chf8Jt
Die Wartezeit aus 1.6.18 hat den Start deutlich verbessert, aber noch nicht
zuverlässig gemacht: beim ersten Anstecken nur Beleuchtung, nach Reset zwei
von drei Versuchen erfolgreich.
Aus dem Fehlerbild folgt, wo es hakt: Die Beleuchtung ging an. Sie hängt an
der Bridge und wird über I2C geschaltet, die Kommunikation steht also.
Verloren gehen die DCS-Weckbefehle an das Panel selbst - sie wurden genau
einmal geschickt, bevor die Videoausgabe lief. War der Panel-Controller
dann noch nicht aufnahmebereit, blieb der Bildschirm dunkel, während jeder
Schritt Erfolg meldete.
- ws7_panel_wake() gebündelt und wird jetzt zweimal aufgerufen: vor und
nach esp_lcd_panel_init(), getrennt durch WS7_PANEL_SETTLE_MS.
- Jeder Anlauf ist im Start-Log einzeln benannt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7JsiDJ1QbNFLDz5chf8Jt
Entscheidender Hinweis vom Gerät: "Sobald der serielle Monitor läuft,
startet es fast immer einwandfrei." Dessen USB-Anmeldung verzögert den
Start - und ersetzte damit unbeabsichtigt eine Wartezeit, die der
Werks-Testsketch explizit hat: delay(2000) am Anfang von setup().
Unsere Firmware sprach Bridge und Panel sofort nach dem Einschalten an.
Das Displaymodul ist dann noch nicht bereit: Die Bridge nimmt ihre
Register nicht an oder das Panel zeigt trotz korrekt gesendeter DSI-Daten
nichts - beides ohne Fehlermeldung, weil aus Sicht des P4 jeder Schritt
geklappt hat. Daher das sprunghafte Verhalten und der Fehlstart beim
ersten Anstecken.
jc_board_bringup() wartet für WS_PANEL_7H jetzt WS7_PANEL_WARMUP_MS
(Vorgabe 2000 ms), bevor es beginnt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7JsiDJ1QbNFLDz5chf8Jt
Der Störimpulsfilter aus 1.6.16 wirkt - nach dem Panel-Start melden sich
wieder alle drei I2C-Bausteine, keine Bridge- oder Touch-Fehler mehr.
Das Prüfbild aus 1.6.15 war als Test jedoch untauglich: gesetzt bei
esp_lcd_dpi_panel_set_pattern(), aber unmittelbar danach lief
lvgl_port_init() und schrieb in den Framebuffer, womit das Muster wieder
verschwand. Der Test sah damit genauso aus wie der Fehler, den er finden
soll.
Mit WS7_TEST_PATTERN = 1 endet jc_board_bringup() jetzt direkt nach dem
Muster: Backlight an, Rückgabe false, kein LVGL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Der I2C-Scan vor und nach dem Panel-Start war entscheidend:
vor: 0x14 (Touch) 0x18 (Audio) 0x45 (Bridge)
nach: 0x18
Es fallen genau die beiden Bausteine weg, die am Displaykabel hängen; der
Audio-Baustein auf der Platine bleibt erreichbar, der Bus arbeitet also.
Sobald die DSI-Ausgabe läuft, stören deren Signale auf das Flachbandkabel
ein. Die Bridge liess sich danach nicht mehr ansprechen
(ESP_ERR_INVALID_STATE), das Panel blieb dunkel.
i2c_master_bus_config_t bekommt für WS_PANEL_7H jetzt glitch_ignore_cnt = 7
und enable_internal_pullup - beides setzt auch das Waveshare-Beispiel. In
unserem Bringup fehlte es, weil die Bus-Einrichtung von den JC-Panels
übernommen wurde, die kein Flachbandkabel dieser Länge haben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Kaltstart, kein einziger Fehler im Log, alle I2C-Bausteine da - und trotzdem
keine Anzeige. Zwei Lücken in der Diagnose:
- Die DCS-Weckbefehle (MADCTL, Sleep Out, Display On) liefen ungeprüft
durch. Jetzt über BSP_STEP gemeldet.
- Nach dem Panel-Start endete die Ausgabe. "Bringup fertig" vor und
"LVGL laeuft" nach lvgl_port_init() trennen jetzt ein Hängenbleiben in
LVGL von einem fehlenden Bild.
Neuer Schalter WS7_TEST_PATTERN nutzt esp_lcd_dpi_panel_set_pattern() für
Farbbalken aus dem DSI-Baustein, ohne Framebuffer und ohne LVGL - wie im
funktionierenden Werks-Testsketch. Erscheinen sie, arbeitet die Kette bis
zum Panel und der Fehler liegt weiter oben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Der GT9271 meldete "i2c transaction failed" auch beim Kaltstart, während
alle Panel-Schritte fehlerfrei durchliefen und die Bus-Freitaktung aus 1.6.13
den Bus als frei meldete.
Ursache ist die Reihenfolge: Im funktionierenden Werks-Testsketch steht
initTouch() hinter enableBacklight(). Der GT9271 hängt an derselben
Versorgung wie das Panel und antwortet erst, wenn die Bridge über 0xAD
freigegeben ist. Beim Übertragen in die Firmware war das verloren gegangen -
jc_backlight_set() lief erst nach jc_board_bringup().
- Für WS_PANEL_7H wird die Beleuchtung jetzt innerhalb des Bringups vor der
Touch-Initialisierung eingeschaltet, mit WS7_TOUCH_POWER_MS Pause.
- Neue Diagnose ws7_i2c_scan() listet die antwortenden I2C-Adressen vor und
nach dem Panel-Start.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Der Log nach einem Reset zeigte alle Panel-Schritte fehlerfrei, aber
"i2c transaction failed" beim GT911. Bridge und Touch teilen sich den Bus:
Trifft ein Reset des P4 mitten in eine Übertragung, bleibt der Baustein in
seiner Bitausgabe stehen und hält SDA dauerhaft low. Beide hängen an
Dauerstrom, der Zustand überlebt also Reset und Flashen.
- ws7_i2c_bus_recover() taktet SCL vor dem Anlegen des Busses bis zu neunmal
von Hand, bis SDA freigegeben wird, und erzeugt dann eine Stop-Bedingung.
Bei freiem Bus läuft die Funktion wirkungslos durch.
- ws7_bridge_write() prüft den Rückgabewert. Bisher liefen fehlgeschlagene
Schreibzugriffe stillschweigend ins Leere - der eigentliche Grund, warum
das schwarze Bild keinerlei Spur hinterließ.
- pins_config.h: "#elsealle" statt "#else" seit 1.6.10 korrigiert. Der
Tippfehler traf nur den JC_PANEL_43-Zweig, der dadurch nicht mehr
übersetzbar war. Beide Panel-Varianten wieder gegengebaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Die Bridge-Abschaltfolge aus 1.6.11 hat den Fehler nicht behoben, sondern
verschlimmert: Nach C0/C2/AC = 0x00 kam das Panel auch direkt nach dem
Flashen nicht mehr hoch. Der Bringup entspricht wieder 1.6.10.
Stattdessen zwei Diagnosen zur Eingrenzung:
- esp_reset_reason() wird beim Hochlauf ausgegeben, um Kalt- von Warmstart
unterscheiden zu können.
- esp_ldo_acquire_channel() läuft nicht mehr ungeprüft durch, sondern meldet
einen Fehler über BSP_STEP. Es war der einzige Schritt ohne Prüfung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Nach einem Reset des P4 blieb das Bild schwarz, die Hintergrundbeleuchtung
brannte; nur direkt nach dem Flashen lief es.
Die Bridge des Panels hat keine Reset-Leitung und hängt an Dauerstrom. Ein
Reset des P4 setzt sie nicht zurück - ihre Register standen noch auf den
Werten der vorigen Sitzung, während der P4 seine DSI-Strecke komplett neu
aufbaut. Beim Flashen wird die Versorgung getrennt, deshalb trat der Fehler
dort nicht auf.
ws7_bridge_pre_init() schaltet die Bridge jetzt zuerst definiert ab
(AB/AA, AD, dann AC/C2/C0), wartet WS7_BRIDGE_RESET_MS und schreibt erst
danach die Grundinitialisierung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
- WS7_PARTIAL_REFRESH = 1 auch im Auslieferungsstand. Am Gerät geprüft: kein
Flackern; von den drei Tempo-Maßnahmen aus 1.6.4 ist es der größte Gewinn.
- make_page() setzt einen deckenden Hintergrund in COL_BG statt LV_OPA_TRANSP.
Optisch identisch, spart LVGL beim Scrollen aber das Zeichnen und Mischen
der darunterliegenden Ebenen.
- Neuer Schalter WS7_PERF_MONITOR schaltet LV_USE_SYSMON/LV_USE_PERF_MONITOR
ein und blendet Bilder je Sekunde und Prozessorlast ein. Vorgabe 0, mit 1
gegengebaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Der GT9271 lässt gelegentlich einen Messzyklus aus oder liefert ein
unplausibles Paket. Der Hersteller-Treiber kann "gerade nichts Neues" nicht
von "Finger weg" unterscheiden und meldet beides als losgelassen. Seit die
Abtastrate mit 1.6.8 von 33 auf 16 ms gestiegen ist, wurde daraus sichtbar
Drücken-Loslassen-Drücken-Loslassen.
Vor den Treiber ist jetzt ein eigener get_xy-Aufsatz gesetzt: Er verwirft
unplausible Rohwerte und hält den letzten gültigen Punkt noch
WS7_TOUCH_HOLD_MS lang (Vorgabe 40 ms). Der Aufsatz wird zur Laufzeit in das
Touch-Handle eingehängt, die Herstellerdateien bleiben unverändert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
LV_DEF_REFR_PERIOD steuert nicht nur die Bildrate, sondern auch die
Abtastrate des Touch und die Schrittweite von Animationen. Mit 33 ms waren
das 30 Schritte je Sekunde - beim Ziehen mit dem Finger sichtbar ruckelig.
Für das Waveshare-Panel jetzt 16 ms, passend zu dessen 60 Hz. Kommt das
Zeichnen nicht hinterher, wird ein Bild später fertig; kaputt geht nichts.
Die JC-Panels bleiben unverändert bei 33 ms.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Eine Messung mit WS7_TOUCH_DEBUG hat gezeigt, dass die Touch-Zuordnung stimmt:
Der Tipp auf den Menü-Knopf landete bei X=66/Y=27, der Knopf liegt aber bei
X 8..56. Mit 48x36 Pixeln ist er auf dem 7-Zoll-Panel ein sehr kleines Ziel.
lv_obj_set_ext_click_area vergrößert die Trefferfläche um 20 Pixel, ohne den
Knopf optisch zu ändern. Die Vergrößerung wirkt nur innerhalb der Kopfleiste,
weil diese ihre Kinder beschneidet - dem Seiteninhalt darunter wird nichts
weggenommen, und bei offenem Menü liegt ohnehin der Vorhang darüber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Der Menü-Knopf oben links reagiert am Waveshare-Panel nicht. Ob der Touch die
48x36 Pixel grosse Flaeche in der 44 Pixel hohen Kopfleiste ueberhaupt trifft,
liess sich bisher nicht nachsehen.
WS7_TOUCH_DEBUG in pins_config.h gibt jeden Berührpunkt roh und umgerechnet
über esp_rom_printf aus, also auch bei Core-Debug-Level "none". Vorgabe 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Zwei der drei Tempo-Maßnahmen aus 1.6.4 haben sich am Gerät als untauglich
erwiesen und stehen wieder auf ihrem alten Wert:
- WS7_COLOR_BITS = 24: Die Bridge dieses Panels nimmt kein RGB565 an.
- WS7_PARTIAL_REFRESH = 0: Der Menü-Vorhang ist halbtransparent und wird in
den gerade aktiven Bildpuffer eingemischt. Die nächste Zustandsmeldung der
S3 zeichnet in den zweiten Puffer, der ihn nie bekommen hat - der Vorhang
war damit sofort wieder weg und der Knopf schien wirkungslos. Nutzbar wäre
der Modus erst, wenn der LVGL-Port die geänderten Bereiche zwischen beiden
Puffern abgleicht.
Beide bleiben als Schalter erhalten, mit Notiz woran sie gescheitert sind.
WS7_PARALLEL_RENDER bleibt eingeschaltet - von den drei Maßnahmen trägt nur
diese.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
1280x720 sind 2,4-mal so viele Pixel wie beim 4,3-Zöller, und bis 1.6.3 wurde
bei jeder Änderung das komplette Bild in 24 Bit neu gezeichnet. Drei Schalter
in pins_config.h, einzeln zurückstellbar:
- WS7_COLOR_BITS = 16: RGB565 statt RGB888, halbe Datenmenge je Bild.
lv_conf.h liest den Wert aus pins_config.h, damit Sketch und LVGL-Bibliothek
dieselbe Farbtiefe benutzen; display_hal.cpp sichert das per static_assert.
- WS7_PARTIAL_REFRESH = 1: Avoid-Tear-Mode 3 (Direct-Mode, zwei Puffer) statt
Vollbild-Neuzeichnen.
- WS7_PARALLEL_RENDER = 1: LV_USE_OS = FREERTOS und zwei Zeichen-Threads.
Framebuffer-Bedarf sinkt dadurch von rund 8,3 MB auf 3,7 MB PSRAM. Die
JC-Panels sind von allen drei Schaltern nicht betroffen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Nach dem ersten Betrieb am Waveshare ESP32-P4-Pico:
- UART zur Hauptplatine auf TX = GPIO20 / RX = GPIO21 (vorher GPIO17/18).
Die JC-Panels bleiben bei GPIO33/GPIO31.
- Das Bild steht in der tatsächlichen Einbaulage richtig herum, deshalb ist
WS7_ROTATE_180 wieder 0. Der Schalter bleibt für den Fall einer anderen
Montage erhalten.
- Der Touch war oben/unten spiegelverkehrt: Der Digitizer sitzt gegenüber dem
Bild um die Hochachse gedreht. WS7_TOUCH_MIRROR_RAW_X steht daher auf 1.
Bild- und Touch-Ausrichtung sind zwei getrennte Schalter, weil sie an diesem
Panel nicht zusammenfallen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Ein fehlgeschlagener Panel-Aufbau endete bisher in einem ESP_ERROR_CHECK
tief im LVGL-Port und damit in einem Speicherauszug ohne erkennbare Ursache.
- Die Schritte des Waveshare-Bringups melden ihren Fehler über esp_rom_printf,
also auch bei Core-Debug-Level "none". Zusätzlich werden PSRAM-Größe,
freier PSRAM, größter freier Block und der Framebuffer-Bedarf ausgegeben.
- jc_board_bringup() liefert jetzt einen Status. Kommt das Panel nicht hoch,
bleibt die Firmware ohne Anzeige lauffähig (UART + OTA), statt beim ersten
LVGL-Zugriff erneut abzustürzen.
- ui.cpp: alle öffentlichen Funktionen prüfen, ob ui_init gelaufen ist.
- Fehlerbehebung: esp_lcd_touch_new_i2c_gt911 setzt im Fehlerfall einen bereits
freigegebenen Zeiger. Das Handle wird vorbelegt und der Rückgabewert geprüft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Der Ordner hieß JC_Display_Firmware, bedient seit 1.6.0 aber auch das
Waveshare 7inch DSI LCD (H) am ESP32-P4-Pico. Der Name führte in die Irre.
- JC_Display_Firmware/ -> P4_Display_Firmware/ (samt Sketch, den Arduino
gleichnamig zum Ordner verlangt)
- Doku/JC-Display_UART-Protokoll.md -> Doku/P4-Display_UART-Protokoll.md,
Verweise und Titel angepasst
- Verweise in CLAUDE.md, README und Quelltextköpfen nachgezogen
Nur Namen und Pfade, keine Logikänderung. Der Vendor-Ordner
JC_Display_Firmware_7zoll/ behält seinen Namen, ebenso die Bezeichner
JC_PANEL_TYPE/jc_board_bringup im Quelltext.
Enthält außerdem die bereits im Arbeitsverzeichnis liegende, noch nicht
committete Ergänzung des Git-Workflows in CLAUDE.md (PRs über die Gitea-API
statt der hängenden tea-CLI).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Das Panel ist auf dem Kopf eingebaut. WS7_ROTATE_180 in pins_config.h dreht
Bild und Touch gemeinsam; Vorgabe ist gedreht.
Die Drehung übernimmt der PPA-Grafikbeschleuniger (EXAMPLE_LVGL_PORT_PPA_
ROTATION_ENABLE), weil die Software-Rotation des LVGL-Ports nur RGB565
verarbeitet und für dieses RGB888-Panel nicht in Frage kommt. Der Touch wird
im process_coordinates-Hook mitgedreht, sonst läge die Bedienung
spiegelbildlich zum Bild.
MADCTL ist als WS7_MADCTL herausgezogen, falls die Bridge es doch auswertet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Der 40-polige Header des ESP32-P4-Pico hat eine andere Belegung als die
Expansion-IO der JC-Panels. Für WS_PANEL_7H gilt jetzt TX = GPIO17,
RX = GPIO18; die JC-Panels bleiben bei GPIO33/GPIO31.
README und Changelog entsprechend ergänzt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
Dieselbe P4-Firmware läuft jetzt zusätzlich auf einem Waveshare ESP32-P4-Pico
mit dem Waveshare 7inch DSI LCD (H) (1280x720). Auswahl über JC_PANEL_TYPE in
config.h (neu: WS_PANEL_7H, ab jetzt Vorgabe); JC_PANEL_43 und JC_PANEL_70
bleiben unverändert.
- board_bringup.c: DSI-Bus, DBI-Kommandokanal und DPI-Panel werden ohne
Hersteller-Panel-Treiber direkt angelegt (PLL_F20M, 2 Lanes zu 1250 Mbit/s,
80 MHz DPI, Austastlücken 64/64/64, RGB888). Panel-Freigabe und Helligkeit
über die Bridge auf I2C 0x45; Licht erst nach dem Start der DSI-Ausgabe.
- lv_conf.h leitet LV_COLOR_DEPTH aus JC_PANEL_TYPE ab (24 Bit für das
Waveshare-Panel, 16 Bit für die JC-Panels) und liest dafür config.h.
display_hal.cpp sichert das mit static_assert ab.
- Touch GT9271 (I2C 0x14) über den GT911-Treiber; Achsentausch und Begrenzung
auf die Bildfläche im process_coordinates-Hook.
- pins_config.h: Auflösung, DSI-Zeitbasis und Touch-Umrechnung als WS7_*-Werte.
- README und Changelog ergänzt.
Die UART-Pins zum S3 stehen für das Waveshare-Board noch auf den JC-Werten
(GPIO33/31) und müssen nach dem Header-Aufdruck gesetzt werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxAvqVnrY6YSoGFrNGeRTi
Dieselbe Display-Firmware bedient jetzt wahlweise das bisherige 4,3-Zoll-Panel
JC4880P443C-I-W (ST7701, 480x800) oder das neue 7-Zoll-Panel JC1060P470C-I-W
(JD9165, 1024x600). Umgeschaltet wird über JC_PANEL_TYPE in config.h
(JC_PANEL_43 / JC_PANEL_70) oder per Compiler-Flag -DJC_PANEL_TYPE=70. Vorgabe
bleibt das 4,3-Zoll-Panel; dessen Verhalten ändert sich nicht.
- src/lcd/esp_lcd_jd9165.c/.h aus dem Hersteller-Paket übernommen;
board_bringup.c legt je nach Panel DSI-Bus, DPI-Zeitbasis und Herstellertreiber an.
- pins_config.h liefert Auflösung, LCD-Reset (4,3": GPIO5, 7,0": GPIO27) und
Ausrichtung panelabhängig. Das 7-Zoll-Panel ist nativ Querformat, deshalb
entfallen dort die 270-Grad-Rotation und die PPA-Beschleunigung.
- lvgl_port_v9.h bezieht LVGL_PORT_H/V_RES aus pins_config.h statt fest 480/800.
- Der GT911 des 7-Zöllers meldet Rohkoordinaten im Raster 800x480 statt 1024x600.
Die Umrechnung hängt am process_coordinates-Hook von esp_lcd_touch, damit die
Herstellerdatei esp_lcd_touch_gt911.c unverändert bleibt.
- ui.cpp rechnet nicht mehr mit fest verdrahteten 480 Pixeln Höhe, sondern mit
DISP_VER_RES. Layout und Schriften bleiben sonst unverändert.
- Hersteller-Paket des 7-Zöllers unter JC_Display_Firmware_7zoll/ abgelegt;
Werkzeuge, Archive und mitgelieferte Fremdbibliotheken per .gitignore
ausgeschlossen (587 MB im Original, 21 MB im Repo).
Kompiliertest ESP32-P4, beide Varianten:
4,3": 1.173.768 B Flash (37 %), 322.880 B RAM (98 %)
7,0": 1.138.752 B Flash (36 %), 322.600 B RAM (98 %)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Über den Schalter kam die Abfrage "Espresso oder Cold Extraction" zuverlässig,
über Antippen am Display nur manchmal. Ursache waren zwei unterschiedliche
Bedingungen für dieselbe Sache:
- askMode (zeigt die Auswahl im Dialog) verlangte zusätzlich cxArmable
- choiceRelevant (sendet "wakeEspresso" und schließt damit das Auswahlfenster
der S3) verlangte es nicht
In der Lücke dazwischen zeigte der Dialog nur "Aufwecken", unterdrückte aber
trotzdem die Rückfrage der S3 - die Frage war auf beiden Seiten weg. Der
Schalterweg hatte diese Lücke nie, weil er allein an der S3 hängt.
P4: beide Stellen benutzen jetzt wake_choice_offered(). Bot der Dialog die
Auswahl nicht an, sendet er "deactivateStandby"; dann öffnet die S3 ihr
eigenes Auswahlfenster und die Frage kommt trotzdem.
S3: Die Fensterentscheidung ist als Bedingungskette geschrieben und hält
Quelle und Ergebnis des letzten Aufweckens fest. /Cold-Extraction zeigt das
an - die bisherige Live-Liste zeigt nur den jetzigen Stand, der beim
Nachsehen längst wieder ein anderer sein kann.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Abfrage erschien nicht, sobald der Cold-Extraction-Modus noch aktiv war — und
da er seit 5.3.1 persistent ist, war er das nach dem ersten kalten Bezug dauerhaft.
Damit erklärt sich, warum weder der Schalter- noch der Touch-Weg etwas zeigte.
Die Bedingung "Modus ist aus" war falsch gedacht ("sonst gibt es nichts zu fragen").
Die Auswahl muss in beide Richtungen gehen, also auch von kalt zurück auf Espresso.
Sonst hängt man nach dem ersten kalten Bezug im kalten Modus fest, ohne beim
Aufwecken je wieder gefragt zu werden.
- Bedingung aus beiden Öffnungspfaden entfernt (Standby-Übergang und Kaltstart).
- Das Fenster bleibt bei aktivem Modus offen; bisher schloss es sofort wieder.
- "wakeEspresso" beendet jetzt einen laufenden Modus, statt ihn nur nicht
einzuschalten — sonst wäre die Auswahl eine Einbahnstraße.
- Diagnose auf /Cold-Extraction listet den Modus nicht mehr als Voraussetzung,
sondern zeigt ihn als Ist-Zustand.
- P4: Der Cold-Knopf zeigt den Ist-Zustand ("aktiv lassen" und hervorgehoben, wenn
der Modus läuft).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Abfrage hängt an fünf Bedingungen gleichzeitig (freigeschaltet, Abfrage
eingeschaltet, Display verbunden, Modus gerade aus, kein Wartungsmodus/Reinigung/
Tuning). Fehlt eine, passiert schlicht nichts, und von außen war nicht erkennbar
welche. Die Seite /Cold-Extraction zeigt sie jetzt live mit Haken bzw. Kreuz an,
dazu ob das Auswahlfenster gerade offen ist.
Zusätzlich eine echte Lücke geschlossen: Die Abfrage kam nur beim Standby-Übergang.
Wer die Maschine am Netzschalter einschaltet, durchläuft nie einen solchen Übergang
— dieser Weg war nicht abgedeckt. Jetzt öffnet das Fenster einmal pro Laufzeit auch
nach einem Kaltstart, sobald sich das Display gemeldet hat. Startet die Maschine in
den Standby hinein, bleibt es beim Standby-Übergang.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Abfrage war eine reine Vorab-Abfrage des Displays und erschien nur beim
Antippen des Standby-Bildschirms. Beim Aufwecken über den Schalter ist der Standby
aber schon beendet, bevor das Display überhaupt fragen könnte — dieser Weg war nie
abgedeckt.
Die Steuerung hält jetzt nach dem Aufwecken ein Auswahlfenster offen (20 s): Der
Wasserkreis heizt in dieser Zeit nicht, das Display blendet die Auswahl von sich aus
ein und zeigt einen Countdown. Ohne Entscheidung wird danach normal geheizt, es geht
also nichts verloren, wenn niemand hinsieht.
Das Fenster öffnet nur mit angeschlossenem Display (sonst könnte niemand wählen),
bei freigeschalteter Funktion und aktiver Einstellung "Abfrage beim Aufwecken", und
nur wenn der Modus nicht ohnehin schon läuft. Es schließt bei Entscheidung,
Bezug/Spülen oder erneutem Standby.
Neue Aktion wakeEspresso (beendet Standby falls aktiv und schließt das Fenster) —
das Display sendet sie statt deactivateStandby, sobald die Auswahl relevant ist,
sonst würde direkt nach dem Aufwecken nochmal gefragt. Neue State-Felder
cxWakeChoice und cxWakeChoiceSec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Aufweck-Dialog am Touch-Display wird zur Auswahl: "Womit aufwecken?" mit
"Espresso" (heizt normal auf) und "Cold Extraction (Wasser bleibt kalt)". Damit
lässt sich bei jedem Aufwecken neu entscheiden, ohne vorher die Web-UI zu bedienen.
Bei der kalten Variante wird der Modus scharf geschaltet, bevor der Standby endet —
der Wasserkreis heizt also gar nicht erst an.
Abschaltbar über die neue Einstellung "Abfrage beim Aufwecken" (EEPROM 982), zu
finden auf /Cold-Extraction und auf der Cold-Extraction-Seite des Displays. Ist sie
aus, erscheint wieder der bisherige Dialog. Ohne angeschlossenes Display oder bei
nicht freigeschalteter Funktion hat sie keine Wirkung. Neues Feld cxAskOnWake im
State und in saveBrew.
Dazu: Die Meldungstexte der kalten Extraktion sind jetzt mit echten Umlauten
geschrieben ("nicht möglich" statt "nicht moeglich"). Für das OLED übersetzt die
neue Hilfsfunktion oledUmlauts() sie beim Rendern nach CP437 — dort hätte UTF-8
sonst zwei Fehlzeichen je Umlaut ergeben, weshalb die Texte ursprünglich
transliteriert waren. Web-UI und Touch-Display stellen UTF-8 direkt dar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Während einer kalten Extraktion stand als Zielgewicht überall noch der
Brew-by-Weight-Wert des normalen Espresso-Bezugs. OLED, Web-Dashboard und
Touch-Display griffen alle direkt auf brewByWeightTargetGrams zu — die kalte
Extraktion ersetzt Brew-by-Weight aber und stoppt nach coldExtractionTargetGrams.
Neue Hilfsfunktion activeTargetWeightGrams() liefert das Ziel des aktuellen bzw.
nächsten Bezugs. Das UART-Feld targetWeight führt jetzt diesen Wert; bbwTarget
bleibt unverändert die Roh-Einstellung, damit die Brew-Control-Seite weiter das
Richtige anzeigt.
Zusätzlich hing die Gewichtsanzeige selbst an "Brew-by-Weight aktiv" — das ist bei
kalter Extraktion typischerweise aus, obwohl ein Ziel existiert. OLED, Waage-Kachel
und Live-Bezugsschirm zeigen Gewicht und Fortschritt jetzt auch beim kalten Bezug.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cold Extraction bekommt in beiden Oberflächen einen eigenen Menüpunkt statt eines
angehängten Blocks auf der Brew-Control-Seite. Die Parameter sind damit erstmals
auch am Touch-Display änderbar.
S3: neue Seite /Cold-Extraction, gegliedert in Grundeinstellung, Vorbenetzung,
Hauptbezug, Bezugsende und Pumpenschutz, jeder Abschnitt mit kurzer Begründung.
/Brew-Control verweist nur noch darauf. Das UART-Kommando saveBrew akzeptiert jetzt
die cx-Felder, und das State-JSON liefert die restlichen Parameter mit, damit das
Display seine Felder vorbelegen kann.
Wichtig: Der Speicher-Handler von /Brew-Control wertet die cx-Felder nicht mehr aus.
Er tat es bisher, und nach dem Verschieben der Checkbox hätte ein Speichern dort die
Freischaltung abgeschaltet, weil das Feld im Formular fehlt.
P4: neue Seite "Cold Extraction" mit allen Parametern; der Modus-Schalter ist von der
Temperatur-Seite dorthin umgezogen, damit es nur eine Stelle gibt. Die Brew-Control-
Seite ist neu gegliedert: jede Funktion in einer eigenen Karte mit Titel und einer
Zeile, die erklärt was sie tut, statt einer durchlaufenden Liste unter
Zwischenüberschriften. Neues Hilfsmakro group_card() für beide Seiten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Messung an der Maschine: Nach 7 s Vorbenetzung kommen heiß 1-2 sirupartige Tropfen,
kalt bei voller Pumpenleistung rund 10 g dünne Brühe ohne Crema — etwa zehnfacher
Durchfluss trotz dreifacher Viskosität.
Die Ursache ist nicht die Viskosität, sondern das Kaffeebett: Kaltes Mehl quillt
nicht, ist schlecht benetzbar, es wandert kein Feinanteil und es lösen sich kaum
Feststoffe, die das Bett verdichten. Volle Leistung auf den trockenen kalten Puck
legt deshalb sofort Kanäle an, die sich — anders als bei heißem Bezug — nie wieder
schließen; der restliche Bezug läuft daran vorbei statt zu extrahieren.
Vorbenetzung und Hauptbezug pulsen jetzt beide, nur mit unterschiedlicher Leistung.
Neue Einstellung "Pumpenleistung Vorbenetzung" (Standard 10 %), Standard im
Hauptbezug von 30 % auf 20 % gesenkt. Bestehende Installationen behalten ihre Werte;
das neue Feld greift beim Update automatisch auf den Default zurück.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Standby aktiv" und "Kessel zu warm" galten bisher auch fürs Aktivieren. Das war
falsch herum gedacht: Der Modus schaltet die Heizung AB, man kam aber erst an den
Schalter, nachdem die Maschine aufgeweckt war und schon zu heizen begonnen hatte —
genau der Ablauf, den die Funktion vermeiden soll.
Scharfschalten und Beziehen sind jetzt getrennt:
- Scharfschalten (neues State-Feld cxArmable): blockiert nur durch fehlende
Freischaltung, Wartungsmodus, Reinigungsassistent, PID-Tuning oder einen
laufenden Bezug/Spülvorgang.
- Bezug (unverändert cxAllowed): zusätzlich kein Standby, Kessel unter der
Freigabeschwelle, gültiger Sensorwert.
Ist der Modus scharf, der Bezug aber gesperrt, sagen Web-UI und Display das jetzt
ausdrücklich. Bei Aktivierung mit warmem Kessel geht die Heizung sofort aus, die
Rückmeldung nennt die Sperre bis zum Unterschreiten der Schwelle.
P4: Der Aufweck-Dialog bekommt "Aufwecken mit Cold Extraction" — sendet
startColdExtraction vor deactivateStandby, damit der Wasserkreis gar nicht erst
anheizt. Der Schalter hängt jetzt an cxArmable; bei älterer S3-Firmware fällt das
Feld auf cxEnabled zurück und bleibt bedienbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ohne aktive und verbundene Waage greifen weder das Zielgewicht noch die
Stillstands-Erkennung — der Bezug endet dann erst nach der maximalen Bezugsdauer,
die damit faktisch zur Dosierung wird. Bisher passierte diese Degradierung
stillschweigend.
Die Funktion bleibt ohne Waage bewusst nutzbar (Zeitsteuerung als Rückfallebene),
damit ein kurzzeitig abgemeldeter HX711 die kalte Extraktion nicht komplett
blockiert. Angezeigt wird der Zustand jetzt an vier Stellen: dauerhafter
Warnhinweis auf /Brew-Control (Zielgewicht-Feld zusätzlich als "nur mit Waage
wirksam" beschriftet), Hinweiszeile unter dem Dashboard-Schalter, Statustext
"Cold Extraction bereit (ohne Waage: Zeitsteuerung)" und eine einmalige Meldung
beim Aktivieren.
P4: Statuszeile zeigt "ohne Waage, Ende nach Zeit" in Warnfarbe statt des blauen
Normalhinweises; Erklärzeile am Schalter und Parameter-Anzeige auf der Brew-Seite
ergänzen den Hinweis und nennen bei aktivem Modus ohne Waage die Zeit statt des
Zielgewichts. Neuer cxNotice-Code 4 (additiv, Protokoll bleibt v2).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Stillstands-Erkennung lief bereits während der Vorbenetzung mit. In dieser
Phase sättigt der Puck aber erst, in der Tasse kommt naturgemäß nichts an — bei
einer Vorbenetzung länger als die 15 s Stillstands-Zeit wurde deshalb jeder kalte
Bezug mit "kein Zulauf" abgebrochen. Mit dem bisherigen Standard von 30 s wäre das
ausnahmslos passiert; die Funktion war mit aktiver Waage unbenutzbar. Die
Erkennung startet jetzt erst mit dem Hauptbezug.
Dazu der Standard der Vorbenetzung von 30 s auf 10 s: Die Pumpe läuft in dieser
Phase bewusst ungepulst auf voller Leistung. Heiß kommt der erste Tropfen nach
etwa 7 s, kalt (rund dreifache Viskosität) entsprechend später — 30 s wären längst
voller Bezug bei vollem Druck gewesen, und die Hälfte des Pumpen-Laufzeitbudgets
wäre vor dem eigentlichen Bezug verbraucht. Bestehende Installationen behalten
ihren gespeicherten Wert.
Die Beschriftung auf /Brew-Control sagt jetzt ausdrücklich, dass die Pumpe während
der Vorbenetzung durchläuft und erst im Hauptbezug gepulst wird.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Von 68 Warnungen (arduino-cli --warnings all) auf eine einzige, die im
ESP32-Core-SDK steckt und nicht im Projekt behebbar ist.
- LV_FS_DEFAULT_DRIVE_LETTER heißt ab LVGL 9.3 LV_FS_DEFAULT_DRIVER_LETTER. Der
alte Name funktionierte weiter, löste aber in lv_api_map_v9_1.h ein #warning in
JEDER Übersetzungseinheit aus, die lvgl.h einbindet — allein rund 60 Warnungen
pro Build, die alles andere zugedeckt haben. In lv_conf.h umbenannt.
- LV_PART_* und LV_STATE_* sind in LVGL 9 verschiedene Enum-Typen; die direkte
|-Verknüpfung ist in C++20 abgekündigt. Neues Makro TH_SEL(part, state) in
theme.h castet beide auf lv_style_selector_t (ohnehin uint32_t), 5 Stellen.
- Bug-Fix: Der Puffer der Cold-Extraction-Infozeile auf der Brew-Seite war mit 192
Bytes zu klein für den ~205 Zeichen langen Text (das "°" zählt doppelt), die
letzte Zeile wurde abgeschnitten. Jetzt 256 Bytes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Display-Seite der kalten Extraktion: Schalter unter "Modi" auf der Temperatur-Seite
mit Erklärzeile, eigener Slot in der Statuszeile direkt hinter der
Sicherheitsmeldung, "Cold Extraction - Heizen aus" in der Wasser-Kachel statt eines
unerreichbaren Sollwerts, unterdrückter Aufheiz-Countdown, "Vorbenetzung" als erste
Phase des kalten Bezugs, Parameter-Anzeige auf der Brew-Seite und "(kalt)"-Markierung
in der Statistik (kalte Bezüge zählen nicht in die mittlere Dauer).
Bug-Fix P4: PROTO_RX_LINE_MAX lag bei 2048 Bytes, während die State-Zeile der S3
bereits rund 2,1 KB erreicht. Mit langem Status-, Profil- oder SSID-Text lag sie
darüber — dann wurde die ganze Zeile verworfen und das Display fror auf dem letzten
Stand ein, ohne dass die Verbindung als tot erkannt wurde. Limit jetzt 4096 Bytes;
der Puffer bleibt bei 2 KB reserviert, weil der interne RAM knapp ist und eine
Arduino-String nicht ins PSRAM alloziert werden kann.
S3 5.3.2: Sperrgrund und transiente Meldung gehen als Codes (cxBlock/cxNotice) statt
als Klartext ans Display, dazu cxPreInf für die Phasenleiste. Der bis zu 110 Byte
lange Freitext hätte die State-Zeile über das Zeilenlimit gedrückt; die Texte rendert
jetzt das Display, analog zur Trennung statusKey/statusText. Auf dem OLED und in den
ack-Antworten bleibt der Klartext unverändert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Modus wird im EEPROM (Adresse 977) gespeichert und beim Start wieder
übernommen, sofern die Funktion auf /Brew-Control freigeschaltet ist. Standby
beendet ihn nicht mehr, sondern verwirft nur die Laufzeitdaten.
Grund: Wer nur kalte Bezüge machen will, musste den Modus nach jedem Standby
neu einschalten. Bis dahin heizte die Maschine bereits auf und war für die
nächste kalte Extraktion zu warm — man musste auf das Abkühlen warten, also
genau die Situation, die der Modus vermeiden soll.
Der Modus endet nur noch durch explizites Ausschalten, durch Entzug der
Freischaltung oder durch einen Werksreset.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kalte Extraktion ohne Kessel-Bypass: Da kaltes Wasser nur bei kaltem Wasserkessel
am Puck ankommt, wird die Funktion über eine Temperaturschwelle freigegeben
(Standard 30 °C, 10–40 °C einstellbar). Solange der Modus aktiv ist, heizt der
Wasserkreis nicht (SSR gesperrt, Stellgröße und dutyW-Telemetrie auf 0, I-Anteil
eingefroren, Feed-Forward-Boost aus); der Dampfkreis bleibt unberührt.
Die Freigabe wird vor jedem Bezug neu geprüft, weil der weiter heizende
Dampfkessel den Wasserkessel über die Zeit über die Schwelle bringen kann. Ein
abgewiesener Start meldet den Grund auf OLED, Touch-Display und Web-Dashboard;
ein laufender Bezug wird nie abgebrochen. 5 °C Hysterese, Sensorfehler sperrt.
Fluss über Pumpen-Pulsung nach einer Vorbenetzung mit voller Leistung; Ende über
Zielgewicht oder Timeout. Brew-by-Time/Weight und FlowGuard sind während einer
kalten Extraktion außer Kraft, da FlowGuard sonst um denselben Pumpen-Ausgang
konkurrieren würde. Pumpenschutz über kumulierte Einschaltzeit mit Zwangspause
plus Stillstands-Erkennung bei aktiver Waage.
Kalte Bezüge werden im CSV-Log mit einem vierten Feld "C" gekennzeichnet, zählen
in allen Stückzahlen mit, bleiben aber aus der durchschnittlichen Bezugsdauer
heraus. Neue additive State-Felder cx* im UART-JSON (Protokoll bleibt v2) und
neuer statusKey "coldextraction".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Während eines Bezugs strömt kaltes Wasser in den Kessel; die PID reagiert
erst auf den verzögert gemessenen Temperaturabfall. Der neue Feed-Forward
schaltet der Heizung sofort einen einstellbaren Leistungs-Boost auf
(0-100 % der Fenstergröße, 0 % = aus), die PID regelt nur den Restfehler.
- Zustandslose Aufschaltung in der SSR-Fensterlogik (effectiveOutputWasserMs),
gedeckelt auf die Fenstergröße; Duty-Telemetrie (dutyW) zeigt den Boost mit
- Optional: I-Anteil der Wasser-PID während des Bezugs einfrieren
(zentral in der PID-Schleife verwaltet, applyPidWasserTunings-Helper)
- Nur bei echten Bezügen (Hardware/Software-Shot), nicht bei Spülen,
Wartungsmodus oder AutoTune; Sicherheitslogik unverändert
- Web-UI: neue Sektion „Feed-Forward bei Bezug (Wasser)“ auf /PID;
EEPROM 944/945, bewusst kein Profilfeld (maschinenabhängig)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Zeile stand linksbündig ohne max-width außerhalb der zentrierten
Seitenstruktur. Jetzt im status-message-Stil der Seite (zentriert,
max. 600 px, dezenter neutraler Rahmen), Profilname fett. Version 5.1.2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neuer Profil-Chip im Header (alle Seiten): zeigt das zuletzt geladene
bzw. gespeicherte Profil aus den neuen S3-State-Feldern profile/profDirty
(S3 ab 5.1.1), „*" = seitdem geänderte Einstellungen. Antippen öffnet
ein Schnellwahl-Overlay mit allen Profilen (aktives mit Häkchen und
Akzentfarbe); ein Tap lädt direkt per loadProfile. Profile-Seite
markiert das aktive Profil in der Liste. Bei älterer S3-Firmware bleibt
der Chip unsichtbar. Version 1.0.13, Protokoll-Doku ergänzt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Steuerung merkt sich das zuletzt geladene bzw. gespeicherte Profil
dauerhaft im EEPROM (Adressen 912-943). Profil-relevante Änderungen
(Setpoints, PID inkl. AutoTune, Boost, Eco, Brew-Control, Piezo,
Fast-Heat-Up) markieren das Profil als „geändert"; reine Anzeige-/
Systemeinstellungen bewusst nicht. Löschen des aktiven Profils und
Werksreset setzen die Anzeige zurück.
Sichtbar in der Web-UI (Dashboard + Profil-Seite) und als neue additive
State-Felder "profile"/"profDirty" im UART-JSON für das Touch-Display.
Version 5.1.1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compile-Schalter ENABLE_DISPLAY entfällt: Der Display-Typ (Ohne / OLED SH1106 /
UART-Touch) wird jetzt zur Laufzeit unter /Sensoren gewählt, im EEPROM (Adresse 911)
gespeichert und ohne Neustart übernommen. Vor der OLED-Initialisierung prüft eine
I2C-Probe (0x3C), ob das Display angeschlossen ist — ohne Antwort bleibt die Anzeige
deaktiviert und die Steuerung bootet sicher weiter. Die Touch-UART (Serial1) wird nur
noch bei gewähltem UART-Touch-Display initialisiert und bedient.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Dezimierung im Verlauf-Chart pickte pro Punkt ein Einzelsample; das
Abtast-Raster hing an g_histPos und wanderte mit jedem Sekundentick um
eine Position. Die schnell schaltende Heizleistung (PWM) sprang dadurch
sichtbar zwischen zwei Kurvenbildern hin und her. Jetzt wird über den
gesamten stride-Bucket gemittelt — Bild steht ruhig, Leistungs-Kurve
zeigt die echte mittlere Heizleistung. Version 1.0.12.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Karte von 460 auf 560 px verbreitert und Spaltenabstand von 10 auf 28 px
erhöht; die drei großen Werte standen zu nahe beisammen und waren schlecht
lesbar.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Verlauf-Seite: umschaltbares Zeitfenster 2/10/30/60 min über 1-s-Ringpuffer
(60 min Historie, 120 Chart-Punkte dezimiert), Fensterwahl in NVS
- Gestrichelte Soll-Linien für Wasser/Dampf; Y-Skala bezieht Soll-Werte nur
sichtbarer Kreise ein
- Heizleistung (Duty) Wasser/Dampf als zuschaltbare Zusatzkurven auf
Sekundärachse 0–100 % inkl. Live-Legende, Einstellung in NVS
- Temperaturkurven in Zehntelgrad statt ganzzahlig (Schwingungen ablesbar)
- PID-AutoTune-Live-Karte auf der Verlauf-Seite: Laufzeit, heizt/kühlt ab,
Schwingungshub je Kreis; nach Ende ~30 s Ergebnis farbcodiert
- PID-Tuning-Statusmeldung antippbar -> springt zur Verlauf-Seite
- Display-Firmware 1.0.11 + Changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PRs dürfen jetzt immer selbstständig gemergt werden (kein grünes CI mehr erforderlich)
- Branch-Namen, Commit-Messages und PR-Texte sind mit echten Umlauten zu schreiben
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wartungsmodus wird nun (wie auch bereits der ECO-Modus) beendet, sobald in den Standby gewechselt wird.
Dampf permanent Heizen (sofern aktiviert) wurde auch im Wartungsmodus durchgeführt, wodurch eine korrekte Reinigung des Dampfkreislaufs nicht möglich war - Behoben.