diff --git a/heizung.ino b/heizung.ino index 03984af..57d3ad4 100644 --- a/heizung.ino +++ b/heizung.ino @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include // Adafruit_GFX und Adafruit_SSD1306 entfernt – OLED aktuell nicht genutzt, // das Objekt würde ~1 KB RAM als Frame-Buffer belegen.; @@ -31,111 +31,120 @@ const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); // MAX6675 Abgastemperatur -const int max6675SO = 8; -const int max6675CS = 9; +const int max6675SO = 8; +const int max6675CS = 9; const int max6675CLK = 10; MAX6675 ktc(max6675CLK, max6675CS, max6675SO); // Sensor-Adressen DeviceAddress wohnung = {0x28, 0xA3, 0x9A, 0x61, 0x2F, 0x21, 0x01, 0x9E}; -DeviceAddress Kunten = {0x28, 0x51, 0x4D, 0x56, 0x2F, 0x21, 0x01, 0x43}; -DeviceAddress Koben = {0x28, 0x81, 0x87, 0x64, 0x2F, 0x21, 0x01, 0x57}; +DeviceAddress Kunten = {0x28, 0x51, 0x4D, 0x56, 0x2F, 0x21, 0x01, 0x43}; +DeviceAddress Koben = {0x28, 0x81, 0x87, 0x64, 0x2F, 0x21, 0x01, 0x57}; DeviceAddress Vorlauf = {0x28, 0x22, 0xF7, 0x60, 0x2F, 0x21, 0x01, 0x31}; -DeviceAddress Aussen = {0x28, 0xE8, 0x19, 0x60, 0x2F, 0x21, 0x01, 0x19}; +DeviceAddress Aussen = {0x28, 0xE8, 0x19, 0x60, 0x2F, 0x21, 0x01, 0x19}; // ============================================================ // Einstellbare Parameter (änderbar per Serial-Befehl vom Raspi) // ============================================================ float time_adjust = 140.0; // Sekunden – Mischer-Laufzeit von ganz-zu bis ganz-auf -float neigung = 1.2; // Heizkurven-Neigung -int rtsoll = 20; // Raumtemperatur Soll (°C) -int nachtabs = 3; // Nachtabsenkung (Grad) -int tag_anfang = 4; // Stunde: Tagesbetrieb beginnt -int tag_ende = 20; // Stunde: Tagesbetrieb endet -int misch_begin = 12; // Heizung läuft wenn Aussentemperatur <= diesem Wert +float neigung = 1.2; // Heizkurven-Neigung +int rtsoll = 20; // Raumtemperatur Soll (°C) +int nachtabs = 3; // Nachtabsenkung (Grad) +int tag_anfang = 4; // Stunde: Tagesbetrieb beginnt +int tag_ende = 20; // Stunde: Tagesbetrieb endet +int misch_begin = 12; // Heizung läuft wenn Aussentemperatur <= diesem Wert // ============================================================ // Interne Zustandsvariablen // ============================================================ -float temp_aussen = 0; +float temp_aussen = 0; float temp_vorlauf = 0; -float temp_koben = 0; -float temp_kunten = 0; +float temp_koben = 0; +float temp_kunten = 0; float temp_wohnung = 0; -float temp_abgas = 0; +float temp_abgas = 0; -float vtsoll = 0; -float niveau = 0; -float darw = 0; +float vtsoll = 0; +float niveau = 0; +float darw = 0; -int stellung = 0; // aktuelle Mischerstellung in Sekunden (0 = kalt, time_adjust = warm) +int stellung = 0; // aktuelle Mischerstellung in Sekunden (0 = kalt, time_adjust = warm) int mischeron = 0; const int arranz = 7; // Anzahl Anzeige-Einträge // ============================================================ // System-Zustandsautomat // ============================================================ -enum SystemState { STARTUP, RUNNING }; +enum SystemState +{ + STARTUP, + RUNNING +}; SystemState systemState = STARTUP; unsigned long startupStart = 0; // ============================================================ // Sensor-Timing // ============================================================ -unsigned long sensorTimer = 0; -const unsigned long SENSOR_INTERVAL = 10000UL; // alle 10 Sekunden Temp lesen -bool sensorsRequested = false; +unsigned long sensorTimer = 0; +const unsigned long SENSOR_INTERVAL = 10000UL; // alle 10 Sekunden Temp lesen +bool sensorsRequested = false; unsigned long sensorRequestTime = 0; -const unsigned long SENSOR_WAIT = 800UL; // DS18B20 Konvertierungszeit +const unsigned long SENSOR_WAIT = 800UL; // DS18B20 Konvertierungszeit // ============================================================ // Mischer-Puls (non-blocking, abweichungsabhängige Pulszeit) // ============================================================ -enum MischerPulse { PULSE_NONE, PULSE_PLUS, PULSE_MINUS }; -MischerPulse currentPulse = PULSE_NONE; +enum MischerPulse +{ + PULSE_NONE, + PULSE_PLUS, + PULSE_MINUS +}; +MischerPulse currentPulse = PULSE_NONE; unsigned long mischerPulseStart = 0; -unsigned long activePulseTime = 4000UL; // wird vor jedem Puls gesetzt +unsigned long activePulseTime = 4000UL; // wird vor jedem Puls gesetzt // Zonalregelung: Schwellwerte der Vorlauf-Abweichung (°C) // Der Sensor sitzt außen am Rohr → Totzeit ca. 2-5 Min. // Bei großer Abweichung aggressiv regeln, bei kleiner vorsichtig (kein Überschwingen) -const float ZONE_SPRINT = 8.0; // >8°C Abweichung → maximale Energie -const float ZONE_JOGGING = 4.0; // 4-8°C → zügig -const float ZONE_NORMAL = 1.5; // 1.5-4°C → fein / Totband-Grenze +const float ZONE_SPRINT = 8.0; // >8°C Abweichung → maximale Energie +const float ZONE_JOGGING = 4.0; // 4-8°C → zügig +const float ZONE_NORMAL = 1.5; // 1.5-4°C → fein / Totband-Grenze // Pulszeiten pro Zone (öffnen / Richtung +) -const unsigned long PULS_SPRINT = 12000UL; // 12 Sekunden -const unsigned long PULS_JOGGING = 6000UL; // 6 Sekunden -const unsigned long PULS_NORMAL = 3000UL; // 3 Sekunden +const unsigned long PULS_SPRINT = 12000UL; // 12 Sekunden +const unsigned long PULS_JOGGING = 6000UL; // 6 Sekunden +const unsigned long PULS_NORMAL = 3000UL; // 3 Sekunden // Schließen (Richtung −): zonenabhängig, aber etwas kürzer als Plus (Sensor-Trägheit) -const unsigned long PULS_MINUS_SPRINT = 8000UL; // 8 Sekunden +const unsigned long PULS_MINUS_SPRINT = 8000UL; // 8 Sekunden const unsigned long PULS_MINUS_JOGGING = 4000UL; // 4 Sekunden -const unsigned long PULS_MINUS_NORMAL = 2000UL; // 2 Sekunden +const unsigned long PULS_MINUS_NORMAL = 2000UL; // 2 Sekunden // Check-Intervalle pro Zone -const unsigned long CHECK_SPRINT = 25000UL; // 25 Sekunden +const unsigned long CHECK_SPRINT = 25000UL; // 25 Sekunden const unsigned long CHECK_JOGGING = 45000UL; // 45 Sekunden -const unsigned long CHECK_NORMAL = 70000UL; // 70 Sekunden +const unsigned long CHECK_NORMAL = 70000UL; // 70 Sekunden const unsigned long CHECK_TOTBAND = 90000UL; // 90 Sekunden (nur Überwachung) // Wartezeit zwischen Mischer-Korrekturen (wird dynamisch aus Zone gesetzt) -unsigned long mischerCheckTimer = 0; +unsigned long mischerCheckTimer = 0; unsigned long dynamicCheckInterval = CHECK_TOTBAND; // aktuell gültiges Intervall // Anlaufphase: nach mischeron 0→1 weiß der Regler dass das Rohr kalt ist. // → erste Prüfung nach 20 s, danach 5 Min. lang aggressiverer Modus. -unsigned long mischeronStart = 0; -bool mischeronWarmup = false; // true = Anlaufphase aktiv -const unsigned long WARMUP_DAUER = 300000UL; // 5 Minuten Anlaufphase +unsigned long mischeronStart = 0; +bool mischeronWarmup = false; // true = Anlaufphase aktiv +const unsigned long WARMUP_DAUER = 300000UL; // 5 Minuten Anlaufphase // ============================================================ // Display-Zyklus (ersetzt delay(3000) im Display-Loop) // ============================================================ -unsigned long displayTimer = 0; -const unsigned long DISPLAY_INTERVAL = 3000UL; // 3 Sekunden pro Sensor +unsigned long displayTimer = 0; +const unsigned long DISPLAY_INTERVAL = 3000UL; // 3 Sekunden pro Sensor int displayIndex = 0; -bool showProzent = false; +bool showProzent = false; unsigned long prozentTimer = 0; const unsigned long PROZENT_DURATION = 3000UL; @@ -148,31 +157,33 @@ const unsigned long SERIAL_OUT_INTERVAL = 2000UL; // alle 2 Sekunden JSON senden // ============================================================ // SETUP // ============================================================ -void setup() { +void setup() +{ Serial.begin(9600); Serial.setTimeout(100); // readStringUntil() bricht nach 100ms ab statt 1s - //rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); sensors.begin(); rtc.begin(); const int relaisPins[] = { - relais1, - relais2, - relais3, - relais4, - relais5, - relais6, - relais7, - relais8 - }; - - for (int i = 0; i < 8; i++) { + relais1, + relais2, + relais3, + relais4, + relais5, + relais6, + relais7, + relais8}; + + for (int i = 0; i < 8; i++) + { pinMode(relaisPins[i], OUTPUT); digitalWrite(relaisPins[i], HIGH); // HIGH = Relais aus (ActiveLow) } // Kesselpumpe sofort an wenn Abgas heiß - if (ktc.readCelsius() >= 40) { + if (ktc.readCelsius() >= 40) + { digitalWrite(relais5, LOW); } @@ -184,32 +195,35 @@ void setup() { // Mischer auf ganz-kalt fahren (non-blocking: Relais an, Zeit merken) digitalWrite(relais1, LOW); startupStart = millis(); - systemState = STARTUP; + systemState = STARTUP; } // ============================================================ // LOOP // ============================================================ -void loop() { +void loop() +{ unsigned long now = millis(); // --- STARTUP: Mischer vollständig zurückdrehen --- - if (systemState == STARTUP) { - if (now - startupStart >= (unsigned long)(time_adjust * 1000UL)) { + if (systemState == STARTUP) + { + if (now - startupStart >= (unsigned long)(time_adjust * 1000UL)) + { digitalWrite(relais1, HIGH); // Relais aus – Mischer stoppt - stellung = 0; + stellung = 0; systemState = RUNNING; // Timer initialisieren - sensorTimer = now; - displayTimer = now; - serialOutTimer = now; + sensorTimer = now; + displayTimer = now; + serialOutTimer = now; mischerCheckTimer = now; // Erste Sensorabfrage starten sensors.requestTemperatures(); - sensorsRequested = true; - sensorRequestTime = now; + sensorsRequested = true; + sensorRequestTime = now; lcd.clear(); lcd.print("Bereit"); @@ -223,15 +237,17 @@ void loop() { handleSerialInput(); // Sensorabfrage periodisch starten - if (!sensorsRequested && (now - sensorTimer >= SENSOR_INTERVAL)) { - sensorTimer = now; + if (!sensorsRequested && (now - sensorTimer >= SENSOR_INTERVAL)) + { + sensorTimer = now; sensors.requestTemperatures(); - sensorsRequested = true; - sensorRequestTime = now; + sensorsRequested = true; + sensorRequestTime = now; } // Sensorwerte auslesen nach Konvertierungszeit - if (sensorsRequested && (now - sensorRequestTime >= SENSOR_WAIT)) { + if (sensorsRequested && (now - sensorRequestTime >= SENSOR_WAIT)) + { sensorsRequested = false; readSensors(); updateLogic(now); @@ -244,7 +260,8 @@ void loop() { updateDisplay(now); // JSON an Raspberry Pi senden - if (now - serialOutTimer >= SERIAL_OUT_INTERVAL) { + if (now - serialOutTimer >= SERIAL_OUT_INTERVAL) + { serialOutTimer = now; sendSerialJSON(); } @@ -255,24 +272,29 @@ void loop() { // ============================================================ // DS18B20 Fehlerwerte: -127°C = Kabelbruch/nicht angeschlossen, 85°C = Initialisierungswert // Bei Fehler: letzten gültigen Wert behalten (wird nicht überschrieben) -#define SENSOR_ERR_LOW -100.0 -#define SENSOR_ERR_HIGH 84.0 +#define SENSOR_ERR_LOW -100.0 +#define SENSOR_ERR_HIGH 84.0 -static void readSensor(float &dest, float raw) { - if (raw > SENSOR_ERR_LOW && raw < SENSOR_ERR_HIGH) { +static void readSensor(float &dest, float raw) +{ + if (raw > SENSOR_ERR_LOW && raw < SENSOR_ERR_HIGH) + { dest = raw; - } else { + } + else + { Serial.print(F("# Sensorfehler: ")); Serial.println(raw, 1); // dest bleibt unverändert (letzter gültiger Wert) } } -void readSensors() { - readSensor(temp_aussen, sensors.getTempC(Aussen)); +void readSensors() +{ + readSensor(temp_aussen, sensors.getTempC(Aussen)); readSensor(temp_vorlauf, sensors.getTempC(Vorlauf)); - readSensor(temp_koben, sensors.getTempC(Koben)); - readSensor(temp_kunten, sensors.getTempC(Kunten)); + readSensor(temp_koben, sensors.getTempC(Koben)); + readSensor(temp_kunten, sensors.getTempC(Kunten)); readSensor(temp_wohnung, sensors.getTempC(wohnung)); temp_abgas = ktc.readCelsius(); // MAX6675 hat eigenes Fehlerprotokoll } @@ -280,60 +302,71 @@ void readSensors() { // ============================================================ // Heizungslogik // ============================================================ -void updateLogic(unsigned long now) { +void updateLogic(unsigned long now) +{ DateTime aktuell = rtc.now(); int jetzt = aktuell.hour(); // Kesselpumpe: an wenn Abgas >= 40°C - if (temp_abgas >= 40) { + if (temp_abgas >= 40) + { digitalWrite(relais5, LOW); - } else { + } + else + { digitalWrite(relais5, HIGH); } // Heizkurve: Vorlaufsoll berechnen darw = temp_aussen - (float)rtsoll; float rtsoll_eff = (float)rtsoll; - if (jetzt < tag_anfang || jetzt >= tag_ende) { + if (jetzt < tag_anfang || jetzt >= tag_ende) + { rtsoll_eff -= (float)nachtabs; // Nachtabsenkung } - vtsoll = rtsoll_eff + niveau - - neigung * darw * (1.4347 + 0.021 * darw + 247.9e-6 * darw * darw); + vtsoll = rtsoll_eff + niveau - neigung * darw * (1.4347 + 0.021 * darw + 247.9e-6 * darw * darw); // Sicherheitsbegrenzung: vtsoll physikalisch sinnvoll halten // Schützt vor Extremwerten bei Sensorfehler (z.B. -127°C Kabelbruch) - if (vtsoll < 10.0) vtsoll = 10.0; - if (vtsoll > 75.0) vtsoll = 75.0; + if (vtsoll < 10.0) + vtsoll = 10.0; + if (vtsoll > 75.0) + vtsoll = 75.0; // Heizkreispumpe / Mischer-Freigabe - bool pufferOK = (temp_koben >= vtsoll); + bool pufferOK = (temp_koben >= vtsoll); bool aussenKalt = (temp_aussen <= (float)misch_begin); - bool abgasHot = (temp_abgas >= 40); + bool abgasHot = (temp_abgas >= 40); int mischeronAlt = mischeron; - if (aussenKalt && (pufferOK || abgasHot)) { + if (aussenKalt && (pufferOK || abgasHot)) + { digitalWrite(relais3, LOW); mischeron = 1; - } else { + } + else + { digitalWrite(relais3, HIGH); mischeron = 0; } // Anlaufphase erkennen: wenn Mischer gerade eingeschaltet wurde - if (mischeron == 1 && mischeronAlt == 0) { + if (mischeron == 1 && mischeronAlt == 0) + { // Übergang 0→1: Rohr ist kalt, sofort erste Prüfung in 20 Sekunden - mischeronWarmup = true; - mischeronStart = now; + mischeronWarmup = true; + mischeronStart = now; mischerCheckTimer = now - CHECK_SPRINT + 20000UL; // erste Prüfung nach 20s Serial.println(F("# Anlaufphase gestartet")); } - if (mischeron == 0) { + if (mischeron == 0) + { mischeronWarmup = false; // Anlaufphase beenden wenn Heizung aus } bool inWarmup = mischeronWarmup && (now - mischeronStart < WARMUP_DAUER); // ── Zonalregelung ────────────────────────────────────────────────────────── // Abweichung positiv = Vorlauf zu kalt, negativ = zu warm - float abweichung = vtsoll - temp_vorlauf; + float abweichung = vtsoll - temp_vorlauf; float abweichungAbs = fabs(abweichung); // Regelzone bestimmen → gibt Pulszeit und Check-Intervall vor. @@ -341,46 +374,66 @@ void updateLogic(unsigned long now) { // Würden wir es mid-flight ändern, würde handleMischerPulse() den Puls // zum falschen Zeitpunkt beenden und stellung falsch berechnen. // Während Anlaufphase: eine Zone aggressiver, da das Rohr erst aufgeheizt werden muss. - if (abweichungAbs > ZONE_SPRINT || (inWarmup && abweichungAbs > ZONE_JOGGING)) { + if (abweichungAbs > ZONE_SPRINT || (inWarmup && abweichungAbs > ZONE_JOGGING)) + { dynamicCheckInterval = CHECK_SPRINT; - if (currentPulse == PULSE_NONE) activePulseTime = PULS_SPRINT; - } else if (abweichungAbs > ZONE_JOGGING || (inWarmup && abweichungAbs > ZONE_NORMAL)) { + if (currentPulse == PULSE_NONE) + activePulseTime = PULS_SPRINT; + } + else if (abweichungAbs > ZONE_JOGGING || (inWarmup && abweichungAbs > ZONE_NORMAL)) + { dynamicCheckInterval = CHECK_JOGGING; - if (currentPulse == PULSE_NONE) activePulseTime = PULS_JOGGING; - } else if (abweichungAbs > ZONE_NORMAL) { + if (currentPulse == PULSE_NONE) + activePulseTime = PULS_JOGGING; + } + else if (abweichungAbs > ZONE_NORMAL) + { dynamicCheckInterval = CHECK_NORMAL; - if (currentPulse == PULSE_NONE) activePulseTime = PULS_NORMAL; - } else { + if (currentPulse == PULSE_NONE) + activePulseTime = PULS_NORMAL; + } + else + { // Totband: Mischer sitzt richtig, nur überwachen dynamicCheckInterval = CHECK_TOTBAND; - if (currentPulse == PULSE_NONE) activePulseTime = PULS_NORMAL; + if (currentPulse == PULSE_NONE) + activePulseTime = PULS_NORMAL; } // Mischer regeln – nur wenn Heizkreis läuft, kein Puls aktiv // und das zonenabhängige Check-Intervall abgelaufen ist if (mischeron == 1 && currentPulse == PULSE_NONE && - (now - mischerCheckTimer >= dynamicCheckInterval)) { + (now - mischerCheckTimer >= dynamicCheckInterval)) + { - if (abweichung > ZONE_NORMAL && stellung < (int)time_adjust) { + if (abweichung > ZONE_NORMAL && stellung < (int)time_adjust) + { // Mischer weiter öffnen (wärmer) // activePulseTime wurde oben zonenabhängig gesetzt - currentPulse = PULSE_PLUS; + currentPulse = PULSE_PLUS; mischerPulseStart = now; digitalWrite(relais2, LOW); Serial.print(F("# Mische + Puls=")); Serial.print(activePulseTime / 1000); Serial.print(F("s Abw=")); Serial.println(abweichung, 1); - } else if (abweichung < -ZONE_NORMAL && stellung > 0) { + } + else if (abweichung < -ZONE_NORMAL && stellung > 0) + { // Mischer schließen (kälter) – zonenabhängig, etwas kürzer als Plus - if (abweichungAbs > ZONE_SPRINT) { + if (abweichungAbs > ZONE_SPRINT) + { activePulseTime = PULS_MINUS_SPRINT; - } else if (abweichungAbs > ZONE_JOGGING) { + } + else if (abweichungAbs > ZONE_JOGGING) + { activePulseTime = PULS_MINUS_JOGGING; - } else { + } + else + { activePulseTime = PULS_MINUS_NORMAL; } - currentPulse = PULSE_MINUS; + currentPulse = PULSE_MINUS; mischerPulseStart = now; digitalWrite(relais1, LOW); Serial.print(F("# Mische - Abw=")); @@ -392,73 +445,84 @@ void updateLogic(unsigned long now) { // ============================================================ // Mischer-Puls beenden wenn Zeit abgelaufen // ============================================================ -void handleMischerPulse(unsigned long now) { - if (currentPulse == PULSE_NONE) return; +void handleMischerPulse(unsigned long now) +{ + if (currentPulse == PULSE_NONE) + return; - if (now - mischerPulseStart >= activePulseTime) { + if (now - mischerPulseStart >= activePulseTime) + { // Stellung um die tatsächliche Pulszeit (in Sekunden) aktualisieren int pulsSekunden = (int)(activePulseTime / 1000UL); - if (currentPulse == PULSE_PLUS) { + if (currentPulse == PULSE_PLUS) + { digitalWrite(relais2, HIGH); stellung += pulsSekunden; - if (stellung > (int)time_adjust) stellung = (int)time_adjust; - } else { + if (stellung > (int)time_adjust) + stellung = (int)time_adjust; + } + else + { digitalWrite(relais1, HIGH); stellung -= pulsSekunden; - if (stellung < 0) stellung = 0; + if (stellung < 0) + stellung = 0; } - currentPulse = PULSE_NONE; + currentPulse = PULSE_NONE; mischerCheckTimer = now; // Check-Timer neu starten (nächstes Intervall läuft ab jetzt) // Prozentanzeige für 3 Sekunden einblenden - showProzent = true; + showProzent = true; prozentTimer = now; - //showProzentDisplay(); + // showProzentDisplay(); } } // ============================================================ // Display-Zyklus (non-blocking) // ============================================================ -void updateDisplay(unsigned long now) { +void updateDisplay(unsigned long now) +{ // Prozentanzeige: solange aktiv, nichts anderes zeigen - if (showProzent) { - if (now - prozentTimer >= PROZENT_DURATION) { - showProzent = false; + if (showProzent) + { + if (now - prozentTimer >= PROZENT_DURATION) + { + showProzent = false; displayTimer = now; // Zyklus-Timer zurücksetzen } return; } - if (now - displayTimer >= DISPLAY_INTERVAL) { + if (now - displayTimer >= DISPLAY_INTERVAL) + { displayTimer = now; displayIndex = (displayIndex + 1) % arranz; showSensor(); } } -//void showProzentDisplay() { -// float proz = (stellung * 100.0) / time_adjust; -// lcd.clear(); -// lcd.print("Mischer "); -// lcd.print(proz, 0); -// lcd.print("%"); -// lcd.setCursor(0, 1); -// lcd.print("geoeffnet"); -//} +// void showProzentDisplay() { +// float proz = (stellung * 100.0) / time_adjust; +// lcd.clear(); +// lcd.print("Mischer "); +// lcd.print(proz, 0); +// lcd.print("%"); +// lcd.setCursor(0, 1); +// lcd.print("geoeffnet"); +// } -void showSensor() { +void showSensor() +{ char Uhr[] = "hh:mm"; DateTime aktuell = rtc.now(); float sensorVal[7] = { - temp_aussen, temp_vorlauf, vtsoll, - temp_koben, temp_kunten, temp_wohnung, temp_abgas - }; - const char* ort[7] = { - "Aussentemp", "Vorlauf ist", "Vorlauf soll", - "Puffer Oben", "Puffer unt.", "Heizung", "Abgas" - }; + temp_aussen, temp_vorlauf, vtsoll, + temp_koben, temp_kunten, temp_wohnung, temp_abgas}; + const char *ort[7] = { + "Aussentemp", "Vorlauf ist", "Vorlauf soll", + "Puffer Oben", "Puffer unt.", "Heizung", "Abgas"}; lcd.clear(); lcd.begin(16, 2); @@ -471,7 +535,7 @@ void showSensor() { // showProzentDisplay(); // Sonderanzeige Mischer-Grenzwerte – nur wenn Mischer aktiv läuft - //if (mischeron) { + // if (mischeron) { // if (stellung >= (int)time_adjust) { // lcd.clear(); // lcd.print("Mischer max!"); @@ -489,60 +553,60 @@ void showSensor() { // ============================================================ // JSON-Ausgabe an Raspberry Pi // ============================================================ -void sendSerialJSON() { +void sendSerialJSON() +{ char buffer[512]; float abw = vtsoll - temp_vorlauf; snprintf(buffer, sizeof(buffer), - "{" - "\"aussentemp\":%.1f," - "\"vorlauf_ist\":%.1f," - "\"vorlauf_soll\":%.1f," - "\"kessel_oben\":%.1f," - "\"kessel_unten\":%.1f," - "\"wohnung\":%.1f," - "\"abgas\":%.1f," - "\"stellung\":%d," - "\"mischeron\":%d," - "\"neigung\":%.2f," - "\"rtsoll\":%d," - "\"misch_begin\":%d," - "\"nachtabs\":%d," - "\"tag_anfang\":%d," - "\"tag_ende\":%d," - "\"time_adjust\":%d," - "\"abweichung\":%.1f," - "\"check_intervall\":%ld," - "\"puls_zeit\":%ld," - "\"puls_aktiv\":%d," - "\"warmup\":%d" - "}", + "{" + "\"aussentemp\":%.1f," + "\"vorlauf_ist\":%.1f," + "\"vorlauf_soll\":%.1f," + "\"kessel_oben\":%.1f," + "\"kessel_unten\":%.1f," + "\"wohnung\":%.1f," + "\"abgas\":%.1f," + "\"stellung\":%d," + "\"mischeron\":%d," + "\"neigung\":%.2f," + "\"rtsoll\":%d," + "\"misch_begin\":%d," + "\"nachtabs\":%d," + "\"tag_anfang\":%d," + "\"tag_ende\":%d," + "\"time_adjust\":%d," + "\"abweichung\":%.1f," + "\"check_intervall\":%ld," + "\"puls_zeit\":%ld," + "\"puls_aktiv\":%d," + "\"warmup\":%d" + "}", - temp_aussen, - temp_vorlauf, - vtsoll, - temp_koben, - temp_kunten, - temp_wohnung, - temp_abgas, + temp_aussen, + temp_vorlauf, + vtsoll, + temp_koben, + temp_kunten, + temp_wohnung, + temp_abgas, - stellung, - mischeron, - neigung, - rtsoll, - misch_begin, - nachtabs, - tag_anfang, - tag_ende, - time_adjust, + stellung, + mischeron, + neigung, + rtsoll, + misch_begin, + nachtabs, + tag_anfang, + tag_ende, + time_adjust, - abw, - dynamicCheckInterval / 1000, - activePulseTime / 1000, - (currentPulse != PULSE_NONE) ? 1 : 0, - mischeronWarmup ? 1 : 0 - ); + abw, + dynamicCheckInterval / 1000, + activePulseTime / 1000, + (currentPulse != PULSE_NONE) ? 1 : 0, + mischeronWarmup ? 1 : 0); Serial.println(buffer); } @@ -551,31 +615,42 @@ void sendSerialJSON() { // Serielle Befehle vom Raspberry Pi verarbeiten // Format: SET \n // ============================================================ -void handleSerialInput() { - if (!Serial.available()) return; +void handleSerialInput() +{ + if (!Serial.available()) + return; String line = Serial.readStringUntil('\n'); line.trim(); - if (!line.startsWith(F("SET "))) return; + if (!line.startsWith(F("SET "))) + return; // "SET key value" parsen - int sp1 = line.indexOf(' '); // nach "SET" + int sp1 = line.indexOf(' '); // nach "SET" int sp2 = line.indexOf(' ', sp1 + 1); - if (sp1 < 0 || sp2 < 0) return; + if (sp1 < 0 || sp2 < 0) + return; String key = line.substring(sp1 + 1, sp2); String val = line.substring(sp2 + 1); val.trim(); bool ok = true; - if (key == F("neigung")) neigung = val.toFloat(); - else if (key == F("rtsoll")) rtsoll = val.toInt(); - else if (key == F("nachtabs")) nachtabs = val.toInt(); - else if (key == F("misch_begin")) misch_begin = val.toInt(); - else if (key == F("tag_anfang")) tag_anfang = val.toInt(); - else if (key == F("tag_ende")) tag_ende = val.toInt(); - else ok = false; + if (key == F("neigung")) + neigung = val.toFloat(); + else if (key == F("rtsoll")) + rtsoll = val.toInt(); + else if (key == F("nachtabs")) + nachtabs = val.toInt(); + else if (key == F("misch_begin")) + misch_begin = val.toInt(); + else if (key == F("tag_anfang")) + tag_anfang = val.toInt(); + else if (key == F("tag_ende")) + tag_ende = val.toInt(); + else + ok = false; // Bestätigung zurücksenden Serial.print(F("{\"ok\":"));