feat(S3): Display-Auswahl über die Web-UI statt Compile-Schalter — Default „Ohne Display" #6

Merged
thomas merged 1 commits from feat/display-auswahl-über-web-ui into main 2026-07-13 01:56:25 +02:00
2 changed files with 225 additions and 82 deletions
+9
View File
@@ -1,3 +1,12 @@
Version 5.1.0:
- Display-Auswahl in die Web-UI (/Sensoren) verlegt: Ohne Display / OLED (SH1106, I2C) / UART-Touch-Display.
Der Compile-Schalter ENABLE_DISPLAY entfällt, die Einstellung wird im EEPROM gespeichert und ohne Neustart
übernommen. Default ist "Ohne Display", damit die Steuerung ohne angeschlossene Anzeige immer sicher startet.
- Boot-Schutz: Vor der OLED-Initialisierung wird per I2C-Probe (0x3C) geprüft, ob das Display wirklich
angeschlossen ist. Ohne Antwort bleibt die Anzeige deaktiviert und die Steuerung bootet normal weiter -
eine falsch gesetzte Display-Einstellung kann die Maschine damit nicht mehr blockieren.
- UART-Touch-Anbindung (Serial1) wird nur noch initialisiert und bedient, wenn "UART-Touch-Display" gewählt ist.
Version 5.0.12:
- Neues additives Feld "piState" im State-JSON für das UART-Touch-Display (0=inaktiv, 1=Pre-Infusion,
2=Pause, 3=Hauptbezug): Die Pre-Infusion-Phasenleiste auf dem Display folgt damit quelltreu der echten
+216 -82
View File
@@ -52,8 +52,17 @@
// Standard-Waagen-Typ (kann in /Sensoren geändert werden)
#define DEFAULT_SCALE_TYPE SCALE_ESPNOW
// Nachfolgende Zeile auskommentieren, falls die Steuerung ohne OLED-Display verwendet wird!
//#define ENABLE_DISPLAY
/************************************************************************************
* DISPLAY-KONFIGURATION
* Der Display-Typ wird zur Laufzeit in der Web-UI (/Sensoren) gewaehlt und im
* EEPROM gespeichert. Default ist "Ohne Display", damit die Steuerung ohne
* angeschlossene Anzeige immer sicher startet.
************************************************************************************/
#define DISPLAY_TYPE_NONE 0 // Ohne Display
#define DISPLAY_TYPE_OLED 1 // OLED (SH1106, I2C)
#define DISPLAY_TYPE_TOUCH_UART 2 // JC-UART-Touch-Display (Serial1)
#define DEFAULT_DISPLAY_TYPE DISPLAY_TYPE_NONE
#define OLED_I2C_ADDRESS 0x3C
/************************************************************************************
* Vorwärtsdeklarationen benutzerdefinierter Strukturen
@@ -80,6 +89,9 @@ bool executeDashboardAction(const String& action, const String& value,
bool& settingsChanged, bool& pidNeedsUpdate);
void touchUartRxTick();
void touchUartTick();
void initTouchUart();
bool initOledDisplay(bool showSplash);
void applyDisplayTypeChange(uint8_t newType);
void applyLightOutput();
void initResetDiagnostics();
void recordResetCheckpoint(uint16_t checkpoint);
@@ -91,10 +103,10 @@ void syncFullyKioskWithStandby(bool active);
************************************************************************************/
#include <Wire.h>
#ifdef ENABLE_DISPLAY
// OLED-Bibliotheken werden immer einkompiliert; ob das Display genutzt wird,
// entscheidet zur Laufzeit die Einstellung displayType (Web-UI /Sensoren).
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#endif // ENABLE_DISPLAY
#include <EEPROM.h>
#include <PID_v1.h>
@@ -154,9 +166,9 @@ const unsigned long DASHBOARD_WS_SHOT_END_GRACE_MS = 500;
#define DASHBOARD_JSON_BUFFER_SIZE 2400
unsigned long lastDashboardWsPushMs = 0;
// JC-Touch-Display (UART). Bewusst UNABHAENGIG von ENABLE_DISPLAY (OLED), damit alle
// Kombinationen baubar sind (nur OLED / nur JC / beide / keines). Auf 1 setzen, sobald
// das JC-Display an Serial1 (GPIO43/44) angeschlossen ist.
// JC-Touch-Display (UART). Ob die UART-Anbindung genutzt wird, entscheidet zur
// Laufzeit displayType (Web-UI /Sensoren); dieser Compile-Schalter bleibt als
// Notausstieg, um den Code komplett herauszubauen.
#define TOUCH_UART_ENABLED 1
#define TOUCH_UART_TX_PIN 43
#define TOUCH_UART_RX_PIN 44
@@ -179,6 +191,13 @@ const unsigned long TOUCH_UART_SHOT_END_GRACE_MS = 500;
const unsigned long TOUCH_UART_CLIENT_TIMEOUT_MS = 15000;
unsigned long lastTouchUartPushMs = 0;
// Aktiver Display-Typ (DISPLAY_TYPE_*). Wird beim Start aus dem EEPROM geladen
// und kann in der Web-UI (/Sensoren) zur Laufzeit umgestellt werden.
uint8_t displayType = DEFAULT_DISPLAY_TYPE;
// true, sobald das OLED per I2C gefunden UND initialisiert wurde. Schuetzt davor,
// dass Renderaufrufe ohne angeschlossenes Display laufen (Boot-Haenger-Schutz).
bool oledDisplayPresent = false;
HardwareSerial& touchUart = Serial1;
String touchUartRxLine;
bool touchUartRxOverflow = false;
@@ -342,19 +361,15 @@ MAX6675 thermocouple(MAX6675_SCK, MAX6675_CS, MAX6675_SO);
/************************************************************************************
* Display-Objekt (Adafruit SH1106G)
************************************************************************************/
#ifdef ENABLE_DISPLAY
/************************************************************************************
* Display-Objekt (Adafruit SH1106G)
************************************************************************************/
// Dieser Block muss vorhanden sein, wenn Display aktiviert!
// Objekt existiert immer; initialisiert wird es nur bei displayType == DISPLAY_TYPE_OLED
// und erfolgreicher I2C-Probe (siehe initOledDisplay()).
Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
#endif // ENABLE_DISPLAY
/************************************************************************************
* Firmware-Informationen
************************************************************************************/
String version = "5.0.12";
String version = "5.1.0";
String versionHersteller = "Thomas M&uuml;ller";
String versionHerstellerMail =
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
@@ -926,7 +941,8 @@ const int EEPROM_ADDR_HEATUP_MINUTES = 904; // 4 Bytes (int) -> Ende
const int EEPROM_ADDR_BACKLIGHT_ACTIVE_PERCENT = 908; // 1 Byte (uint8_t) -> Ende 908
const int EEPROM_ADDR_BACKLIGHT_STANDBY_CLOCK_PERCENT = 909; // 1 Byte (uint8_t) -> Ende 909
const int EEPROM_ADDR_POWERON_STANDBY = 910; // 1 Byte (bool) -> Ende 910
// Naechste freie Adresse: 911 (Innerhalb EEPROM_SIZE=1024)
const int EEPROM_ADDR_DISPLAY_TYPE = 911; // 1 Byte (uint8_t) -> Ende 911
// Naechste freie Adresse: 912 (Innerhalb EEPROM_SIZE=1024)
/************************************************************************************
* Eco-Mode Variablen
@@ -1299,12 +1315,10 @@ String standbyTimerStatusMessage = ""; // fuer Nachrichten auf der Timer-Seite
/************************************************************************************
* WiFi Signal Bitmap (Display-Anzeige)
************************************************************************************/
#ifdef ENABLE_DISPLAY
static const unsigned char PROGMEM wifiSymbol[] = {
0x1f, 0xc0, 0x20, 0x20, 0x4f, 0x90, 0x90, 0x48,
0x27, 0x20, 0x08, 0x80, 0x02, 0x00
};
#endif // ENABLE_DISPLAY
/************************************************************************************
* Gemeinsame CSS-Styles (PROGMEM) - Unverändert
@@ -2451,12 +2465,10 @@ void checkWifiConnection() {
}
}
#ifdef ENABLE_DISPLAY
// WiFi-Symbol aufs Display zeichnen
// WiFi-Symbol aufs Display zeichnen
void drawwifiSymbol(int16_t x, int16_t y) {
display.drawBitmap(x, y, wifiSymbol, 13, 7, SH110X_WHITE);
}
#endif // ENABLE_DISPLAY
/************************************************************************************
* Handler zum Rücksetzen der Betriebszeit - URL angepasst
@@ -2896,6 +2908,7 @@ void resetToDefaults() {
EEPROM.put(EEPROM_ADDR_BACKLIGHT_ACTIVE_PERCENT, defaultBacklightActivePercent);
EEPROM.put(EEPROM_ADDR_BACKLIGHT_STANDBY_CLOCK_PERCENT, defaultBacklightStandbyClockPercent);
EEPROM.put(EEPROM_ADDR_POWERON_STANDBY, (uint8_t)(defaultPowerOnStandby ? 1 : 0));
EEPROM.put(EEPROM_ADDR_DISPLAY_TYPE, (uint8_t)DEFAULT_DISPLAY_TYPE);
EEPROM.put(EEPROM_ADDR_OFFSET_COMPENSATION_ENABLED, (uint8_t)0);
EEPROM.put(EEPROM_ADDR_SCALE_ENABLED, (uint8_t)(defaultScaleEnabled ? 1 : 0));
EEPROM.put(EEPROM_ADDR_SCALE_TYPE, defaultScaleType);
@@ -2960,6 +2973,7 @@ void resetToDefaults() {
xSwitchLongAction = defaultXSwitchLongAction;
buttonLongPressMs = (uint16_t)DEFAULT_BUTTON_LONG_PRESS_MS;
dashboardTempDisplayMode = defaultDashboardTempDisplayMode;
applyDisplayTypeChange(DEFAULT_DISPLAY_TYPE); // deaktiviert ein ggf. aktives Display sauber
xSwitchActive = false;
ecoForcedActive = false;
standbyOverriddenByWeb = false;
@@ -3866,7 +3880,7 @@ void stopBrewSequence(unsigned long durationMillis, bool manualStop = false, boo
/************************************************************************************
* setup(): Initialisierung des Systems
* - Angepasst für optionale Display-Unterstützung via ENABLE_DISPLAY
* - Optionale Display-Unterstützung via Laufzeit-Einstellung displayType (Web-UI)
* - Korrekte Wire.begin() Logik für Display/Waage
* - Zusätzliche Serial-Ausgaben für Headless-Betrieb
************************************************************************************/
@@ -3995,15 +4009,6 @@ void setup() {
enterRescueMode(); // kehrt nie zurueck
}
#if TOUCH_UART_ENABLED
// TX-Puffer >= max. Zeilenlaenge (State-JSON bis TOUCH_UART_JSON_BUFFER_SIZE=3000
// + "\r\n"), damit println() die Hauptschleife NIE blockiert (sonst bis ~41 ms
// Stall -> PID-Jitter). Headroom deckt zudem OTA-Chunks (~1,4 KB) und gestaute
// Nachrichten (ack direkt vor State-Push) ab.
touchUart.setTxBufferSize(4096); // war 2048; nicht-blockierendes Senden bis 3000-Byte-Zeile
touchUart.begin(TOUCH_UART_BAUDRATE, SERIAL_8N1, TOUCH_UART_RX_PIN, TOUCH_UART_TX_PIN);
touchUartRxLine.reserve(TOUCH_UART_MAX_LINE_LEN);
#endif
EEPROM.begin(EEPROM_SIZE); // EEPROM initialisieren
loadFullyKioskConfig(fullyKioskConfig);
@@ -4253,6 +4258,12 @@ void setup() {
storedBacklightStandbyClock = defaultBacklightStandbyClockPercent;
}
backlightStandbyClockPercent = storedBacklightStandbyClock;
uint8_t storedDisplayType = DEFAULT_DISPLAY_TYPE;
EEPROM.get(EEPROM_ADDR_DISPLAY_TYPE, storedDisplayType);
if (storedDisplayType > DISPLAY_TYPE_TOUCH_UART) {
storedDisplayType = DEFAULT_DISPLAY_TYPE; // u. a. unprogrammierte 0xFF
}
displayType = storedDisplayType;
uint8_t storedScaleEnabled = 0;
uint8_t storedScaleType = defaultScaleType;
EEPROM.get(EEPROM_ADDR_SCALE_ENABLED, storedScaleEnabled);
@@ -4356,11 +4367,6 @@ void setup() {
// Serial.println("---------------------------------------------");
// Serial.print("Firmware Version: "); Serial.println(version);
// #ifdef ENABLE_DISPLAY
// Serial.println("Display: Aktiviert");
// #else
// Serial.println("Display: Deaktiviert");
// #endif
// if (WiFi.getMode() == WIFI_AP || WiFi.status() != WL_CONNECTED) {
// Serial.println(F("WiFi Modus: Access Point (AP)"));
// Serial.print(F("SSID: ")); Serial.println(WiFi.softAPSSID());
@@ -4393,51 +4399,13 @@ void setup() {
}
// --- Display-Initialisierung (nur wenn aktiviert) ---
#ifdef ENABLE_DISPLAY
// --- Display-Initialisierung gemaess Web-UI-Einstellung (displayType) ---
recordResetCheckpoint(RESET_CP_SETUP_DISPLAY);
if (!display.begin(0x3C, true)) { // display.begin benutzt das ggf. schon initialisierte Wire
Serial.println(F("Display konnte nicht initialisiert werden!"));
} else {
Serial.println(F("Display initialisiert."));
// Display-Ausgabe am Start (1)
display.clearDisplay();
display.setCursor(0, 0);
display.setTextColor(SH110X_WHITE);
display.setTextSize(1);
display.println(infoHersteller);
display.println(infoModell);
display.println(infoZusatz);
display.println("");
display.println(F("Dual-PID"));
display.print(F("Version: "));
display.println(version);
display.println(F("von Thomas M\201ller")); // \201 ist ü für manche Displays
display.display();
delay(delayInit1); // Verzögerung nur bei aktiviertem Display
// Display-Ausgabe am Start (2) - WiFi Info
display.clearDisplay();
display.setCursor(0, 0);
display.setTextColor(SH110X_WHITE);
display.setTextSize(1);
display.println(infoHersteller);
display.println(infoModell);
display.println(infoZusatz);
display.println("");
if (WiFi.status() != WL_CONNECTED) {
display.println(F("WiFi: AP-Modus"));
display.print(F("SSID: ")); display.println(WiFi.softAPSSID());
display.print(F("IP: ")); display.println(WiFi.softAPIP());
} else {
display.println(F("Webserver gestartet"));
display.print(F("IP: ")); display.println(WiFi.localIP());
display.print(F("Host: ")); display.print(hostname); display.println(".local");
}
display.display();
delay(delayInit2); // Verzögerung nur bei aktiviertem Display
if (displayType == DISPLAY_TYPE_OLED) {
initOledDisplay(true);
} else if (displayType == DISPLAY_TYPE_TOUCH_UART) {
initTouchUart();
}
#endif // ENABLE_DISPLAY
// --- Waage Initialisierung (nur ESP32) ---
@@ -5177,10 +5145,10 @@ void loop() {
applyLightOutput();
// Display regelmaessig updaten, wenn aktiviert / angeschlossen
#ifdef ENABLE_DISPLAY
if (displayType == DISPLAY_TYPE_OLED && oledDisplayPresent) {
recordResetCheckpoint(RESET_CP_LOOP_DISPLAY);
updateDisplay();
#endif // ENABLE_DISPLAY
}
// --- Waagen-spezifische Logik in der Hauptschleife ---
recordResetCheckpoint(RESET_CP_LOOP_SCALE);
@@ -5350,8 +5318,108 @@ double readCaseTemperature() {
return readNTCTemperatureFromPin(ANALOG_CASE_NTC_PIN);
}
#ifdef ENABLE_DISPLAY
/************************************************************************************
* OLED-Display initialisieren (Laufzeit, mit I2C-Probe als Boot-Schutz)
* Liefert true, wenn das Display gefunden und initialisiert wurde. Ohne ACK auf
* OLED_I2C_ADDRESS bleibt die Anzeige deaktiviert und die Steuerung startet normal.
************************************************************************************/
bool initOledDisplay(bool showSplash) {
oledDisplayPresent = false;
// I2C-Probe: haengt kein Display am Bus, wird der Init uebersprungen statt zu blockieren.
Wire.beginTransmission(OLED_I2C_ADDRESS);
if (Wire.endTransmission() != 0) {
Serial.println(F("OLED-Display nicht gefunden (I2C 0x3C) - Anzeige deaktiviert."));
return false;
}
if (!display.begin(OLED_I2C_ADDRESS, true)) { // display.begin benutzt das ggf. schon initialisierte Wire
Serial.println(F("Display konnte nicht initialisiert werden!"));
return false;
}
oledDisplayPresent = true;
Serial.println(F("Display initialisiert."));
if (showSplash) {
// Display-Ausgabe am Start (1)
display.clearDisplay();
display.setCursor(0, 0);
display.setTextColor(SH110X_WHITE);
display.setTextSize(1);
display.println(infoHersteller);
display.println(infoModell);
display.println(infoZusatz);
display.println("");
display.println(F("Dual-PID"));
display.print(F("Version: "));
display.println(version);
display.println(F("von Thomas M\201ller")); // \201 ist ü für manche Displays
display.display();
delay(delayInit1); // Verzögerung nur bei aktiviertem Display
// Display-Ausgabe am Start (2) - WiFi Info
display.clearDisplay();
display.setCursor(0, 0);
display.setTextColor(SH110X_WHITE);
display.setTextSize(1);
display.println(infoHersteller);
display.println(infoModell);
display.println(infoZusatz);
display.println("");
if (WiFi.status() != WL_CONNECTED) {
display.println(F("WiFi: AP-Modus"));
display.print(F("SSID: ")); display.println(WiFi.softAPSSID());
display.print(F("IP: ")); display.println(WiFi.softAPIP());
} else {
display.println(F("Webserver gestartet"));
display.print(F("IP: ")); display.println(WiFi.localIP());
display.print(F("Host: ")); display.print(hostname); display.println(".local");
}
display.display();
delay(delayInit2); // Verzögerung nur bei aktiviertem Display
}
return true;
}
/************************************************************************************
* Display-Typ zur Laufzeit umschalten (Web-UI /Sensoren)
* Deaktiviert das bisherige Display sauber und initialisiert das neue - ganz ohne
* Neustart. Der Splash wird beim Laufzeit-Wechsel uebersprungen (keine delays im
* Web-Handler-Kontext).
************************************************************************************/
void applyDisplayTypeChange(uint8_t newType) {
if (newType > DISPLAY_TYPE_TOUCH_UART) { newType = DEFAULT_DISPLAY_TYPE; }
if (newType == displayType) { return; }
// Bisheriges Display deaktivieren
if (displayType == DISPLAY_TYPE_OLED && oledDisplayPresent) {
display.clearDisplay();
display.display();
oledDisplayPresent = false;
}
if (displayType == DISPLAY_TYPE_TOUCH_UART) {
#if TOUCH_UART_ENABLED
touchUart.end();
#endif
touchUartClientActive = false;
touchUartClientFirmware = "";
touchUartRxLine = "";
touchUartRxOverflow = false;
}
displayType = newType;
if (displayType == DISPLAY_TYPE_OLED) {
initOledDisplay(false);
} else if (displayType == DISPLAY_TYPE_TOUCH_UART) {
initTouchUart();
}
}
void updateDisplay() {
if (!oledDisplayPresent) { return; }
recordResetCheckpoint(RESET_CP_DISPLAY_RENDER);
double displayedWaterTemp = getDisplayedWaterTemperature();
double displayedSteamTemp = getDisplayedSteamTemperature();
@@ -5992,7 +6060,6 @@ if (scaleModeActive) {
yield();
display.display(); // Display *immer* am Ende aktualisieren
}
#endif // ENABLE_DISPLAY
void updateShotTimer() {
if (cleaningAssistantActive) {
@@ -7652,6 +7719,25 @@ static const char sensorsHtmlHeadEnd[] PROGMEM = R"rawliteral(
static const char sensorsBodyStart[] PROGMEM = R"rawliteral(
<body>
<h1>Sensoren</h1>
<form action='/updateSensorSettings' method='POST'>
<input type='hidden' name='sensorSection' value='display'>
<h3>Display</h3>
<label for='displayType'>Typ:</label>
<select id='displayType' name='displayType'>
<option value='0' )rawliteral";
static const char sensorsDisplayOptionMid1[] PROGMEM = R"rawliteral(>Ohne Display</option>
<option value='1' )rawliteral";
static const char sensorsDisplayOptionMid2[] PROGMEM = R"rawliteral(>OLED-Display (SH1106, I2C)</option>
<option value='2' )rawliteral";
static const char sensorsDisplaySectionEnd[] PROGMEM = R"rawliteral(>UART-Touch-Display</option>
</select>
<small class="select-description">Wird sofort &uuml;bernommen, kein Neustart n&ouml;tig. Das OLED wird nur aktiviert, wenn es am I2C-Bus (0x3C) gefunden wird &ndash; ohne angeschlossenes Display startet die Steuerung immer sicher.</small>
<br>
<input type='submit' value='Einstellungen speichern'>
</form>
<form action='/updateSensorSettings' method='POST'>
<input type='hidden' name='sensorSection' value='case'>
<h3>Zusatz-Temperatur-Sensor</h3>
@@ -7902,6 +7988,18 @@ void handleSensors(AsyncWebServerRequest *request) {
response->print(FPSTR(sensorsHtmlHeadEnd));
response->print(FPSTR(commonNav));
response->print(FPSTR(sensorsBodyStart));
if (displayType == DISPLAY_TYPE_NONE) {
response->print(F("selected"));
}
response->print(FPSTR(sensorsDisplayOptionMid1));
if (displayType == DISPLAY_TYPE_OLED) {
response->print(F("selected"));
}
response->print(FPSTR(sensorsDisplayOptionMid2));
if (displayType == DISPLAY_TYPE_TOUCH_UART) {
response->print(F("selected"));
}
response->print(FPSTR(sensorsDisplaySectionEnd));
if (caseSensorEnabled) {
response->print(F(" checked"));
}
@@ -9806,6 +9904,10 @@ struct SensorSettingsUpdate {
uint8_t xSwitchLongAction = 0;
bool hasButtonLongPressMs = false;
uint16_t buttonLongPressMs = 0;
bool updateDisplaySection = false;
bool hasDisplayType = false;
uint8_t displayType = 0;
};
struct SensorSettingsApplyResult {
@@ -9944,6 +10046,16 @@ SensorSettingsApplyResult applySensorSettingsUpdate(const SensorSettingsUpdate&
}
}
if (update.updateDisplaySection && update.hasDisplayType) {
uint8_t newDisplayType = update.displayType;
if (newDisplayType > DISPLAY_TYPE_TOUCH_UART) { newDisplayType = DEFAULT_DISPLAY_TYPE; }
if (newDisplayType != displayType) {
applyDisplayTypeChange(newDisplayType);
EEPROM.put(EEPROM_ADDR_DISPLAY_TYPE, displayType);
result.changed = true;
}
}
if (result.changed) {
EEPROM.commit();
}
@@ -9959,11 +10071,13 @@ void handleUpdateSensorSettings(AsyncWebServerRequest *request) {
bool isCaseSection = (section.length() == 0 || section == "case");
bool isScaleSection = (section.length() == 0 || section == "scale");
bool isXSwitchSection = (section.length() == 0 || section == "xSwitch");
bool isDisplaySection = (section.length() == 0 || section == "display");
SensorSettingsUpdate update;
update.updateCaseSection = isCaseSection;
update.updateScaleSection = isScaleSection;
update.updateXSwitchSection = isXSwitchSection;
update.updateDisplaySection = isDisplaySection;
if (isCaseSection) {
update.hasCaseEnabled = true;
@@ -9989,6 +10103,11 @@ void handleUpdateSensorSettings(AsyncWebServerRequest *request) {
update.hx711DisplaySmoothing = request->hasArg("hx711DisplaySmoothing");
}
if (isDisplaySection) {
update.hasDisplayType = request->hasArg("displayType");
if (update.hasDisplayType) { update.displayType = (uint8_t)request->arg("displayType").toInt(); }
}
if (isXSwitchSection) {
update.hasXSwitchAction = request->hasArg("xSwitchAction");
if (update.hasXSwitchAction) { update.xSwitchAction = (uint8_t)request->arg("xSwitchAction").toInt(); }
@@ -15540,8 +15659,21 @@ static bool jsonGetUIntValue(const String& json, const char* key, uint32_t& outV
return true;
}
void initTouchUart() {
#if TOUCH_UART_ENABLED
// TX-Puffer >= max. Zeilenlaenge (State-JSON bis TOUCH_UART_JSON_BUFFER_SIZE=3000
// + "\r\n"), damit println() die Hauptschleife NIE blockiert (sonst bis ~41 ms
// Stall -> PID-Jitter). Headroom deckt zudem OTA-Chunks (~1,4 KB) und gestaute
// Nachrichten (ack direkt vor State-Push) ab.
touchUart.setTxBufferSize(4096);
touchUart.begin(TOUCH_UART_BAUDRATE, SERIAL_8N1, TOUCH_UART_RX_PIN, TOUCH_UART_TX_PIN);
touchUartRxLine.reserve(TOUCH_UART_MAX_LINE_LEN);
#endif
}
static void touchUartSendLine(const String& line) {
#if TOUCH_UART_ENABLED
if (displayType != DISPLAY_TYPE_TOUCH_UART) { return; }
touchUart.println(line);
#endif
}
@@ -16283,6 +16415,7 @@ static bool touchUartHandleCommandLine(const String& line) {
void touchUartRxTick() {
#if TOUCH_UART_ENABLED
if (displayType != DISPLAY_TYPE_TOUCH_UART) return;
if (displayOtaActive) return; // waehrend OTA hat der Upload-Handler die UART exklusiv
recordResetCheckpoint(RESET_CP_TOUCH_UART_RX);
while (touchUart.available() > 0) {
@@ -16359,6 +16492,7 @@ static void touchUartPollWifiScan() {
void touchUartTick() {
#if TOUCH_UART_ENABLED
if (displayType != DISPLAY_TYPE_TOUCH_UART) return;
if (displayOtaActive) return; // waehrend OTA keine State-Pushes
recordResetCheckpoint(RESET_CP_TOUCH_UART_TX);
unsigned long now = millis();