Lüftungshelfer mit Python und RasPi Wetterstation

Diagramm absolute Luftfeuchte

Mit steigender Außentemperatur stellt die Belüftung der Kellerräume ein zunehmendes Problem dar. Die warme Außenluft enthält viel Feuchtigkeit, die an den kalten Kellerwänden kondensieren könnte. Kennt man die genauen Werte von Außen- und Innentemperatur sowie die Luftfeuchtigkeit, dann kann ein kleines Python Script bei der Entscheidung helfen, ob das Kellerfenster besser auf oder zu sein sollte. Auf einem Raspberry Pi signalisiert das Script seine Empfehlung leicht erkennbar durch das Aufleuchten einer roten oder grünen LED.

Vorsicht beim Lüften!

Warme Luft kann wesentlich mehr Wasserdampf aufnehmen als kalte Luft. 1 m³ Luft enthält bei einer relativen Luftfeuchte von 60 % und einer Temperatur von 25 °C Wasserdampf mit einer Masse von 13.8 g. Bei einer Temperatur von 16 °C entspricht die gleiche Wasserdampfmenge einer relativen Luftfeuchte von 100 %. Es käme also zur Kondensation des Wasserdampfs zu Wasser. Das heißt, wenn 25 °C warme Luft bei einer Luftfeuchte von 60 % in einen Keller gelangt, dessen Wände 16 °C kalt sind, dann werden die Wände nass. Das sollte man auf jeden Fall vermeiden und bei dieser Konstellation lieber die Kellerfenster schließen!

Sättigungsdampfdruck

Physikalisch beschreibt man den Zustand, bei dem der gasförmige und flüssige Zustand im Gleichgewicht sind, als Sättigungsdampfdruck. Der Sättigungsdampfdruck von Wasserdampf ist von der Temperatur abhängig, zu seiner Berechnung dient die Magnus-Formel. Diese Formel kann man in Python zum Beispiel so implementieren:

a = 6.112
b = 17.67
c = 243.5

# Compute saturated water vapor pressure in hPa
# Param t - temperature in °C
def svp(t):
  svp = a * math.exp((b*t)/(c+t))
  return svp

Die angegebenen Werte für die Konstanten a, b und c liefern für den Temperaturbereich von -30 bis +35 °C eine gute Näherung mit einem Fehler von maximal 0.1 %.

Relative und absolute Luftfeuchte

Hygrometer, wie zum Beispiel die in meiner Wetterstation verwendeten Sensoren, messen immer die relative Luftfeuchte in Prozent. Aus diesem Wert und dem berechneten Sättigungsdampfdruck für die ebenfalls von der Wetterstation gemessene Temperatur lässt sich nun die absolute Luftfeuchte berechnen:

# Compute actual water vapor pressure in hPa
# Param rh - relative humidity in %
# Param t - temperature in °C
def vp(rh, t):
  vp = rh/100. * svp(t)
  return vp

# Compute the absolute humidity in g/m³
# Param rh - relative humidity in %
# Param t - temperature in °C
def ah(rh, t):
  mw = 18.016 # kg/kmol (Molekulargewicht des Wasserdampfes)
  rs = 8314.3 # J/(kmol*K) (universelle Gaskonstante)
  ah = 10**5 * mw/rs * vp(rh, t)/(t + 273.15)
  return ah

Die Funktion ah liefert die Masse des in einem Kubikmeter enthalten Wasserdampfs. Die Lüftungsempfehlung ergibt sich aus dem Vergleich der absoluten Luftfeuchte innen und außen - nur wenn sie außen kleiner ist als innen, sollte das Fenster offen sein!

Messung von Temperatur und Luftfeuchte

Aus diesen Überlegungen folgt, dass man zur Beurteilung der Lage vier Messwerte benötigt: Temperatur und relative Luftfeuchte jeweils außen und innen im zu belüftenden Raum. Typischerweise ist das der Keller, da hier die Temperaturdifferenz im Sommer am größten ist. Die an anderer Stelle beschriebene Wetterstation hat einen Außensensor KS 300 und einen Innensensor S 300 TH im Keller, die alle benötigten Werte liefern. Der aktuelle Messwert gelangt in eine Round-Robin-Datenbank, aus der ihn rrdtool lastupdate wieder ausliest:

# Read the actual weather data using rrdtool
# Return (header, values)
def read_weather_data():
  handle = os.popen('rrdtool lastupdate weather.rrd')
  header = handle.readline().split()
  handle.readline() # empty line
  values = handle.readline().split()
  handle.close()
  return (header, values)

Ausgabe der Lüftungsempfehlung mittels LED

Schaltbild
Anschluss der LEDs an die GPIO Leiste des RasPi.

Da die Software der Wetterstation auf einem Raspberry Pi läuft, kann man ihn auch gleich zur Ausgabe benutzen. Sie soll so einfach wie möglich erfolgen, daher verwende ich nur eine rote und eine grüne LED, die über jeweils einen Vorwiderstand direkt an die GPIO Leiste des RasPi angeschlossen sind. Bei meinem Modell waren die BCM GPIO-Pins 9 und 11 noch frei, das sind Pin 21 und 23 an der GPIO Stiftleiste. GPIO-Pin 9 steuert die rote LED und 11 die grüne.

Aufbau
Die LEDs und die Vorwiderstände passen auf eine kleine Universallochrasterplatte, die mittels einer Buchsenleiste direkt auf der Stiftleiste des RasPi befestigt ist.

Softwareseitig ist die Ansteuerung mit Hilfe von WiringPi realisiert. Zuerst ruft man das in WiringPi enthaltene gpio-Tool in der Shell auf, um die zur Ansteuerung der LEDs verwendeten Pins zu definieren:

gpio export 9 out
gpio export 11 out

Das erzeugt zwei spezielle Dateien im sysfs Bereich, die jetzt das Python-Script direkt zum Schalten der LEDs benutzen kann:

# Control the green led
# Param value - Whether to turn the led on of off
def led_green(value):
  f = open('/sys/class/gpio/gpio11/value', 'w')
  f.write('1') if value else f.write('0')
  f.close()

# Control the red led
# Param value - Whether to turn the led on of off
def led_red(value):
  f = open('/sys/class/gpio/gpio9/value', 'w')
  f.write('1') if value else f.write('0')
  f.close()

Dieses Vorgehen hat den Vorteil, dass das Python-Script jetzt nicht mit root Rechten laufen muss.

Entscheidungsfindung

Nun sind alle Zutaten beisammen, um basierend auf den Messwerten eine Lüftungsempfehlung durch Aufleuchten der LEDs zu geben. Neben dem simplen Vergleich der absoluten Luftfeuchte berücksichtigt der Algorithmus auch noch die Aktualität der Messdaten anhand ihres Zeitstempels. Ist dieser älter als 15 Minuten, dann gehen beide LEDs aus. Außerdem warnt das Script bei zu geringen Außentemperatur durch Aufleuchten der roten LED (Fenster zu), um Frostschäden zu vermeiden:


def main():
  (header, values) = read_weather_data()
  # timestamp is first value
  timestamp = float(values.pop(0).rstrip(': '))
  # create dictionary {sensor name -> sensor value}
  valmap = dict(zip(header, values))
  # compute abs. humdity in cellar (sensor s5)
  ah_cellar = ah(float(valmap['hums5']), float(valmap['temps5']))
  # compute abs. humdity outside (sensor s9)
  t_out = float(valmap['temps9'])
  ah_out = ah(float(valmap['hums9']), t_out)

  # Make decisions based on comparision of abs. humdity values
  print(timestamp, ah_cellar, ah_out)
  if time.time() - timestamp > 15 * 60:
    print('Values too old')
    led_green(False)
    led_red(False)
  elif t_out < 3.0:
    print('Too cold outside - better close the window!')
    led_green(False)
    led_red(True)
  elif math.fabs(ah_out - ah_cellar) < 1:
    print('Difference too small')
    led_green(True)
    led_red(True)
  elif ah_out > ah_cellar:
    print('Close window!')
    led_green(False)
    led_red(True)
  else:
    print('Open window')
    led_green(True)
    led_red(False)

if __name__ == '__main__':
  main()
Geamtansicht
Der Raspberry Pi (ein älteres Modell B ist völlig ausreichend) mit aufgesteckter LED Leiste.

Das Script check_hum.py (Download) testet man zunächst an der Kommandozeile. Läuft alles wie gewünscht, dann sorgt ein per crontab -e vorgenommener Eintrag in der crontab für einen regelmäßigen Aufruf des Scripts.

*/5 * * * * $HOME/humidity/check_hum.py > $HOME/humidity/hum.log 2>&1

Fazit

Der RasPi-Lüftungshelfer läuft seit einem Jahr störungsfrei und liefert eine einfach abzulesende Empfehlung für das Lüften des Kellers. Damit verhindert er wirksam die Entstehung von Kondenswasser an den Kellerwänden. Die Software ist modular aufgebaut, damit lässt sie sich leicht an andere Situationen anpassen. Wenn Sie zum Beispiel Ihre Messwerte nicht mit rrdtool auslesen können, dann ersetzen Sie einfach die Methode read_weather_data durch eine entsprechend angepasste Implementierung. Das gleiche gilt für die Ausgabe. Ganz Mutige können versuchen, statt der LEDs direkt einen Lüftermotor oder einen Fensteröffner anzusteuern.