Seit einiger Zeit beschäftige ich mich mit dem Thema Luftfeuchtigkeit, vorrangig ging es bisher um die Entfeuchtung von Kellerräumen, welche mit einem Luftentfeuchter auf einem schadfreien Luftfeuchte-Level gehalten werden sollen.
Am Anfang war die These, einen Luftentfeuchter reinzustellen, die Luftfeuchtigkeit auf 50% einzustellen, den kältesten Punkt im Raum zu ermitteln, den Taupunkt zu berechnen. Wenn der Taupunkt nicht erreicht wird, dann sollte alles gut werden.
Fazit: Funktionierte im Sommer problemlos, bei bestimmten Aussentemperaturen über eine längere Zeit wurde es dennoch manchmal immer wieder "muffelig", obwohl der Taupunkt nie erreicht wurde.
Den Stromverbrauch des Luftentfeuchters im Blick, wollte ich die Luftfeuchte nicht unbedingt duerhaft nach unten korrigieren, also dann erstmal hingesetzt und überlegt, wo das eigentliche Problem liegt.
Nimmt man eine Luftfeuchtig von 55%rel und eine Raumtemperatur von 23.3Grad an, liegt der Taupunkt bei ca. 13.7 Grad, die absolute Luftfeuchte liegt dann bei 9.8g/KgLuft. Hat dann zum Beispiel die Kellerwand an der kältesten Stelle 20.0 Grad, könnte man nun der Meinung sein, dass die Luftfeuchte für den ganzen Raum schadfrei sein könnte, da man ja weit vom Taupunkt entfernt ist.
Nun ist es aber so, dass der Taupunkt den Wert kennzeichnet, bei dem die Luftfeuchtigkeit 100%rel erreicht und dann Feuchtigkeit sichtbar an dem Objekt kondensiert, wenn diese Temperatur unterschritten wird.
Nun weiss man aber auch, dass Schimmelbildung bei einer relativen Luftfeuchte von 70% mit Sicherheit einsetzt und dauerhaft die relative Luftfeuchte unter 70% liegen sollte, damit auch Schimmelbildung ausgeschlossen werden kann.
Berechnet man nun die Luftfeuchte an der Kellerwand bei 20.0 Grad, kommt an dieser Stelle ein Wert von ca. 67%rel raus, das heisst, man befindet sich schon fast an der Grenze zur Schimmelbildung. Das heisst, dass die maximal schadfreie Raumluftfeuchte von allen 3 Faktoren abhängig ist.
Also musste mehr Sensorik her um die Raumparameter zu erfassen. Ausgestattet mit einem Aktor der den Luftentfeuchter steuert, einem Raumthermostat der die Luftfeuchte und Raumtemperatur misst und zwei Temperatursensoren, die an den kältesten Stellen im Raum angebracht wurden, werden die entsprechenden Werte erfasst und einem Script zur weiteren Berechnung übergeben.
Das eigentliche Scipt erzeugt ein "Luftfeuchte Objekt" in dem alle erforderlichen Daten erfasst und die dafür erforderlichen Funktionen enthalten sind.
Für die Berechnung ist auch der Luftdruck in "bar" von Bedeutung, wird dieser nicht angegeben, wird als Luftfdruck 0m über Meeresspiegel angenommen.
Übrigends: für die Taupunktberechnung spielt der Luftdruck keine Rolle, will man allerdings korrekte Werte für Dampfdruck oder die absFeuchte, sind die Ergebnisse umso genauer, wenn der tatsächliche oder auch der über die "einfache Höhenformel" errechnete Luftdruck benutzt wird. Am besten wäre natürlich der augenblicklich gemessene Luftdruck eines Barometers.
Benötigt man lediglich Vergleichs-Werte z.B. für eine Lüftungsempfehlung, kann man mit dem Standard-Luftdruck bei 0m Meeresspiegel rechnen.
Nun zum Script:
Um im Script ein neues Luftfeuchteobjekt zu erstellen und die Berechnung zu starten (der Luftdruck ist optional):
var keller = new Luftfeuchte(temperatur, luftfeuchte [,LuftdruckInBar]);
keller.Calc;
Danach können die einzelnen Werte ausgelesen werden, die wichtigsten sind:
keller.tTau == Taupunkt GradCelsius
keller.absFeuchte == Absolute Feuchte in KgWasser/KgLuft
Die Funktion Calc gibt ein Flag zurück, die signalisiert ob die Berechnung erfolgreich (true) oder fehlerhaft (false) war.
Um nun die maximale Luftfeuchtigkeit des Raumes zu berechnen, bei der auch an der kältesten Stelle des Raumes die maximale Luftfeuchtigkeit nicht überschritten wird, besitzt das Objekt eine Funktion, welche die maximal zulässige Raumluftfeuchte berechnet.
Zur Berechnung (wird maxRelLuftfeuchte nicht angegeben wird 65%rel angenommen):
var maxPhi = keller.CalcMaxPhi(raumtemperatur, tempDerKaltenStelle [, maxRelLuftfeuchteDesRaums]);
Als Ergebnis erhält man die maximal zulässige Luftfeuchte, damit an der kältesten Stelle der geforderte Wert nicht überschritten wird.
Beispielhaft hier ein Script, das ich seit einiger Zeit in Verwendung habe:
!
// V0.02 Luftfeuchtigkeit im Büro Keller regeln ! // Namen für die einzelnen states const OBJ_TEMP_SENSOR1 = 'LuftfeuchteKeller.Bodensensor1'; const OBJ_TEMP_SENSOR2 = 'LuftfeuchteKeller.Bodensensor2'; const OBJ_MAXPHI = 'LuftfeuchteKeller.ObererGrenzwert'; const OBJ_MINPHI = 'LuftfeuchteKeller.UntererGrenzwert'; const OBJ_AKTPHI = "LuftfeuchteKeller.AktuelleLuftfeuchtigkeit"; const OBJ_AKTTEMP = "LuftfeuchteKeller.AktuelleTemperatur"; ! // Namen der Homematic Sensoren const ID_HM_LF_STATUS = 'LF-Buero-Status'; // Systemvariable der Homematic die Entfeuchter steuert const ID_HM_SENSOR_PHI = 'Wand-Buero-TempSensor.HUMIDITY'; // Sensor für Luftfeuchte const ID_HM_SENSOR_TEMP = 'Wand-Buero-Regler.ACTUAL_TEMPERATURE'; // Sensor für Raumtemperatur const ID_HM_TempSensor1 = 'BodenTempBuero-Sensor1.TEMPERATURE'; // Sensoren für Bodentemperatur const ID_HM_TempSensor2 = 'BodenTempBuero-Sensor2.TEMPERATURE'; ! const STATUS_TO_WETT = true; // Flag für Systemvariable der Homematic, wenn Raum zu feucht const STATUS_OK = false; // ... wenn Raumfeuchte OK const PHI_HYST = 4.0; // Schalthsysterese für Luftentfeuchtung in %relFeuchte ! var hmsLfStatus = getIdByName(ID_HM_LF_STATUS); var hmsSensorPhi = getIdByName(ID_HM_SENSOR_PHI); var hmsSensorTemp = getIdByName(ID_HM_SENSOR_TEMP); var hmsBodenTemp1 = getIdByName(ID_HM_TempSensor1); var hmsBodenTemp2 = getIdByName(ID_HM_TempSensor2); ! stateCreate(); // Benötigte States Erstellen setTimeout (mainStart, 1000); // Wait for states if new ! function mainStart() { check(); // Bei Scriptstart ausführen on ({id: hmsSensorPhi, change: "ne" }, check); // Wenn sich Luftfeuchtigkeit ändert on ({id: hmsSensorTemp, change: "ne" }, check); // Wenn sich Raumtemperatur ändert on ({id: hmsBodenTemp1, change: "ne" }, check); // Wenn sich Bodentemperatur 1 ändert on ({id: hmsBodenTemp2, change: "ne" }, check); // Wenn sich Bodentemperatur 2 ändert } ! // Prüfen der Klima Parameter function check() { var maxRoomPhi = 0; // Maximale zulässige Raum-Luftfeuchte berechnet nach dem kältesten Punkt im Raum var minRoomPhi = 0; // Unterer Wert der Luftfeuchte zur Steuerung var tempSens1 = 0; // Temperatur des Bodensensors 1 var tempSens2 = 0; // Temperatur des Bodensensors 2 var minTemp = 0; // Niedrigster Wert von beiden Sensoren ! t = new Luftfeuchte(getState(hmsSensorTemp).val, getState(hmsSensorPhi).val); setState(OBJ_AKTPHI,getState(hmsSensorPhi).val); // Speichern für Historie setState(OBJ_AKTTEMP, getState(hmsSensorTemp).val); t.Calc(); tempSens1 = getState(hmsBodenTemp1).val; // Bodentemperaturwerte holen tempSens2 = getState(hmsBodenTemp2).val; setState(OBJ_TEMP_SENSOR1, tempSens1); // Speichern für Historie setState(OBJ_TEMP_SENSOR2, tempSens2); if(tempSens1 < tempSens2) { // Kleinste Temperatur suchen minTemp = tempSens1; } else { minTemp = tempSens2; } // Maximal zulässige Raumluftfeuchte berechnen für kältesten Punkt maxRoomPhi = t.CalcMaxPhi(getState(hmsSensorTemp).val, minTemp, 69); if (isNaN(maxRoomPhi)) { log('Berechnung der maximalen Luftfeuchte fehlerhaft! Prüfen!', 'error'); return; } minRoomPhi = maxRoomPhi - PHI_HYST; setState(OBJ_MAXPHI, maxRoomPhi); // Speichern für Historie setState(OBJ_MINPHI, minRoomPhi); // Speichern für Historie ! if (t.relFeuchte >= maxRoomPhi) { // Luftfeuchte zu hoch, Entfeuchtung aktivieren setState(hmsLfStatus, STATUS_TO_WETT); log('Luftentfeuchtung Keller aktiviert'); return; } ! if(t.relFeuchte <= minRoomPhi) { // Luftfeuchte OK, Entfeuchtung deaktivieren setState(hmsLfStatus, STATUS_OK); log('Luftentfeuchtung Keller deaktiviert'); return; } // Hier kommen wir nur hin, wenn Luftfeuchte zwischen max und min liegt, also keine Reaktion nötig ist } ! // Prototype für Luftfeuchte // Quellenangabe: Dieses kleine und freie Programm stammt von der heute nicht mehr aktiven Seite // "http://ourworld.compuserve.com/homepages/MTEC/" von MTEC Technology Software. // Es arbeitet nach dem Standardwerk "Properties of Water and Steam in SI-Units", // bearbeitet von Ernst Schmidt, Springer-Verlag. // Angepasst fuer ioBroker von coffee-yunk function Luftfeuchte(temperatur , relFeuchte, luftdruck ) { const PMIN = 0.01; const PMAX = 20; const TMIN = -20; const TMAX = 100; const MAX_ALLOWED_PHI = 65; ! if (luftdruck === undefined) {luftdruck = 1.01325;} this.temperatur = parseFloat(temperatur); // t Temperatur in Grad Celsius this.relFeuchte = parseFloat(relFeuchte); // phi relative Luftfeuchte in % this.luftdruck = parseFloat(luftdruck); // p Luftdruck in bar this.sattDampf = NaN; // pS SattDampf in bar this.absFeuchte = NaN; // x Absolute Feuchte in in kg/kg this.enthalpie = NaN; // h EnthalpieLuft KJ/kg (Energiegehalt) this.pD = NaN; // pD Dampfdruck in Bar this.rhoD = NaN; // rhoD Dampf in Kg/m3 this.rhoL = NaN; // Luftdichte in KG/m3 this.rhoG = NaN; // Gesamtfeuchte in KG/m3 this.thoL = NaN; this.tTau = NaN; // Taupunkt in Grad Celsius ! /* Maximale zulässige Luftfeuchte des Raumes berechnen in Abhängikeit des kältesten Punktes im Raum Dabei die maximal zulässige Raumluftfeuchte (safePhi) beachten roomTemp = Raumtemperatur lowTemp = Temperatur der kältesten Stelle im Raum maxColdPhi= Maximale zulässige Luftfeuchte an der kältesten Stelle safePhi = (Optional) Höchste Luftfeuchte des Raumes auf die begrenzt wird (Keine Angabe dann 65 %rel) return NaN, fehlerhafte Daten max. %rel Luftfeuchte des Raumes, die nicht überschritten werden darf um am kältesten Punkt die maximale Luftfeuchte zu erzielen */ this.CalcMaxPhi = function (roomTemp, lowTemp, maxColdPhi, safePhi) { if(safePhi === undefined) {safePhi = MAX_ALLOWED_PHI;} if(safePhi > 100 || safePhi < 0) {return NaN;} if(roomTemp === undefined) {return NaN;} if(lowTemp === undefined) {return NaN;} ! // abs Luftfeuchte berechnen für t-Boden und maxColdPhi var n = new Luftfeuchte(lowTemp, maxColdPhi); n.Calc(); var absRaumfeuchte = n.absFeuchte; ! n.temperatur = roomTemp; ! for (r=safePhi; r>0; r--){ n.relFeuchte = r; n.Calc(); if (n.absFeuchte <= absRaumfeuchte) { return r; } } return 0; ! }; ! // Berechnet den Luftdruck nach der sogenannten "Höhenformel" in bar this.CalcHighToBar = function(h) { return (Math.round(1013.25 *Math.pow(1 - (0.0065 * h / 288.15), 5.255))) / 1000; }; ! // Object copy this.Copy = function(obj) { obj.temperatur = this.temperatur; obj.relFeuchte = this.relFeuchte; obj.luftdruck = this.luftdruck; obj.sattDampf = this.sattDampf; obj.absFeuchte = this.absFeuchte; obj.enthalpie = this.enthalpie; obj.pD = this.pD; obj.rhoD = this.rhoD; obj.rhoL = this.rhoL; obj.rhoG = this.rhoG; obj.thoL = this.thoL; obj.tTau = this.tTau; }; ! // Alle Werte berechnen this.Calc = function() { if (this.temperatur.length === 0 || this.luftdruck.length === 0) { return false;} this.temperatur = parseFloat(this.temperatur); this.luftdruck = parseFloat(this.luftdruck); this.relFeuchte = parseFloat(this.relFeuchte); ! if(this.temperatur < TMIN) { log('temperatur < ' + TMIN + ' °C', 'warn'); return false; } if(this.luftdruck < PMIN) { log('luftdruck < ' + PMIN + ' bar', 'warn'); return false; } if(this.temperatur > TMAX) { log('temperatur > ' + TMAX + ' °C','warn'); return false; } if(this.luftdruck > PMAX) { log('luftdruck > ' + PMAX + ' bar','warn'); return false; } this.sattDampf = this.SattdampfDruckWasser(this.temperatur); this.AbsFeuchte(); this.EnthalpieLuft(); this.DichteWDampf(); this.DichteLuft(); this.rhoG = this.rhoD + this.rhoL; this.Taupunkt(); return true; }; ! this.SattdampfDruckWasser = function (t) { var aq = 5.426651; var bq = -2005.1; var cq = 0.00013869; var dq = 0.000000000011965; var eq = -0.0044; var fq = -0.0057148; var kq = 293700; var ta = (t + 273.15) / 647.3; ! if(t >= -20 && t <= 374) { var te = t + 273.16; var x = te * te - kq; var y = 374.11 - t; var h1 = dq * x * x; var al = aq + bq / te + cq * x / te * (Math.pow(10, h1) - 1) + eq * Math.pow(10, (fq * Math.pow(y, 1.25))); h1 = Math.exp(-12 * Math.pow(ta, 4)); var pS = 1.01325 * Math.pow(10, al) + (ta - 0.422) * (0.577 - ta) * Math.exp(h1) * 0.00980665; return pS; } else { return 0; } }; ! // Energiegehalt der Luft berechnen in KJ/KG this.EnthalpieLuft = function EnthalpieLuft() { var t = this.temperatur; var x = this.absFeuchte; this.enthalpie = 1.006 * t + x * (1.86 * t + 2500); }; ! // Absolute Feuchte in kgWasser pro kgLuft this.AbsFeuchte = function AbsFeuchte() { var phi = this.relFeuchte; var p = this.luftdruck; var pS = this.sattDampf; var phi = phi / 100; this.absFeuchte = 0.622 * phi * pS / (p - phi * pS); }; ! // Dichte Wasserdampf Berechnungen this.DichteWDampf = function () { var phi = this.relFeuchte; var t = this.temperatur; var pS = this.sattDampf; this.rhoD = 1E5 * phi / 100 * pS / 461.1 / (273.15 + t); this.pD = phi / 100 * pS; }; ! this.DichteLuft = function () { var phi = this.relFeuchte; var t = this.temperatur; var p = this.luftdruck; var pS = this.sattDampf; this.rhoL = 1E5 * 1.001076176 * ((p - phi / 100 * pS) / 287.2 / (273.15 + t)); }; ! // Dampfsättigungstemperatur berechnen this.Sattdampftemperatur = function (pS) { var t = 100; var dt = 0.001; var t0; var t1; var f0; var f1; var fs; do { t0 = t - dt / 2; t1 = t + dt / 2; f0 = this.SattdampfDruckWasser(t0) - pS; f1 = this.SattdampfDruckWasser(t1) - pS; fS = (f1 - f0) / dt; t0 = t; t = t - f0 / fS; } while(Math.abs(t - t0) > dt); return(t); }; ! this.SattdampfDruckWasser = function(t) { var aq = 5.426651; var bq = -2005.1; var cq = 0.00013869; var dq = 0.000000000011965; var eq = -0.0044; var fq = -0.0057148; var kq = 293700; var ta = (t + 273.15) / 647.3; if(t >= -20 && t <= 374) { var te = t + 273.16; var x = te * te - kq; var y = 374.11 - t; var h1 = dq * x * x; var al = aq + bq / te + cq * x / te * (Math.pow(10, h1) - 1) + eq * Math.pow(10, (fq * Math.pow(y, 1.25))); h1 = Math.exp(-12 * Math.pow(ta, 4)); var pS = 1.01325 * Math.pow(10, al) + (ta - 0.422) * (0.577 - ta) * Math.exp(h1) * 0.00980665; return(pS); } else { return(0); } }; ! // Taupunkt berechnen this.Taupunkt = function() { var tTau = 0; var x = this.absFeuchte; if(x > 0) {tTau = this.Sattdampftemperatur(x * this.luftdruck / (0.622 + x));} if(tTau === Infinity) { this.tTau = 0; return; } if(x > 0 && tTau > 0) { this.tTau = tTau; } else { this.tTau = 0; } }; ! } function Aufrunden(Wert, Stellen) { Stellen = parseInt(Stellen); var x = Math.pow(10, Stellen); var str = "" + Math.round(Wert * x) / x; return str; } ! // States generieren, falls noch nicht in der DB function stateCreate() { createState(OBJ_TEMP_SENSOR1, { name: 'Temperatur des Bodensensor 1', type: 'number', def: 0.0, unit: "Grad", role: "value" }); ! createState(OBJ_TEMP_SENSOR2, { name: 'Temperatur des Bodensensor 2', type: 'number', def: 0.0, unit: "Grad", role: "value" }); ! createState(OBJ_MAXPHI, { name: 'Maximal erlaubte berechnete Luftfeuchtigkeit', type: 'number', def: 0.0, unit: "%rel", role: "value" }); createState(OBJ_MINPHI, { name: 'Minimale Luftfeuchtigkeit für Luftentfeuchter', type: 'number', def: 0.0, unit: "%rel", role: "value" }); ! createState(OBJ_AKTPHI, { name: 'Aktuelle Luftfeuchtigkeit im Raum', type: 'number', def: 0.0, unit: "%rel", role: "value" }); ! createState(OBJ_AKTTEMP, { name: 'Aktuelle Temperatur im Raum', type: 'number', def: 0.0, unit: "Grad", role: "value" }); } !
Um die Lufentfeuchtung zu steuern, wird eine Systemvariable in der Homematic gesetzt. Natürlich werden in der Homematic noch andere Parameter geprüft, da es ja keinen Sinn macht, den Luftentfeuchter rennen zu lassen, während zum Beispiel durch geöffnete Fenster weiterhin feuchte Luft in den Raum strömt.
Wer es brauchen kann….. und wer Verbesserungsvorschläge hat.... (bin immer noch script-anfänger )