auto format changes and change max to MAX
This commit is contained in:
1 parent
2a9830017a
commit
9be7ab7ffb
1 file changed
+283
-208
+283
-208
@@ -6,7 +6,7 @@
|
||||
#include <DallasTemperature.h>
|
||||
#include <RTClib.h>
|
||||
#include <LiquidCrystal.h>
|
||||
#include <max6675.h>
|
||||
#include <MAX6675.h>
|
||||
#include <Wire.h>
|
||||
// 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 <key> <value>\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\":"));
|
||||
|
||||
Reference in new issue
Block a user