diff --git a/Changelog.txt b/Changelog.txt index 60fa24c..c177b4c 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -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 diff --git a/Dual-PID.ino b/Dual-PID.ino index a7243e0..3df341b 100644 --- a/Dual-PID.ino +++ b/Dual-PID.ino @@ -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 -#ifdef ENABLE_DISPLAY +// OLED-Bibliotheken werden immer einkompiliert; ob das Display genutzt wird, +// entscheidet zur Laufzeit die Einstellung displayType (Web-UI /Sensoren). #include #include -#endif // ENABLE_DISPLAY #include #include @@ -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üller"; String versionHerstellerMail = "thomas@mueller.black"; @@ -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(

Sensoren

+
+ +

Display

+ + + Wird sofort übernommen, kein Neustart nötig. Das OLED wird nur aktiviert, wenn es am I2C-Bus (0x3C) gefunden wird – ohne angeschlossenes Display startet die Steuerung immer sicher. +
+ +

Zusatz-Temperatur-Sensor

@@ -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();