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
229 lines
12 KiB
Markdown
229 lines
12 KiB
Markdown
# 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`](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`](../Doku/P4-Display_UART-Protokoll.md).
|
||
|
||
## Panel-Auswahl
|
||
|
||
In [`config.h`](config.h):
|
||
|
||
```c
|
||
#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`](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 | 3 × 2,8 MB ≈ 8,3 MB PSRAM |
|
||
| 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`](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`](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`](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 + RX** — **kein** 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!**
|