Hardware Heimautomatisierung Software

ESPHome: Feinstaub- / CO2-Ampel fürs Smart Home

Luftqualitätssensor für Feinstaub und CO2

Heute baue ich eine CO2-Ampel, die zusätzlich Temperatur, Feuchtigkeit und Feinstaub messen kann. Diese schickt die Werte an einen Home Assistant. So kann ich dort mit einer Automatisierung auf schlechte Luftqualität reagieren, bzw. aufmerksam gemacht werden. Nach eineinhalb Jahren HomeOffice wird es mal Zeit. Im entvölkerten Office steht schon länger eine mit LoRa angebundene CO2 Ampel, die die Notwendigkeit des Lüftens meldet. Hierzu nutze ich den ESPHome Baukasten.

Als Basis hab ich mir den IKEA Vindriktning Sensor auserkoren. Er ist spottbillig für einen PM2.5 Feinstaubsensor, und soll laut Datenblatt (lokale Kopie) eine MTTF (mean time to failure) von >5 Jahren haben. Ob er das Versprechen einhält, wird sich noch zeigen müssen. Die Sensoren, die ich für das Luftdaten-Projekt im Einsatz hatte, haben fast das 2,5-fache gekostet und jeweils nur ~1 Jahr durchgehalten. Und die hatten weder Gehäuse noch ne schicke LED-Anzeige. Allerdings wurden diese auch (ganzjährig) draußen eingesetzt. Ein weiterer Vorteil ist, dass relativ viel Platz im Gehäuse ist, um den ESP32 und die restliche Sensorik unterzubekommen. Man kann den ESP Huckepack auf der RX-Datenleitung lauschen lassen, während die IKEA MCU weiterhin die Anfragen an den Sensor stellt und die LEDs entsprechend leuchten lässt. Will man nur den PM-Sensor aus dem Vindriktning looten und ohne die IKEA MCU verwenden, so geht dies ebenso.

Vindriktnings innere Werte

Das Gehäuse lässt sich durch Entfernen der 4 Schrauben auf der Rückseite öffnen. Anschließend die beiden Kabel des Feinstaubsensors mit der MCU trennen. Nun kann die Front entfernt werden.

Schauen wir uns das PCB mal etwas näher an. Wir sehen, dass wir gar nicht so viel sehen. Ein paar Widerstände, ein Kondensator, ein Linearregler und nen 8-beinigen Microchip. Dieser hat angenehmerweise all seine Beinchen an Diagnose-Pads herangeführt. Und 5V Spannung liegt dort ebenfalls an.

Abgesehen von +5V und GND, ist vor allem das mit RESET beschriftete Pad interessant, auf dem ich die serielle Kommunikation der MCU mit dem Sensor mitlauschen will. An dem LED_R_1 Pad liegt die Spannung der Photodiode an. Diese werde ich dafür verwenden, die Helligkeit der CO2 LED an die originale anzupassen.

PoC der Feinstaub- / CO2-Ampel auf dem Breadboard

Zunächst kompiliere ich mit dem Home Assistant Pluging eine quasi leere ESPHome Firmware und flashe diese auf den Wroom32. Die komplette .yaml Konfiguration findes sich im weiteren Verlauf dieses Artikels, daher verzichte ich an dieser Stelle auf die Snippets, die mich zum Endergebnis führen. Vor allem stecke ich jetzt einmal alles “trocken” an die später vorgesehen Pins und teste es. Es ist mir nicht nur einmal passiert, dass ich beim finalen Aufbau noch einmal Pins umgelegt habe, die entweder das zukünftige OTA-Flashen oder gar das Booten des ESPs verhindern.

Alle Sensoren der CO2-Ampel werden an ihren künftigen Beinchen des ESP32 zum Testen angeschlossen.

Sensorik

Der Sensirion SDC30 Sensor (CO2, Luftfeuchtigkeit, Temperatur) wird per I²C angesteuert. Folglich benötigt er +3,3V, GND von dem Development Board (später von dem MCP1825S 3,3V Festspannungsregler). In einem Internetforum hieß es, der SDC würde sich im Betrieb etwas erwärmen, was die Temperaturwerte verfälschen soll. Das scheint (zumindest in diesem Setup) aber kaum messbar zu sein. Nichtsdestotrotz habe ich an den I²C Bus zusätzlich noch einen BMP280 Temperatur / Luftdrucksensor mit angeschlossen. Wirklich notwendig ist der retrospektiv aber nicht – es sei denn man möchte unbedingt noch den Luftdruck messen. Weil ich aber sowieso ne Handvoll von dem BMPs hier rumfliegen und sonst keine andere Verwendung für sie hatte, hab ich ihn sogar final mit verbaut. Warum? Weil ich es kann!

Die TX Leitung des PM1006 (am RX UART der IKEA MCU) wird an einem beliebigen GPIO des ESPs angeschlossen. Es reicht, in diesem Betriebsmodus, lediglich den RX Pin in der ESPHome UART Konfiguration zu definieren. Für autonomen Betrieb des Sensors ohne IKEA MCU wird auch ein TX Port benötigt. Siehe Dokumentation des PM1006-Moduls von ESPHome.

Infrarot Empfänger und Sender

Auf dem Bild kann man noch 2 Geräte erkennen, die ich angeschlossen habe. Hierbei handelt es sich um einen Infrarot Empfänger und eine IR-Blaster Diode. Diese schließe ich zusätzlich an, weil dieser Sensor im Schlafzimmer seinen Dienst verrichten soll. Da ich gerne mit Musik oder zu einer Doku einschlafe, soll der Home Assistant später das Media-Geraffel abschalten, sobald Sleep as Android, über seinen Webhook den Eintritt in eine Tiefschlafphase meldet. Der IR Empfänger wird dabei lediglich initial zum Anlernen der IR-Codes genutzt.

Natürlich könnte man sich den Empfänger im finalen Ausbau der CO2-Ampel sparen, aber es ist so viel Platz im Gehäuse und vielleicht wird er später nochmal nützlich.

Status LED

Als letztes wird eine Status LED hinzugefügt. Der Sensor soll die CO2 Werte ja auch optisch analog zum Vindriktning anzeigen können. Hierfür hab ich mir ein Tütchen 10mm RGB-LEDs mit gemeinsamer Kathode (gemeinsamer Minuspol) geklickt. Ich habe hier pro Farbe einen 270Ω Widerstand verbaut. Vermutlich ginge auch ein einzelner Widerstand an der Kathode, aber ich denke, das könnte beim Farbmischen (Orange z. B.) zu inkonsistenter Helligkeit führen. Und Widerstände sind in meinem Privathackerspace auch hinreichend vorhanden.

Schaltplan

Als alles lief, habe ich einen Schaltplan in KiCad erstellt, als Dokumentation für mich selbst. Nach diesem Schaltplan werde ich später alles final verlöten. Das linke Bild ist der Schlafzimmer-Sensor mit dem zusätzlichen IR-Kram. Diesen habe ich für das Wohnzimmer weggelassen, da hier bereits alles smart ist.

Mir ist bewusst, dass der Infrarot-Blaster durch keinen Vorwiderstand geschützt ist. Ursprünglich war hier sogar einer vorgesehen, aber damit leuchtete sie zu schwach, sodass die Befehle bei den Geräten nicht ankamen. Andere Leute im Internet verstärken den Ausgang des ESPs sogar noch mit einem Transistor. Am Ende des Tages reden wir hier von sehr kurzen gepulsten Signalen.

Bisschen belastend ist aber, dass ich die Spannung der Photodiode direkt an einen ADC-Pin des ESP32 heranführe, da hier Spannungen von 0-5V ankommen. Der ESP32 verträgt eigentlich gut nur Spannungen bis 3.3V. Höhere Messwerte als 3,9V können auch gar nicht gemessen werden, selbst mit einer attenuation von 11dB. Bislang ist keiner der beiden ESPs gestorben, also scheint das irgendwie schon noch innerhalb der Toleranz zu liegen. Er muss auch nur Spannungen von <0,3V für die dunkle Darstellung von Spannungen darüber unterscheiden können. Das klappt so.

Beachten sollte man, dass die Photodiode deutlich empfindlicher im imfraroten Bereich als im sichtbaren ist. Auf Tageslicht reagiert sie ganz gut, auf künstliches weniger. Hier habe ich eine sehr helle LED-Taschenlampe zum Testen benutzt und direkt in die LED geleuchtet.

ESPHome Software für die CO2-Ampel kompilieren

Das Schöne an ESPHome ist, dass man kaum programmieren (können) muss, um eine Firmware zu erstellen. Man beschreibt einfach gesagt die Schnittstelle(n) des ESPs mit einer YAML Konfigurationsdatei. Den Rest macht ESPHome selbst. Am Ende herausgefallen kommt eine Binärdatei, die initial einmal per USB auf das Gerät geflasht wird. Fügt man den ‘ota’ Block mit ein, kann man zukünftig direkt aus dem Webinterface von ESPHome heraus das Flashen “durch die Luft” anstoßen.

Nachfolgend einige kommentierte Auszüge aus meiner Konfiguration. Die vollständigen Dateien finden sich bei mir im git: Schlafzimmer, Wohnzimmer

esphome:
  name: sleepingroom
  platform: ESP32
  board: nodemcu-32s

wifi:
  ssid: "Voltage-legacy"
  password: !secret voltage_legacy_psk
  use_address: sleepingroom.home  
  power_save_mode: high
  fast_connect: on

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Schlafzimmer Fallback Hotspot"
    password: !secret fallback_psk

captive_portal:

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:
  password: !secret api

# Enable over-the-air updates
ota:
  password: !secret otaCode-Sprache: YAML (yaml)

Zuerst definiere ich den Namen des Sensors und auf welcher Plattform und Board er laufen soll.

Anschließend zwinge ich ESPHome, den im lokalen DNS eingetragenen FQDN “sleepingroom.home” statt Broadcast-Discover zu verwenden.

Damit der Fallback-AP Dinge tun kann, muss das captive Portal definiert sein.

API und OTA und Fallback-AP Updates sollten selbstverständlich geschützt sein, sonst kann der Sensor sehr einfach übernommen werden.

# Initialize I²C
i2c:
 - id: bus_a
   sda: 13
   scl: 16
   scan: true

# Initialize LEDC GPIOs
output:
  - platform: ledc
    pin: GPIO17
    id: led_red
  - platform: ledc
    pin: GPIO18
    id: led_green
    max_power: 50%
  - platform: ledc
    pin: GPIO19
    id: led_blue

# Initialize UART for Vindriktning sensor
uart:
  rx_pin: GPIO26
  baud_rate: 9600Code-Sprache: PHP (php)

Hier definiere ich die Pins für I²C, die 3 PWM-Ausgänge für die 3 Farben der LED und den RX-Pin für den UART.

Den grünen Kanal für die LED schränke ich auf 50% Leistung ein, da sie sonst viel zu hell wäre und Farbverfälschungen die Folge wären.

# Define RGB mode for LED
light:
  - platform: rgb
    id: co2_light
    name: "CO2 Status LED"
    red: led_red
    green: led_green
    blue: led_blue
    internal: trueCode-Sprache: PHP (php)

Für die Ansteuerung der LED verwende ich die RGB Plattform, die mir die manuelle Farbmischung abnimmt. Ich definiere sie als interne Komponente, da Farbe und Helligkeit auf Basis der Sensorwerte eingestellt werden. Täte ich das nicht, könnte man Farbe und Helligkeit wie bei jeder anderen smarten Lampe über ein Farbrad einstellen und sogar Effekte verwenden.

sensor:
  # WiFi signal strength
  - platform: wifi_signal
    name: "WiFi Signalstärke"
    update_interval: 60s
  # SCD30 CO2 +  temperature + humidity sensor
  # * air quality category 0: LED is purple, sensor needs calibration (<380ppm)
  # • air quality category 1: LED is green (380-800ppm) 
  # • air quality category 2: LED is yellow (900-1200ppm) 
  # • air quality category 3: LED is orange/amber (1200 bis 1600ppm) 
  # • air quality category 4: LED is red (1600-2000ppm) 
  - platform: scd30
    i2c_id: bus_a
    co2:
      name: "Schlafzimmer CO2"
      id: co2_value
      accuracy_decimals: 1
      on_value_range:
        - below: 380
          then:
            - text_sensor.template.publish:
                id: co2_warn
                state: "Kalibrierung nötig"
            - light.turn_on:
                id: co2_light
                red: 75%
                green: 0%
                blue: 100%
        - above: 380
          below: 800
        [ ... ]
    temperature:
      name: "Schlafzimmer Temperatur"
      accuracy_decimals: 2
    humidity:
      name: "Schlafzimmer Luftfeuchtigkeit"
      accuracy_decimals: 1
    # SCD30 temp sensor might be a bit off - this is'nt
    temperature_offset: 0 °C
    ambient_pressure_compensation: 1
    automatic_self_calibration: True
    address: 0x61
    update_interval: 60sCode-Sprache: PHP (php)

Nun definiere ich die Sensoren. Der ‘wifi_signal’ Sensor meldet die aktuelle Empfangsstärke zum AP.

Die Angabe vom I²C Bus ist optional, die Angabe der Adresse nur bei mehreren Sensoren gleichen Typs verpflichtend. Der Ordnung halber, habe ich aber alles festgenagelt.

Sinnvoll ist es, ‘ambient_pressure_compensation’ und ‘automatic_self_calibration” zu aktivieren. Ersteres macht nur bei einem statisch platzierten Sensor Sinn, bei einem mobilen will man stattdessen ‘altitude_compensation’ verwenden. Die automatische Kalibrierung beobachtet über einen 2 Wochen Zeitraum, CO2 Minimalmesswerte begleitet durch Temperatur- und Feuchtigkeitsschwankungen. So wird der Messwert auf den aktuellen durchschnittlichen CO2-Wert von ~400ppm kalibriert.

Mit ‘on_value_range’ reagiert der ESP auf definierte Wertebereiche, um den Text-Sensor und die Farbe der LED zu setzen. Diese habe ich hier abgekürzt.

  # Vindriktning particulate matter sensor
  - platform: pm1006
    pm_2_5:
      name: "Feinstaub PM 2.5µm"
      on_value_range:
      - above: 0
        below: 35
        then:
          - text_sensor.template.publish:
              id: pm25_warn
              state: "grün"
      - above: 35
        below: 85
        then:
          - text_sensor.template.publish:
              id: pm25_warn
              state: "orange"
      - above: 85
        then:
          - text_sensor.template.publish:
              id: pm25_warn
              state: "rot"Code-Sprache: CSS (css)

Da die IKEA MCU die verbauten LEDs bereits leuchten lässt, muss ich hier nichts weiter konfigurieren. Ich setze aber – wie schon beim CO2-Sensor – einen Text-Sensor basierend auf den Grenzwerten, bei denen die LED-Farbe wechselt.

  # Analog photo diode voltage
  - platform: adc
    pin: 32
    name: "Spannung Photodiode"
    attenuation: 11db
    update_interval: 1s
    internal: true
    on_value_range:
      - below: 0.25
        then:
          light.turn_on:
            id: co2_light
            brightness: 30%
      - above: 0.5
        then:
          light.turn_on:
            id: co2_light
            brightness: 60%Code-Sprache: CSS (css)

Zu guter Letzt wird noch die Spannung der Photodiode gemessen, um die Helligkeit der CO2 LED einstellen zu können. Unter 0,25V soll die LED dunkel leuchten, über 0,5V hell. Die Helligkeit habe ich so eingestellt, dass sie sich mit den originalen LEDs in etwa deckt. Die Lücke zwischen den Wertebereichen ist absichtlich, um konsistentes Verhalten im Übergangsbereich zu gewährleisten.

CO2-Ampel in Home Assistant einbinden

Wenn der Sensor mit der neuen Firmware startet, sollte er sich nun direkt am Home Assistant melden. Folglich sollte jetzt bereits eine Benachrichtigung von der ESPHome Integration aufgeploppt sein. Folgt man dieser, kann man die Einbindung mit einem Klick starten, nach der API-Passworteingabe tauchen die Sensoren kurze Zeit später unter Geräte und Entitäten auf. Fügt man die Sensoren zu einer Lovelace Karte hinzu, könnte es folgendermaßen aussehen:

CO2 Ampel verlöten und zusammenbauen

Jetzt, da alles funktioniert und die Firmware installiert ist, kann das Verlöten der Einzelteile erfolgen. Meine erste Idee war, ein PCB mit Spannungsversorgung (3,3V Festspannungsregler) zu löten. Alle Pins vom ESP32 sollten zum Verteilerboard gehen und dort verteilt werden. Zusätzlich wollte ich Vorwiderstände für die LEDs hier ebenfalls unterbringen. Beim Außensensor hatte ich gute Erfahrungen damit gemacht, einfach die Käbelchen an die Lötpads des Wroom32 Moduls anzulöten – das waren aber nur 3 Stück. Ich gebs gerne zu, das war bei so vielen Käbelchen echt ne blöde Idee. Erstens ist es sehr fummelig, und zweitens die Verbindung nicht sehr stark. Das heißt ich musste beim Reinstopfen in das Gehäuse etliche abgelöste Pins wieder reparieren. Einer hat dann auch direkt ein Lötpad vom ESP abgerissen. Ich würde sowas nicht empfehlen, aber seht selbst.

2. Versuch

Weil das so blöd gelaufen ist, habe ich mir für den 2. Sensor vorher noch ein Breakout-Board besorgt, auf dem ich den ESP setzen und die Käbelchen gescheit anlöten kann. Die PSU habe ich diesmal einzeln an der Gehäuse Front befestigt und die LED-Vorwiderstände direkt an die LED auf ein PCB gelötet. So kann ich auf das Verteilerboard verzichten und alle verbliebenden Verbindungen an das Breakout-Board anlöten, was die Sache viel übersichtlicher und stabiler macht. Außerdem muss der Abgang zum ESP so nur noch 90° nach oben abgeknickt werden und es rutscht alles problemlos in das Gehäuse. Eine Plastiknase musste ich dabei abknipsen, da sie gegen das ESP-Board stieß.

So sieht das doch ganz gut aus. Seitdem tun beide Sensoren klaglos ihren Dienst und ich hab schon die ersten Erkenntnisse, unter welchen Bedingungen sich die Raumluft hier in der Wohnung wie schnell verschlechtert und ein Lüften angeraten ist. Hierzu habe ich eine einfache Automatisierung geschrieben, die auslöst, wenn Feinstaub- oder CO2-Warnwert länger als 10m lang “rot” ist (und ich zu Hause bin). Der Home Assistant schickt dann eine persistente Push-Benachrichtigung auf mein Handy, die erst wieder entfernt wird, wenn die Luftqualität wieder “grün” ist.

Fertige CO2-Ampel fürs Wohnzimmer

Autor

Seit Kindheitstagen ist der Computer sein Begleiter. Was mit Linux anfing, wurde 2005 ein/e Beruf/ung, die weit über den Arbeitsplatz hinausgeht. Durch stetige Weiterentwicklung fasste er auch im *BSD Segment Fuß und bietet mittlerweile professionelle Lösungen im Bereich Hosting, Networking und Infrastruktur an. Als Ausgleich beschäftigt er sich neben Computerspielen mit der Fotografie.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.