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>
28 KiB
JC4880P443C-I-W Touch-Display — UART-Protokoll & Architektur
Status: Entwurf v1.0 der Doku · Firmware-Protokollversion: 1 (vorhanden, abgeschaltet) · Ziel-Protokollversion: 2 (geplante Erweiterungen)
Diese Datei beschreibt die Anbindung des Touch-Displays JC4880P443C-I-W an die
Siebträger-Steuerung (Dual_PID_FastHeatUp.ino). Sie dokumentiert den bereits
vorhandenen UART-Code, bewertet ihn und legt die geplanten Erweiterungen fest.
Es wird noch kein Code geändert — diese Doku ist die Grundlage dafür.
1. Architektur-Überblick
Bewusste Designentscheidung (siehe Projektbesprechung):
- Das Webinterface bleibt auf der Hauptplatine (ESP32-S3). Es bleibt unverändert die zentrale Steuerungs- und Konnektivitätsinstanz.
- Das JC-Display ist ein vollwertiges Touch-Bediengerät, das über UART angebunden ist und denselben Funktionsumfang bietet wie das Webinterface — aber kein eigenes WLAN/Webserver betreibt.
- Das OLED (SH1106G) bleibt optional und wird wie bisher über ein Compile-Flag
gewählt (
ENABLE_DISPLAY). Es ist unabhängig vom JC-Display.
+----------------------------+ UART (GPIO43/44) +---------------------------+
| Hauptplatine ESP32-S3 | <----------------------------------> | JC4880P443C-I-W |
| Dual_PID_FastHeatUp.ino | JSON-Zeilen, 230400 Baud, 8N1 | ESP32-P4 (UI/LVGL) |
| | | + ESP32-C6 (WLAN/BT)* |
| - PID-Regelung (Wasser/ | S3 -> P4: state / ack / error | |
| Dampf), SSRs, Sensoren | P4 -> S3: op-Kommandos | - Touch-Bedienung |
| - Waage, Profile, EEPROM | | - Statusanzeige |
| - WLAN + Webinterface | | - Einstellungen |
| - OLED (optional) | | ST7701 / GT911 / ES8311 |
+----------------------------+ +---------------------------+
* Der ESP32-C6 auf dem JC-Display (WLAN/BT) wird in diesem Projekt NICHT für die
Maschinensteuerung benötigt. WLAN/Webinterface laufen weiterhin auf der S3.
Rollenverteilung
| Aufgabe | S3 (Hauptplatine) | P4 (JC-Display) |
|---|---|---|
| Echtzeit-Regelung (PID, SSR, Sicherheit) | ja | nein |
| Sensoren (NTC, MAX6675, Waage) | ja | nein |
| Persistenz (EEPROM, FFat-Profile) | ja | nein |
| WLAN + Webinterface | ja | nein |
| Touch-UI / Bedienoberfläche | nein | ja |
| OLED-Statusanzeige (optional) | ja (Compile-Flag) | — |
| "Quelle der Wahrheit" für den Zustand | ja | spiegelt nur |
Wichtig: Die S3 bleibt die alleinige Quelle der Wahrheit. Das P4-Display hält keinen
eigenen, abweichenden Zustand, sondern stellt den von der S3 gepushten state dar und
schickt Bedienwünsche als Kommandos. So bleiben Web-UI, OLED und Touch-Display konsistent.
2. Hardware-Anbindung
| Parameter | Wert | Quelle im Code |
|---|---|---|
| UART-Instanz | Serial1 |
HardwareSerial& touchUart = Serial1; |
| TX (S3 -> P4) | GPIO43 | #define TOUCH_UART_TX_PIN 43 |
| RX (S3 <- P4) | GPIO44 | #define TOUCH_UART_RX_PIN 44 |
| Baudrate | 230400 | #define TOUCH_UART_BAUDRATE 230400 |
| Format | 8N1 | touchUart.begin(..., SERIAL_8N1, RX, TX) |
| Logikpegel | 3,3 V | ESP32-S3 / ESP32-P4 beide 3,3 V — keine Pegelwandlung nötig |
Verkabelung (gekreuzt)
S3 GPIO43 (TX) -----> P4 RX
S3 GPIO44 (RX) <----- P4 TX
S3 GND ------ P4 GND (gemeinsame Masse zwingend)
Hinweis (bewusst akzeptiert): GPIO43/44 sind beim ESP32-S3 zugleich die Standard- Pins der seriellen Debug-Konsole (UART0). Solange das JC-Display dort angeschlossen ist, steht die klassische serielle Debug-Ausgabe an diesen Pins nicht parallel zur Verfügung. Debug/Logging erfolgt dann über USB-CDC bzw. das Webinterface. Diese Pins wurden ausdrücklich beibehalten, weil sie auf der Platine herausgeführt sind.
3. Transport & Framing
- Zeilenbasiert: Eine Nachricht = eine kompakte JSON-Zeile, abgeschlossen mit
\n.\rwird empfangsseitig ignoriert. - Max. Zeilenlänge (RX):
TOUCH_UART_MAX_LINE_LEN= 1024 Byte. Längere Zeilen werden verworfen und mit einemerror("Zeile zu lang") quittiert (Overflow-Schutz). - State-Puffer (TX):
TOUCH_UART_JSON_BUFFER_SIZE= 2400 Byte (snprintf-basiert). - Encoding: ASCII/UTF-8. Strings werden escaped (
jsonEscapeForUart,escapedStatusTextetc.):\,",\n,\r,\t. - Kein Binär-Frame, kein CRC in v1. (CRC/Handshake siehe v2-Erweiterungen.)
Nachrichtenrichtungen
| Richtung | type/op |
Bedeutung |
|---|---|---|
| S3 -> P4 | type:"state" |
Vollständiger Zustands-Push (Telemetrie) |
| S3 -> P4 | type:"ack" |
Bestätigung eines Kommandos (ok:true/false, message) |
| S3 -> P4 | type:"error" |
Fehler (Parsing, unbekannter op, Overflow) |
| S3 -> P4 | type:"profiles" |
Liste der Profilnamen (Antwort auf listProfiles) |
| S3 -> P4 | type:"usageStats" |
Nutzungsstatistik (Antwort auf getUsageStats) |
| P4 -> S3 | op:"..." |
Kommando vom Display (siehe §6) |
Jedes Kommando trägt eine optionale id (uint32), die in ack/error zurückgespiegelt
wird (Request/Response-Korrelation).
4. Push-Verhalten (S3 -> P4)
Implementiert in touchUartTick():
- Client-Aktivierung: Erst nachdem das Display ein Kommando gesendet hat, gilt es als
aktiv (
touchUartClientActive). Ohne aktiven Client wird nicht gepusht. - Client-Timeout:
TOUCH_UART_CLIENT_TIMEOUT_MS= 15000 ms ohne Kommando -> inaktiv. -> Das Display muss regelmäßigping(oder ein beliebiges Kommando) senden (Heartbeat). - Adaptives Intervall:
- Leerlauf:
TOUCH_UART_INTERVAL_IDLE_MS= 1000 ms - Aktiv (Bezug/Flush/Burst):
TOUCH_UART_INTERVAL_ACTIVE_MS= 250 ms
- Leerlauf:
- Force-Push bei Zustandswechseln: Bezug, Flush, Dampfkreis (= Dampfbezug/Dampf-Flush), Tarier-Phase, Sensorfehler/Sicherheitsabschaltung, Reinigungsassistent, Standby (Details in §5).
- Burst (250 ms) zusätzlich während: Bezug, Flush, Dampfkreis, Tarier-Phase, Reinigungsassistent.
- Burst nach Bezugsende:
TOUCH_UART_SHOT_END_GRACE_MS= 500 ms Nachlauf.
Empfehlung P4-Seite: alle ~5 s ping senden (Heartbeat), nach jedem Bedienvorgang
zusätzlich getState ist nicht nötig (die S3 pusht nach jedem Kommando automatisch state).
5. Telemetrie — state-Objekt (Protokoll v1, IST-Zustand)
Erzeugt von buildTouchUartStateJson(). Alle Felder in einem flachen JSON-Objekt.
| Feld | Typ | Bedeutung |
|---|---|---|
type |
string | immer "state" |
protocolVersion |
int | aktuell 1 |
firmwareVersion |
string | z. B. "4.8.1" |
tempW |
float | Ist-Temperatur Wasser (angezeigt) |
setW |
float | Soll-Temperatur Wasser |
tempD |
float | Ist-Temperatur Dampf (angezeigt) |
setD |
float | Soll-Temperatur Dampf |
statusText |
string | Klartext-Status (DE), z. B. "Bezug aktiv..." |
statusKey |
string | maschinenlesbarer Statusschlüssel (siehe §7) |
shotActive |
bool | Bezug läuft |
shotElapsedMs |
uint | vergangene Bezugszeit in ms |
steamCircuitActive |
bool | Dampf-/Wasserkreis aktiv |
steamFlushActive |
bool | Steam-Flush aktiv |
flushActive |
bool | Flush aktiv |
ecoActive |
bool | Eco-Modus aktiv |
standbyActive |
bool | Standby aktiv |
maintenanceActive |
bool | Wartungsmodus aktiv |
steamHeatDisabled |
bool | Dampfheizung per Nutzer deaktiviert |
lightOn |
bool | Beleuchtung an |
piezoEnabled |
bool | Piezo-Summer aktiv |
profile |
string | zuletzt geladenes/gespeichertes Profil, leer = keines (additiv seit S3 5.1.1) |
profDirty |
bool | profil-relevante Einstellungen seit dem Laden geändert (additiv seit S3 5.1.1) |
weight |
float | aktuelles Waagengewicht (UI) |
targetWeight |
float | Ziel-Gewicht (Brew-by-Weight) |
scaleEnabled |
bool | Waage aktiviert |
scaleType |
uint | Waagentyp |
scaleConnected |
bool | Waage verbunden |
hx711CalFactor |
float | HX711-Kalibrierfaktor |
hx711DisplaySmoothing |
bool | HX711-Anzeigeglättung |
piEnabled |
bool | Pre-Infusion aktiv |
bbtEnabled |
bool | Brew-by-Time aktiv |
bbwEnabled |
bool | Brew-by-Weight aktiv |
sbtEnabled |
bool | Steam-by-Time aktiv |
bbtSecs |
float | Brew-by-Time Zielzeit (s) |
sbtSecs |
float | Steam-by-Time Zielzeit (s) |
piDurSecs |
float | Pre-Infusion Dauer (s) |
piPauseSecs |
float | Pre-Infusion Pause (s) |
bbwTarget |
float | Brew-by-Weight Zielgewicht (g) |
bbwOffset |
float | Brew-by-Weight Offset (g) |
caseSensorEnabled |
bool | Gehäusesensor aktiv |
caseTempDashboard |
bool | Gehäusetemp. im Dashboard sichtbar (effektiv) |
caseTempDashboardSetting |
bool | Einstellung Dashboard-Anzeige |
caseTempDisplaySetting |
bool | Einstellung Display-Anzeige |
caseSensorType |
uint | Typ des Gehäusesensors |
caseOffset |
float | Offset Gehäusesensor |
caseTemp |
float|null | Gehäusetemperatur (null bei Fehler/inaktiv) |
maintenanceInterval |
int | Wartungsintervall |
maintenanceIntervalCounter |
uint | Wartungszähler |
flushDurationSeconds |
uint | Flush-Dauer (s) |
steamFlushDurationSeconds |
uint | Steam-Flush-Dauer (s) |
xSwitchAction |
uint | X-Switch Kurz-Aktion |
xSwitchLongAction |
uint | X-Switch Lang-Aktion |
v2-Felder (seit Phase A.1 implementiert):
| Feld | Typ | Bedeutung |
|---|---|---|
dutyW |
float | Heizleistung Wasser in % (OutputWasser / windowSizeWasser * 100) |
dutyD |
float | Heizleistung Dampf in % |
kpW / kiW / kdW |
float | PID-Parameter Wasser (read-only) |
kpD / kiD / kdD |
float | PID-Parameter Dampf (read-only) |
autoTuneW |
bool | AutoTune Wasser läuft |
autoTuneD |
bool | AutoTune Dampf läuft |
autoTuneWStatus |
uint8 | Ergebnis/Grund letzter Wasser-Lauf: 0=Idle, 1=läuft, 2=Erfolg, 3=Failsafe (>9 Peaks, evtl. ungenau), 4=degeneriert (keine Schwingung, Werte verworfen), 5=Sensorfehler, 6=Sicherheitsabschaltung, 7=manuell abgebrochen |
autoTuneDStatus |
uint8 | Ergebnis/Grund letzter Dampf-Lauf (Codes wie autoTuneWStatus) |
wifiConnected |
bool | STA verbunden oder AP-Modus aktiv |
apMode |
bool | Access-Point-Modus aktiv |
ip |
string | aktuelle IP (STA-IP, sonst softAP-IP) |
ssid |
string | verbundene SSID bzw. AP-SSID |
rssi |
int | WLAN-Signalstärke (dBm), 0 wenn nicht STA-verbunden |
protocolVersionist mit diesen Feldern auf 2 angehoben.
v2.1-Felder (Timer-Kopplung, Sicherheit, Reinigungsassistent):
| Feld | Typ | Bedeutung |
|---|---|---|
steamElapsedMs |
uint | vergangene Dampfbezugszeit in ms (rückwirkende Timer-Kopplung, analog shotElapsedMs) |
flushElapsedMs |
uint | vergangene Wasser-Flush-Zeit in ms (rückwirkende Timer-Kopplung) |
scaleTaring |
bool | Tarier-Phase vor einem Brew-by-Weight-Bezug läuft (HX711); Display zeigt „Tariere…" |
wasserSensorError |
bool | Wassersensor liefert ungültigen Wert |
wasserSafetyShutdown |
bool | Wassertemperatur über Sicherheitslimit → Heizung aus |
dampfSensorError |
bool | Dampfsensor liefert ungültigen Wert |
dampfSafetyShutdown |
bool | Dampftemperatur über Sicherheitslimit → Heizung aus |
cleaningActive |
bool | Reinigungsassistent läuft |
cleaningWaiting |
bool | Reinigungsassistent heizt noch auf 93 °C vor |
cleaningBrewPhase |
bool | aktuelle Phase: true = Bezug, false = Pause |
cleaningCycle |
uint | aktueller Zyklus |
cleaningCycles |
uint | Gesamtzahl der Zyklen |
cleaningBrewSeconds |
uint | Bezugsdauer je Zyklus (s) |
cleaningPauseSeconds |
uint | Pausendauer je Zyklus (s) |
cleaningPhaseRemainSec |
int | verbleibende Sekunden der aktuellen Phase |
Force-Push zusätzlich bei: Dampfkreis start/stop (deckt Dampfbezug und Dampf-Flush ab), Tarier-Phase start/stop, Sensorfehler/Sicherheitsabschaltung (Flanke), Reinigungsassistent start/stop, Standby-Wechsel. Burst (250 ms) zusätzlich während: Dampfkreis, Tarier-Phase, Reinigungsassistent.
6. Kommando-Referenz (Protokoll v1, IST-Zustand)
Verarbeitet in touchUartHandleCommandLine(). Alle Kommandos: {"op":"...","id":<n>,...}.
Antwort jeweils ack/error; bei zustandsändernden Ops folgt automatisch ein state-Push.
op |
Zusatzfelder | Wirkung | Antwort |
|---|---|---|---|
ping |
— | Heartbeat | ack "pong" |
getState |
— | erzwingt sofortigen State-Push | state + ack |
listProfiles |
— | Profilnamen anfordern | profiles + ack |
getUsageStats |
— | Nutzungsstatistik anfordern | usageStats + ack |
action |
action (string), value (opt.) |
ruft executeDashboardAction() auf — dieselbe Aktions-Engine wie das Web-Dashboard |
ack + state |
loadProfile |
profile (string) |
Profil laden | ack + state |
saveProfile |
profile (string) |
aktuelle Einstellungen als Profil speichern | ack (+ state) |
saveBrew |
bbtEnabled,bbtSecs,bbwEnabled,bbwTarget,bbwOffset,piEnabled,piDurSecs,piPauseSecs,sbtEnabled,sbtSecs (alle optional) |
applyBrewControlUpdate() |
ack + state |
saveService |
piezoEnabled,maintenanceInterval,flushDurationSeconds,steamFlushDurationSeconds,resetMaintenanceCounter |
applyServiceSettingsUpdate() |
ack + state |
saveSensor |
section (case/scale/xswitch) + jeweilige Felder (caseSensorEnabled, scaleEnabled, scaleType, hx711CalFactor, xSwitchAction, …) |
applySensorSettingsUpdate() |
ack + state |
startCleaning |
cycles,brewSeconds,pauseSeconds (alle optional, sonst aktuelle Werte) |
setzt die Parameter und startet den Reinigungsassistenten (Prüfung via canStartCleaningAssistant(); bei Ablehnung ok:false mit Begründung) |
ack + state |
stopCleaning |
— | bricht den laufenden Reinigungsassistenten ab | ack + state |
Antwort-Envelopes
// ack
{"type":"ack","id":42,"ok":true,"message":"Brew gespeichert"}
// error
{"type":"error","id":42,"message":"op fehlt"}
// profiles
{"type":"profiles","id":7,"profiles":["Espresso","Lungo","Reinigung"]}
Die wichtigste Erweiterungsstelle: action
executeDashboardAction(action, value, success, message, settingsChanged, pidNeedsUpdate)
ist die gemeinsame Aktions-Engine von Web-Dashboard und Touch-Display. Jede Aktion, die
das Webinterface auslöst (Standby, Eco, Licht, Flush, Bezug-Steuerung, Setpoints,
Reinigungsassistent, …), ist damit ohne Mehraufwand auch übers Display verfügbar.
6.1 Vollständige action-Referenz
Alle gültigen action-Strings aus executeDashboardAction() (Stand v4.8.1, Zeilen
~13399–13863). Aufruf vom Display als {"op":"action","id":<n>,"action":"<name>","value":"<v>"}.
value ist immer ein String; die Engine konvertiert (toFloat/toInt, bzw. Vergleich
== "true").
Temperatur-Sollwerte
action |
value |
Wirkung |
|---|---|---|
setTempW |
float 0..135 |
Wasser-Sollwert (°C), persistiert in EEPROM |
setTempD |
float 0..200 |
Dampf-Sollwert (°C), persistiert in EEPROM |
setTempDisplayMode |
int (CLASSIC..RAILS) |
Temperatur-Anzeigemodus im Dashboard |
Betriebsmodi
action |
value |
Wirkung |
|---|---|---|
startEco |
— | Eco-Modus aktivieren |
stopEco |
— | Eco-Modus für diesen Zyklus beenden |
activateStandby |
— | Standby aktivieren (Software) |
deactivateStandby |
— | Standby aufheben (Software) |
activateStandbyLegacy |
— | Standby-Override (schalterbasiert) reaktivieren |
deactivateStandbyLegacy |
— | Standby per WebUI aufheben (nur bei aktivem Schalter) |
toggleMaintenance |
"true"/"false" |
Wartungsmodus ein/aus (temporär) |
Bezug / Dampf / Spülen
action |
value |
Wirkung |
|---|---|---|
startShot |
— | Bezug starten (Software) |
stopShot |
— | Bezug beenden |
startSteam |
— | Dampfbezug starten |
stopSteam |
— | Dampfbezug / Dampf-Flush beenden |
flush |
— | Spülen starten (Dauer = flushDurationSeconds) |
steamFlush |
— | Dampf-Flush starten (Dauer = steamFlushDurationSeconds) |
overrideSteamDelay |
— | Dampfverzögerung temporär überspringen |
disableSteamHeat |
— | Dampf-Heizung abschalten |
enableSteamHeat |
— | Dampf-Heizung wieder aktivieren |
Persistente Toggles
action |
value |
Wirkung |
|---|---|---|
toggleBoostW |
"true"/"false" |
Boost Wasser (EEPROM) |
toggleBoostD |
"true"/"false" |
Boost Dampf (EEPROM) |
togglePI |
"true"/"false" |
Pre-Infusion (EEPROM) |
toggleBBT |
"true"/"false" |
Brew-by-Time (EEPROM) |
toggleBBW |
"true"/"false" |
Brew-by-Weight (EEPROM) |
toggleLight |
"true"/"false" |
Beleuchtung (SSR + EEPROM) |
Waage
action |
value |
Wirkung |
|---|---|---|
tareScale |
— | Tarieren (nur I2C/HX711; ESP-NOW nicht über UI) |
toggleScaleMode |
"true"/"false" |
Waage-Anzeige-/Wiegemodus (temporär) |
Profil
action |
value |
Wirkung |
|---|---|---|
loadProfile |
Profilname | Profil laden (identisch zu op:"loadProfile") |
Sperre bei Reinigungsassistent: Solange
cleaningAssistantActive, weisenstartEco,toggleMaintenance,startShot,stopShot,startSteam,stopSteam,flush,steamFlushmitsuccess:falseund entsprechender Meldung ab. Das Display sollte diese Aktionen dann ausgegraut darstellen (Statusmaintenance).
Unbekannte Aktion: liefert
message:"Unbekannte Aktion."undsuccess:false.
7. Status-Schlüssel (statusKey)
Aus getTouchUartStatus(). Für sprach-unabhängige UI-Logik/Icons auf dem P4 verwenden
(statt statusText zu parsen):
ready, error, standby, maintenance, tuning, brewing, eco, fastheatup,
heating, ready_eco_pending.
8. Geplante Erweiterungen — Protokoll v2
Für ein vollwertiges 480×800-Touch-UI fehlen v1 noch einige Daten/Funktionen. Geplant:
8.1 Neue Telemetrie-Felder im state
- [erledigt A.1] Netzwerk:
wifiConnected,apMode,ssid,ip,rssi. - [erledigt A.1] PID: aktuelle Kp/Ki/Kd Wasser & Dampf (read-only).
- [erledigt A.1] AutoTune:
autoTuneW/autoTuneD(laufend ja/nein). - [erledigt] AutoTune-Ergebnis:
autoTuneWStatus/autoTuneDStatus(Erfolg/Abbruch + Grund; siehe §8.1-Tabelle). P4-Dashboard blendet das Ergebnis nach Lauf-Ende ~30 s als Banner ein; S3-Web/PID-Tuningzeigt es persistent. - [erledigt A.1] Heizleistung: Duty Wasser & Dampf (
dutyW/dutyD, für Balkenanzeige). - [offen] Flow-Rate:
flowRate(g/s) als Live-Zahl (kein Chart) — bei Bedarf später. - [offen] AutoTune-Fortschritt: numerischer Fortschritt (Peak-Count) — derzeit nur Lauf-Flag + Ergebnis-/Abbruchstatus.
Kein Chart am Display (Entscheidung): Es wird keine Verlaufs-/Historiendaten- Übertragung benötigt. Damit entfällt der aufwändigste Teil (großer Ringpuffer, Chunking).
8.2 Neue Kommandos (op)
op |
Status | Zweck |
|---|---|---|
hello |
erledigt | Handshake: P4 meldet sich, S3 antwortet mit type:"hello" + firmwareVersion, protocolVersion, capabilities[], danach state + ack. |
getProfileDetails |
erledigt | Profil-Inhalt (profile) → Antwort type:"profileDetails" mit allen Feldern (Setpoints, Offsets, Kp/Ki/Kd, Boost, Window, Eco, Brew, Piezo, FastHeatUp). |
setPid |
erledigt | Felder kpW/kiW/kdW/kpD/kiD/kdD (alle optional) → EEPROM + Live-SetTunings. |
startAutotune |
erledigt | target:water|steam (auch wasser/dampf). Spiegelt handleAutoTune* (Guard: nur wenn keins läuft, Wartungsmodus wird zurückgesetzt). |
stopAutotune |
erledigt | Bricht laufendes AutoTune Wasser und Dampf ab. |
scanWifi |
erledigt | Startet einen asynchronen WLAN-Scan. Sofort ack; wenn fertig, pusht die S3 type:"wifiNetworks" (Array {ssid,rssi,enc}, max. 20). Timeout 15 s. |
setWifi |
erledigt | Felder ssid (Pflicht) + password (optional, offenes Netz = leer). Speichert in WiFiConfig (EEPROM) und ruft WiFi.begin(...) nicht-blockierend auf; vorhandene IP-Einstellungen bleiben erhalten. Fortschritt über die wifi*-Felder im state. |
getUsageStats |
erledigt | Nutzungsstatistik anfordern → Antwort type:"usageStats". Aggregierte Kennzahlen aus calculateShotStatistics() (today/week/month, yesterday/lastWeek/lastMonth, total, avgDur, avgPerDay) plus shots[] = letzte ~12 Einzelbezüge (dt, d=Dauer s, w=Gewicht g bzw. -1). Es wird nur das Dateiende von /Nutzungsstatistik.csv gelesen, nie die ganze Datei (P4-Heap schonen). |
getNetworkInfo |
entfällt | WLAN-Details sind bereits im state enthalten — eigener Op unnötig. |
Antwort auf
scanWifi(Beispiel, eigene Push-Nachricht sobald der Scan fertig ist):{"type":"wifiNetworks","networks":[ {"ssid":"MeinWLAN","rssi":-52,"enc":true}, {"ssid":"Gast","rssi":-71,"enc":false} ]}
Antwort auf
getUsageStats(Beispiel):{"type":"usageStats","id":7,"available":true,"total":340, "today":3,"week":18,"month":72,"yesterday":5,"lastWeek":21,"lastMonth":80, "avgDur":27.4,"avgPerDay":4.9, "shots":[ {"dt":"02.07. 08:14","d":27.5,"w":36.5}, {"dt":"02.07. 07:41","d":25.0,"w":-1} ]}
Antwort auf
hello(Beispiel):{"type":"hello","protocolVersion":2,"firmwareVersion":"4.8.1", "capabilities":["state","action","listProfiles","loadProfile","saveProfile","saveBrew","saveService","saveSensor","setPid","startAutotune","stopAutotune","getProfileDetails","scanWifi","setWifi","getUsageStats"]}Anzeige am P4: eigener Menüpunkt „Statistik" (
ui.cpp,PG_STATS). Beim Öffnen der Seite (und über einen „Aktualisieren"-Button) sendet der P4getUsageStatsund stellt die Aggregate (Heute/Woche/Monat inkl. Vorperiode, Gesamt, mittlere Dauer, mittl. Bezüge/Tag) sowie die letzten ~12 Einzelbezüge dar.
8.3 Robustheit / Transport
- Handshake + Capabilities statt impliziter Aktivierung -> P4 kann sich an die tatsächliche Firmware-Version anpassen (Vorwärts-/Rückwärtskompatibilität).
- Optionale Prüfsumme (z. B. CRC16 als Feld
crc) gegen SSR-/EMV-Störungen auf der Maschine. Empfänger verwirft Zeilen mit falscher Prüfsumme. - Puffergrößen: Ohne Chart bleiben die Nutzdaten klein.
getProfileDetails(einTemperatureProfile) passt voraussichtlich in die bestehenden Limits (Zeile 1024 B, State 2400 B). Chunking ist damit nicht erforderlich; Limit nur bei Bedarf leicht anheben.
8.4 Versionierung
protocolVersionwird auf 2 angehoben, sobald ein nicht abwärtskompatibles Feld Pflicht wird. Reine Ergänzungen (neue optionale Felder/Ops) bleiben v1-kompatibel — der P4 ignoriert Unbekanntes.- Der P4 prüft beim
hellodieprotocolVersionder S3 und schaltet Features entsprechend frei/aus.
9. OLED-Koexistenz (Compile-Flag)
- OLED bleibt über
ENABLE_DISPLAY(Compile-Flag) wählbar — unverändert. - Das JC-Display wird über
TOUCH_UART_ENABLED(Compile-Flag) aktiviert. - Geplante Änderung: beide Flags strikt unabhängig halten, damit alle Kombinationen baubar sind:
ENABLE_DISPLAY |
TOUCH_UART_ENABLED |
Ergebnis |
|---|---|---|
| 0 | 0 | weder OLED noch Touch (Headless, nur Web) |
| 1 | 0 | nur OLED (heutiger Standard) |
| 0 | 1 | nur JC-Touch-Display |
| 1 | 1 | OLED und JC-Touch parallel |
Aktuell ist sicherzustellen, dass kein UART-Code versehentlich von
ENABLE_DISPLAYabhängt und keine I2C-/Pin-Ressource doppelt belegt wird (OLED nutzt I2C GPIO17/18, Touch nutzt UART GPIO43/44 — kein Konflikt).
10. Sprachen / UI-Texte
- Die App ist aktuell reines Deutsch — es existiert keine i18n-/Sprachumschalt-
Infrastruktur (geprüft: S3-Firmware und Web-Skripte haben nur fest verdrahtetes Deutsch,
HTML durchgängig
lang="de"). Damit ist keingetStrings/setLanguagenötig. - Der P4 hält seine deutschen UI-Texte lokal (statische Strings im Display-Projekt).
statusKey(sprachneutral, §7) bleibt trotzdem die saubere Grundlage für die UI-Logik (Icons/Zustände), unabhängig vom angezeigtenstatusText.- Projekt-Hinweis: Sollte später eine zweite Anwendungssprache eingeführt werden, ist sie laut Projektregel überall zu ergänzen — dann auch in den P4-Stringtabellen.
11. Bewertung des Bestands (Keep / Change / Delete)
Behalten (1:1): Protokoll-Framing, Push-Engine (touchUartTick), RX-Parser
(touchUartRxTick), Kommando-Dispatch (touchUartHandleCommandLine), JSON-Helfer
(jsonGetStringValue/UInt/Bool/Float/RawValue, jsonDecodeString, jsonEscapeForUart),
ACK/Error-Envelopes, Reset-Checkpoints (RESET_CP_TOUCH_UART_RX/TX).
Ändern/Erweitern: Flag-Entkopplung (§9), Telemetrie-/Op-Erweiterungen (§8),
Puffergrößen/Chunking, optionaler CRC + Handshake, action-Stringliste dokumentieren.
Löschen: Nichts.
Fully-Kiosk bleibt erhalten (Entscheidung) — als eigenständige, optionale Funktion neben dem JC-Display. Lastbewertung der S3:
- Einziger Aufruf
sendFullyKioskLoadUrl()wird nur bei Standby-Zustandswechsel getriggert (if (standbyModeActive != lastStandbyModeActive)), nicht periodisch (wenige Male pro Tag). - Blockierender HTTP-GET, aber mit kurzem Timeout (
timeoutMs, Default 700 ms); beienabled=0sofortiger Rückkehr ohne Netzwerkzugriff. - Übergänge passieren im Standby, also außerhalb eines Bezugs -> kein Einfluss auf die Regelung. Keine relevante Dauerlast, keine Leistungsgrenze. Koexistiert problemlos mit dem UART-Display.
12. Implementierungsplan
Phase A — S3-Seite (Hauptplatine, Dual_PID_FastHeatUp.ino)
- [erledigt]
action-Stringliste ausexecuteDashboardAction()dokumentiert (§6.1). - [erledigt A.1] Flag-Entkopplung von
ENABLE_DISPLAYdokumentiert/abgesichert (§9); Defines kommentiert,TOUCH_UART_PROTOCOL_VERSION-> 2, Puffer -> 3000. - [erledigt A.1] v2-Telemetriefelder ergänzt: Netzwerk, PID, Duty, AutoTune-Status (§8.1).
- [erledigt A.1] Op
helloimplementiert (§8.2). - [erledigt] Weitere Ops:
getProfileDetails,setPid,startAutotune/stopAutotune(§8.2). - [offen, optional] CRC + ggf. Puffer-Feinjustage (§8.3) — bewusst nicht als toter, ungetesteter Code eingebaut; bei Bedarf später.
- [offen, durch dich]
TOUCH_UART_ENABLEDauf 1 setzen + Build verifizieren, sobald das JC-Display physisch an Serial1 (GPIO43/44) hängt. Bewusst noch 0, damit die serielle Debug-Konsole (GPIO43/44) im aktuellen Betrieb erhalten bleibt.
Phase B — P4-Seite (JC-Display, Projekt JC_Display_Firmware/)
Grundgerüst angelegt (Arduino-ESP32 P4 + LVGL 9 + ArduinoJson 7). Aufteilung:
config.h, machine_state.h, protocol_client.*, ui.*, display_hal.*, .ino, README.md.
- [erledigt] UART-Client: Zeilen-RX/TX, JSON-Parsing,
state-> UI-Modell,hello-Handshake, Heartbeat, Link-Timeout (protocol_client.*). - [erledigt] Dashboard: Temps/Soll/Duty, Status-Banner (nach
statusKey), Verbindungs-/WLAN-Anzeige, Bezugs-Timer, Bedien-Buttons -> Kommandos (ui.*). - [erledigt] Senderoutinen für
setPid,startAutotune/stopAutotune,getProfileDetailsim Client vorhanden. - [erledigt] Board-Bringup auf Basis der Hersteller-Portierung verdrahtet
(
display_hal.cpp, SchalterJC_USE_REAL_PANEL): MIPI-DSI/ST7701, GT911 (I²C SDA=7/SCL=8), Backlight GPIO23, LCD-Reset GPIO5; LVGL-Port mit eigenem Task + Lock. Stub-Modus bleibt für Schreibtisch-Tests. Vendor-BSP-Dateien sind in den Sketch zu kopieren (siehe Projekt-README). - [festgelegt] UART-Pins am JC-Display: P4 GPIO50 (TX) / GPIO51 (RX) vom Expand-IO-Header
JP1 (Schaltplan
4_USB&IO.png). Gegenseite S3 unverändert GPIO43/44. - [offen] Weitere Screens: Einstellungen (Brew/Service/Sensor), Profil-Auswahl/-Editor,
PID/AutoTune-Bedienung; Ausrichtung Hochformat (Rotation 0 in
pins_config.h).
Entschieden
- Sprachen: App ist Deutsch-only, keine i18n -> P4 hält deutsche Strings lokal (§10).
- Chart am Display: nicht benötigt -> kein
getChartData/Chunking (§8). - Fully-Kiosk: bleibt als optionale Funktion erhalten; keine relevante S3-Last (§11).
Letzter Abgleich mit Code: Dual_PID_FastHeatUp.ino v4.8.1 — Abschnitt „Dashboard Code
Block" (buildTouchUartStateJson, touchUartHandleCommandLine, touchUartTick,
touchUartRxTick) sowie Defines TOUCH_UART_* (Zeilen ~155–184).