Files
raw-designsandClaude Opus 5 db707ffd8c diag(P4): Touch-Rohwerte vermessen, einheitliche Zeitbasis (1.9.10)
Auswertung des letzten Logs (lv_tick lief 2728 ms hinter millis):
Tipp erkannt 49428, Rückfrage aufgebaut 49430, Bildaufbau fertig 49746.
Also 318 ms vom erkannten Tipp bis zum fertigen Bild - die gemessene Kette
ist schnell. Der Knopfdruck im Dialog liegt bei 55638 (Bereich 645..856,
374..426), die Antwort des S3 bei 59303, also 937 ms Rundlauf.

Die Verzögerung liegt damit VOR der Erkennung. Deshalb:
- ws7_touch_get_xy() meldet den ersten verworfenen Rohwert nach Ruhe mit
  Koordinaten und zählt, wie viele bis zur akzeptierten Berührung anfallen.
- Alle Messzeilen auf millis() statt lv_tick_get(); die beiden Uhren
  auseinanderzurechnen hat das Lesen unnötig erschwert.

Beide Panel-Varianten mit arduino-cli gegengebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeFMr439WcLMdebE3kU7fS
2026-09-03 13:51:48 +02:00
..

Touch-Display-Firmware (ESP32-P4)

Client-Firmware für die Touch-Displays, die als UART-Bedienung für die Dual-PID-Siebträgersteuerung dienen:

  • Guition JC4880P443C-I-W (4,3", ST7701, 480×800)
  • Guition JC1060P470C-I-W (7,0", JD9165, 1024×600)
  • Waveshare ESP32-P4-Pico + Waveshare 7inch DSI LCD (H) (1280×720)

Alle drei teilen sich denselben Sketch; das Zielpanel wird über JC_PANEL_TYPE in config.h gewählt.

Die Hauptplatine (ESP32-S3, Dual_PID_FastHeatUp.ino) bleibt die Steuerzentrale inkl. WLAN/Webinterface. Dieses Display ist ein reiner UART-Client: es empfängt den Zustand und sendet Bedienkommandos. Protokoll: siehe ../Doku/P4-Display_UART-Protokoll.md.

Panel-Auswahl

In config.h:

#define JC_PANEL_TYPE WS_PANEL_7H   // Waveshare P4-Pico + 7inch DSI LCD (H)  (Vorgabe)
#define JC_PANEL_TYPE JC_PANEL_43   // 4,3" JC4880P443C-I-W
#define JC_PANEL_TYPE JC_PANEL_70   // 7,0" JC1060P470C-I-W

Der Schalter steuert Panel-Treiber, Auflösung, Rotation, Reset-/Backlight-Ansteuerung, die Touch-Umrechnung und die Farbtiefe von LVGL; UI und Protokoll bleiben identisch.

Nur hier umschalten, nicht per Compiler-Flag. lv_conf.h liest JC_PANEL_TYPE aus config.h, weil das Waveshare-Panel RGB888 verlangt und LVGL deshalb mit 24 statt 16 Bit übersetzt werden muss. Ein -D-Flag der IDE erreicht die LVGL-Bibliothek nicht — Sketch und Bibliothek liefen dann mit verschiedenen Farbtiefen. display_hal.cpp bricht den Build mit einer static_assert ab, falls das doch passiert.

Hardware

Vendor-Pakete: 4,3" im Ordner JC4880P443C_I_W/, 7,0" im Repo-Ordner ../JC_Display_Firmware_7zoll/.

Komponente 4,3" JC4880P443C-I-W 7,0" JC1060P470C-I-W Waveshare P4-Pico + 7" DSI (H)
Haupt-MCU (UI) ESP32-P4 ESP32-P4 ESP32-P4 (32 MB PSRAM, 32 MB Flash)
WLAN/BT (hier ungenutzt) ESP32-C6 ESP32-C6 — (kein Funkmodul)
Display ST7701, 480×800 IPS, MIPI-DSI JD9165, 1024×600 IPS, MIPI-DSI 1280×720 IPS, MIPI-DSI 2 Lanes über Waveshare-Bridge
Farbformat RGB565 RGB565 RGB888 — die Bridge nimmt kein RGB565 (geprüft)
LVGL-Ausrichtung Rotation 270° (PPA) → 800×480 keine Rotation, nativ 1024×600 keine Rotation, nativ 1280×720
Touch GT911, I²C SDA=GPIO7 / SCL=GPIO8 GT911, I²C SDA=GPIO7 / SCL=GPIO8 GT9271 (I²C-Adresse 0x14), SDA=GPIO7 / SCL=GPIO8
Touch-Rohkoordinaten 480×800 (= Panelraster) 800×480 → wird auf 1024×600 skaliert 720×1280 (Hochformat) → X/Y werden getauscht
LCD-Reset GPIO5 GPIO27 kein Reset-Pin (macht die Bridge)
Backlight GPIO23 (LEDC-PWM) GPIO23 (LEDC-PWM) Bridge-Register auf I²C 0x45 (AD/AB/AA)
Framebuffer 3 × 750 KB ≈ 2,3 MB PSRAM 3 × 1,2 MB ≈ 3,6 MB PSRAM 1 × 2,8 MB — mehr verträgt das Panel beim Start nicht
Freie Expansion-IO Stiftleiste JP1: GPIO 52/51/50/49/35/34/33/32/31/30/29/28 FPC 1,0 mm 16-pol. FPC4: GPIO 34/33/32/31/30/29/28 · FPC3: 48/47/46/45/5/4/3/2 40-poliger Header im Raspberry-Pi-Pico-Format; belegt sind I²C (7/8), TF-Karte (9/10/11/12/13/53) und Debug-UART0 (37/38)
UART zur Hauptplatine TX GPIO33 / RX GPIO31 TX GPIO33 / RX GPIO31 TX GPIO20 / RX GPIO21

Waveshare 7" (H) — Besonderheiten des Bringups

Das Panel hängt hinter einer Waveshare-Bridge und braucht keinen Hersteller-Panel-Treiber; board_bringup.c legt DSI-Bus, DBI-Kommandokanal und DPI-Panel direkt an:

  • Referenztakt der D-PHY = PLL_F20M (Legacy). Mit der Standardquelle bleibt das Bild auf ESP32-P4 Rev. 1.3 / ECO2 schwarz.
  • 2 Lanes à 1250 Mbit/s, DPI-Takt 80 MHz, Austastlücken 64/64/64 (≈ 60 Hz). Alle Werte stehen in pins_config.h (WS7_*) und lassen sich dort ändern, falls das Bild reißt oder flimmert — 1280×720 in RGB888 belastet den PSRAM spürbar.
  • Reihenfolge: Bridge-Grundinitialisierung (C0/C2/AC) → DSI → DCS MADCTL/Sleep Out/ Display On → DPI-Start → erst dann Hintergrundbeleuchtung. Wird das Licht früher eingeschaltet, blitzt beim Start Bildrauschen auf.
  • Einbaulage: WS7_ROTATE_180 in pins_config.h dreht Bild und Touch um 180°, falls das Panel auf dem Kopf montiert wird. Vorgabe ist 0 — in der jetzigen Einbaulage steht das Bild richtig herum. Die Drehung macht der PPA-Grafikbeschleuniger; die Software-Rotation des LVGL-Ports beherrscht nur RGB565.
  • Touch-Spiegelung: Der Digitizer sitzt gegenüber dem Bild um die Hochachse gedreht. WS7_TOUCH_MIRROR_RAW_X steht deshalb auf 1 (dreht oben/unten), unabhängig von der Bilddrehung.

Waveshare 7" (H) — Tempo-Schalter

1280×720 bedeutet 2,4-mal so viele Pixel wie beim 4,3-Zöller. Drei Schalter in pins_config.h halten die Bedienung flüssig; jeder ist einzeln zurückstellbar, falls er Ärger macht:

Schalter Vorgabe Wirkung Stand am Gerät
WS7_COLOR_BITS 24 16 (RGB565) würde die Datenmenge je Bild halbieren. Geht an diesem Panel nicht — die Bridge nimmt nur RGB888
WS7_PARTIAL_REFRESH 0 1 zeichnet nur geänderte Bereiche neu (LVGL-Direct-Mode, zwei wechselnde Puffer). Wäre der größte Gewinn. Geht so nicht — der halbtransparente Menü-Vorhang landet nur in einem der beiden Puffer und ist sofort wieder weg
WS7_PARALLEL_RENDER 1 LVGL zeichnet mit zwei Threads auf beiden Prozessorkernen. Aktiv

Damit WS7_PARTIAL_REFRESH nutzbar würde, müsste der LVGL-Port die geänderten Bereiche zwischen beiden Puffern abgleichen. Die Maschinerie dafür liegt in lvgl_port_v9.c, wird aber nur bei gedrehter Ausgabe übersetzt.

Die Farbtiefe muss für Sketch und LVGL-Bibliothek dieselbe sein; lv_conf.h liest WS7_COLOR_BITS deshalb direkt aus pins_config.h.

Beim 7-Zöller ist die Expansion-IO kein Stiftleisten-Header, sondern eine FPC-Buchse (1,0 mm Raster, 16-polig) — für die UART-Verdrahtung wird ein FPC-Breakout/Adapterkabel gebraucht. Schaltplan: ../JC_Display_Firmware_7zoll/5-Schematic/4_CONN.png.

Verkabelung zur Hauptplatine (UART, gekreuzt)

Für beide JC-Panels dieselben P4-Pins (4,3": Header JP1, Schaltplan JC4880P443C_I_W/5-Schematic/4_USB&IO.png · 7,0": FPC-Buchse FPC4):

  S3 GPIO43 (TX) ----> P4 GPIO31 (RX)     (DISPLAY_UART_RX_PIN)
  S3 GPIO44 (RX) <---- P4 GPIO33 (TX)     (DISPLAY_UART_TX_PIN)
  S3 GND        ------ P4 GND

Waveshare ESP32-P4-Pico (40-poliger Header):

  S3 GPIO43 (TX) ----> P4 GPIO21 (RX)     (DISPLAY_UART_RX_PIN)
  S3 GPIO44 (RX) <---- P4 GPIO20 (TX)     (DISPLAY_UART_TX_PIN)
  S3 GND        ------ P4 GND

Die rechte JP1-Spalte (C6_U01TXD/C6_I10RXD/…) gehört zum ESP32-C6 — nicht verwenden. 3,3-V-Logik beidseitig → keine Pegelwandlung nötig.

Projektstruktur (eigener Code)

Datei Zweck
P4_Display_Firmware.ino setup()/loop(), verdrahtet die Module
config.h Pins, UART, Protokoll-Parameter
machine_state.h Spiegel des state-Objekts der S3
protocol_client.h/.cpp UART-Protokoll: hello/state/ack/profiles/… + Heartbeat
ui.h/.cpp LVGL-Dashboard (hardwareunabhängig)
display_hal.h/.cpp LVGL-/UI-Verdrahtung + Lock-Wrapper
board_bringup.c esp_lcd-Hardware-Bringup (C; nur bei JC_USE_REAL_PANEL=1)

Abhängigkeiten

  • Arduino-ESP32 ≥ 3.1 (mit ESP32-P4-Support), Board: ESP32P4 Dev Module, PSRAM aktiviert.
  • LVGL 9.x. Die lv_conf.h im Sketch-Ordner ist die maßgebliche; sie wird über den Include-Pfad des Sketches gefunden und leitet LV_COLOR_DEPTH aus JC_PANEL_TYPE ab. Liegt zusätzlich eine alte lv_conf.h neben dem lvgl-Ordner der Bibliotheken, wird sie nicht benutzt — sie darf dort aber auch nicht stören.
  • ArduinoJson 7.x.

Zwei Betriebsmodi (JC_USE_REAL_PANEL in display_hal.h)

0 = Stub-Modus (Standard)

Kompiliert ohne Display-Treiber (eigenes Minimal-LVGL, rendert ins Leere). Ideal, um Protokoll/UART/Logik am Schreibtisch zu testen (Debug über USB-CDC Serial). Läuft auf jedem ESP32 mit LVGL+ArduinoJson.

1 = echte Hardware (Hersteller-BSP)

display_hal.cpp baut auf der erprobten Hersteller-Portierung auf. Dazu folgende Dateien aus JC4880P443C_I_W/1-Demo/arduino_examples/lvgl_v9_sw_rotation/ in diesen Sketch-Ordner kopieren:

pins_config.h
lvgl_port_v9.h
lvgl_port_v9.c
src/lcd/      (esp_lcd_st7701*.c/.h + st7701_lcd.*  sowie  esp_lcd_jd9165.c/.h)
src/touch/    (komplett: esp_lcd_touch*.c/.h, gt911_touch.*)

Diese Dateien liegen bereits im Sketch. esp_lcd_jd9165.* stammt aus dem 7-Zoll-Vendor-Paket (../JC_Display_Firmware_7zoll/1-Demo/Demo_Arduino/1_2_Lvgl_V9/.../src/lcd/); je nach JC_PANEL_TYPE wird nur einer der beiden Panel-Treiber eingebunden.

Der Init-Ablauf (MIPI-DSI-PHY-LDO, ST7701 bzw. JD9165, GT911, LEDC-Backlight, lvgl_port_init) ist 1:1 aus dem Vendor-Beispiel lvgl_sw_rotation.c übernommen — nur der Demo-Aufruf (lv_demo_widgets()) wurde durch ui_init() ersetzt.

Die Touch-Skalierung des 7-Zöllers hängt in board_bringup.c am process_coordinates-Hook von esp_lcd_touch, damit die Vendor-Datei esp_lcd_touch_gt911.c unverändert bleibt (im Vendor-Demo ist die Umrechnung direkt in den Treiber gepatcht).

Wichtig: Im realen Modus läuft LVGL in einem eigenen FreeRTOS-Task. UI-Zugriffe aus der Arduino-loop() (z. B. ui_update bei jedem state-Push) sind daher in hal_lock()/hal_unlock() (= lvgl_port_lock/unlock) gekapselt — ist in der .ino bereits umgesetzt.

Ausrichtung

Das Dashboard ist Querformat. Beim 4,3-Zöller (physisch 480×800) wird das per LVGL-Rotation 270° mit PPA-Beschleunigung erreicht → 800×480. Der 7-Zöller ist nativ 1024×600, dort steht die Rotation auf 0 und PPA ist abgeschaltet. Beides setzt pins_config.h automatisch anhand von JC_PANEL_TYPE.

Das Layout arbeitet fast durchgehend mit LV_PCT/LV_SIZE_CONTENT und skaliert deshalb mit. Feste Pixelmaße (Rundinstrumente, Thermometer, Buttons) bleiben gleich groß — auf dem 7-Zöller entsteht dadurch mehr Luft. Die Schriftgrößen bleiben ebenfalls unverändert: 4,3" ≈ 213 dpi, 7,0" ≈ 170 dpi, gleiche Pixelhöhe wirkt dort physisch größer.

Status

Fertig & hardwareunabhängig:

  • Vollständiger UART-Protokoll-Client (v2): hello-Handshake, state-Parsing, ack/error, Profile, Heartbeat, Link-Timeout.
  • Dashboard: Live-Temperaturen Wasser/Dampf (ist/soll), Duty-Balken, Status-Banner (Farbe nach statusKey), Verbindungs- & WLAN-Anzeige, Bezugs-Timer, Bedien-Buttons.
  • Senden für setPid, startAutotune/stopAutotune, getProfileDetails vorhanden.

Board-Bringup: auf die konkreten JC-Pins/Treiber gesetzt (ST7701 bzw. JD9165, GT911, Backlight, I²C) — sobald die BSP-Dateien kopiert und JC_USE_REAL_PANEL=1 gesetzt ist, lauffähig.

Noch offen (UI-Ausbau): weitere Screens — Einstellungen (Brew/Service/Sensor), Profil-Auswahl/-Editor, PID/AutoTune-Bedienung.

Inbetriebnahme / Fehlersuche (Lessons learned)

  • Stromversorgung: Jedes Board eigene Versorgung. Zwischen P4 und S3 nur GND + TX + RXkein 3V3/5V! Den S3 nicht über den 3V3-Header des Displays speisen (reicht nicht / Regler-Rückspeisung).
  • Pins nach Silkscreen: Die UART-Pads am Board nach Aufdruck GPIO50/GPIO51 identifizieren, nicht nach Header-Pin-Nummern.
  • S3-Seite: muss mit TOUCH_UART_ENABLED 1 geflasht sein und laufen. Da Serial (Debug) und der Touch-UART beide auf GPIO43/44 liegen, ggf. „USB CDC On Boot: Enabled" verwenden, damit der Debug-Port nicht die UART-Pins blockiert.

Diagnose-Schalter (in config.h)

  • SHOW_DEBUG_OVERLAY 1 → blendet unten ein grünes Panel ein: gesendete hello, empfangene Bytes/Zeilen, Parse-Fehler, letzte Rohzeile, Link-Status.
  • UART_SELFTEST_INTERNAL_LOOPBACK 1 → legt TX chip-intern auf RX (ohne Draht/Pins). Läuft „RX byte" hoch, sind Firmware+UART ok → Problem liegt an Pins/Verkabelung. Nach dem Test wieder auf 0!