IoT-Automatisierung

Raumluftqualität-Messgerät mit CO2- und PM2.5-Sensoren und MQTT bauen

Verbinden Sie einen SCD41-CO2-Sensor und einen PMS5003-Feinstaubsensor mit einem ESP32, veröffentlichen Sie die Messwerte als JSON über MQTT und richten Sie Lüftungsalarme ein, wenn die Luft verbraucht oder staubig wird.

Kleines Raumluftqualität-Messgerät mit CO2- und PM2.5-Sensoren auf einer ESP32-Platine, das Daten über WLAN sendet
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Ein Raumluftqualität-Messgerät, das CO2 und Feinstaub misst, zeigt Ihnen, wann ein Raum frische Luft oder Filterung braucht, lange bevor sich Menschen müde fühlen oder zu husten beginnen. In diesem Tutorial bauen Sie eines mit einem ESP32, einem Sensirion-SCD41-CO2-Sensor und einem Plantower-PMS5003-Feinstaubsensor, veröffentlichen die Messwerte als JSON über MQTT und richten Lüftungsalarme auf einem ConnectMQ-Broker ein.

Was die Sensoren messen

  • CO2 (ppm) ist der beste Indikator für die Lüftung in genutzten Räumen. Außenluft liegt bei etwa 400 bis 450 ppm. Lüftungsempfehlungen für Gebäude verwenden häufig Werte um 800 bis 1.000 ppm als Zeichen dafür, dass die Frischluftzufuhr für die Anzahl der Personen im Raum nicht ausreicht. In Deutschland nennt die Technische Regel für Arbeitsstätten ASR A3.6 (Lüftung) 1.000 ppm als Orientierungswert, was das Thema auch für Büros, Werkstätten und Produktionsbereiche im Mittelstand relevant macht. Der SCD41 ist ein echter photoakustischer NDIR-Sensor und keine Schätzung aus VOC-Messwerten.
  • PM2.5 (Mikrogramm pro Kubikmeter) misst Feinstaub aus Kochen, Kerzen, Außenluftverschmutzung oder Baustaub. Der PMS5003 arbeitet mit Laserstreuung und meldet PM1.0, PM2.5 und PM10.
  • Temperatur und Luftfeuchte liefert der SCD41 gratis mit; sie geben Kontext für die Behaglichkeit.

Eine VOC-Erfassung ist eine sinnvolle Erweiterung. Ein Sensirion SGP41 oder ein Bosch BME680 ergänzt einen VOC-Index, der auf Reinigungsmittel und Ausgasungen reagiert. Er fehlt im Hauptaufbau, um den Code kurz zu halten.

Bauteile und Verdrahtung

Bauteil Pin ESP32-Pin Hinweise
SCD41 VDD 3V3 Breakout-Boards haben meist Pull-ups
SCD41 GND GND
SCD41 SDA GPIO 21 I2C-Daten
SCD41 SCL GPIO 22 I2C-Takt
PMS5003 VCC 5V (VIN) Der Lüfter braucht 5 V
PMS5003 GND GND
PMS5003 TX GPIO 16 (RX2) Der Logikpegel der Sensordaten beträgt 3,3 V
PMS5003 RX GPIO 17 (TX2) Optional, nur für Befehle

Verwenden Sie die Arduino IDE 2.x mit dem esp32-Board-Paket (Core 3.x) und installieren Sie Sensirion I2C SCD4x (Version 1.x) sowie PubSubClient über den Bibliotheksverwalter. Der PMS5003-Frame wird direkt im Sketch geparst, eine zusätzliche Bibliothek ist also nicht nötig.

Platzieren Sie das Messgerät auf Atemhöhe, entfernt von Fenstern, Türen und Zuluftauslässen, und halten Sie den Lufteinlass des PMS5003 frei. Ein Sensor neben einem offenen Fenster misst die Außenluft, nicht den Raum.

Der vollständige Sketch

#include <WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>
#include <SensirionI2cScd4x.h>

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";

// Öffentlicher MQTT-Broker, nur für Tests. Für den Produktivbetrieb 8883 + WiFiClientSecure und Zugangsdaten aus dem Portal verwenden.
const char* BROKER = "broker.connectmq.com";
const int   PORT   = 1883;
const char* TOPIC  = "connectmq/demo/iaq-3hv9/office-201/air";

const unsigned long PUBLISH_MS = 30000;

WiFiClient net;
PubSubClient mqtt(net);
SensirionI2cScd4x scd4x;
HardwareSerial pmsSerial(2);

uint16_t co2 = 0;
float tempC = NAN, rh = NAN;
uint16_t pm1 = 0, pm25 = 0, pm10 = 0;
bool pmValid = false;
unsigned long lastPublish = 0;

// Einen 32-Byte-PMS5003-Frame lesen: 0x42 0x4D, Länge, 13 Datenwörter, Prüfsumme
bool readPms() {
  static uint8_t buf[32];
  while (pmsSerial.available() >= 32) {
    if (pmsSerial.peek() != 0x42) { pmsSerial.read(); continue; }
    pmsSerial.readBytes(buf, 32);
    if (buf[1] != 0x4D) continue;
    uint16_t sum = 0;
    for (int i = 0; i < 30; i++) sum += buf[i];
    uint16_t check = (buf[30] << 8) | buf[31];
    if (sum != check) continue;
    // Werte für "atmosphärische Umgebung"
    pm1  = (buf[10] << 8) | buf[11];
    pm25 = (buf[12] << 8) | buf[13];
    pm10 = (buf[14] << 8) | buf[15];
    return true;
  }
  return false;
}

void connectWifi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(250);
}

void connectMqtt() {
  while (!mqtt.connected()) {
    String id = "iaq-" + String((uint32_t)ESP.getEfuseMac(), HEX);
    if (!mqtt.connect(id.c_str())) delay(3000);
  }
}

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  pmsSerial.begin(9600, SERIAL_8N1, 16, 17);

  scd4x.begin(Wire, SCD41_I2C_ADDR_62);
  scd4x.stopPeriodicMeasurement();      // falls er nach einem Soft-Reset noch lief
  delay(500);
  if (scd4x.startPeriodicMeasurement() != 0) {
    Serial.println("SCD41 not found, check wiring");
  }

  connectWifi();
  mqtt.setServer(BROKER, PORT);
  mqtt.setBufferSize(512);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWifi();
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  if (readPms()) pmValid = true;

  bool ready = false;
  if (scd4x.getDataReadyStatus(ready) == 0 && ready) {
    uint16_t c; float t, h;
    if (scd4x.readMeasurement(c, t, h) == 0 && c != 0) {
      co2 = c; tempC = t; rh = h;
    }
  }

  if (millis() - lastPublish >= PUBLISH_MS && co2 > 0) {
    lastPublish = millis();
    char msg[256];
    snprintf(msg, sizeof(msg),
      "{\"co2_ppm\":%u,\"temp_c\":%.1f,\"rh\":%.1f,\"pm1\":%u,\"pm25\":%u,\"pm10\":%u,\"pm_valid\":%s,\"rssi\":%d}",
      co2, tempC, rh, pm1, pm25, pm10, pmValid ? "true" : "false", WiFi.RSSI());
    mqtt.publish(TOPIC, msg);
    Serial.println(msg);
  }
}

Der SCD41 liefert im periodischen Modus alle fünf Sekunden einen neuen Messwert, und der PMS5003 sendet etwa einmal pro Sekunde einen Frame. Der Sketch behält die jeweils neuesten Werte und veröffentlicht alle 30 Sekunden, was für die Raumluftqualität mehr als ausreicht.

Den Dateneingang beobachten

Abonnieren Sie im Terminal:

mosquitto_sub -h broker.connectmq.com -p 1883 -i iaq-view-3hv9 -t "connectmq/demo/iaq-3hv9/#" -v

Sie sollten Nachrichten wie diese sehen:

connectmq/demo/iaq-3hv9/office-201/air {"co2_ppm":742,"temp_c":23.4,"rh":41.8,"pm1":3,"pm25":6,"pm10":7,"pm_valid":true,"rssi":-58}

Atmen Sie sanft in die Nähe des SCD41, und der CO2-Wert steigt innerhalb einer Minute. Zünden Sie in der Nähe ein Streichholz an und blasen Sie es aus, und PM2.5 schnellt nach oben. Beides sind schnelle Plausibilitätsprüfungen dafür, dass Sensoren und Parser funktionieren. Der kostenlose öffentliche MQTT-Broker ist für diese Phase ideal, weil Sie kein Konto brauchen; er ist jedoch für Tests und Prototypen gedacht, nicht für den Dauerbetrieb.

Umstieg auf einen privaten Broker mit TLS

Raumluftdaten verraten, wann Menschen anwesend sind; Messgeräte im Produktivbetrieb sollten daher einen privaten Broker nutzen. Erstellen Sie im ConnectMQ-Portal einen Broker und ändern Sie den Verbindungscode:

#include <WiFiClientSecure.h>

WiFiClientSecure net;              // ersetzt WiFiClient
const int PORT = 8883;

// in setup(), vor mqtt.setServer():
// Das Zertifikat ISRG Root X1 (Let's Encrypt) für die vollständige Validierung laden:
// net.setCACert(ISRG_ROOT_X1_PEM);

// in connectMqtt():
// mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD");

Verwenden Sie die Client ID genau so, wie sie ausgegeben wurde, denn ConnectMQ bindet Client IDs an Zugangsdaten. Unser ESP32-MQTT-Tutorial erklärt den Umgang mit Zertifikaten auf dem ESP32 ausführlicher.

Lüftungsalarme

Legen Sie im ConnectMQ-Portal Schwellwert-Alarmregeln für die Felder co2_ppm und pm25 an. Empfohlene Ausgangswerte, die Sie an lokale Vorgaben und die Nutzung des Raums anpassen sollten:

Stufe CO2 PM2.5 Empfohlene Maßnahme
Gut unter 800 ppm unter 12 Nichts zu tun
Mäßig 800 bis 1.200 ppm 12 bis 35 Lüftung erhöhen, Filter prüfen
Schlecht über 1.200 ppm über 35 Fenster öffnen oder Lüftung verstärken, Partikelquelle suchen

Regeln lösen bei jedem Wert aus, der den Schwellwert überschreitet, und das Alarmprotokoll bewahrt den Verlauf, sodass Sie sehen, welche Räume zu bestimmten Zeiten regelmäßig stickig sind. Um einen Lüfter oder eine Klappe automatisch anzusteuern, abonnieren Sie das Luft-Topic aus einem Node-RED-Flow oder einer Home-Assistant-Automatisierung und schalten den Aktor über sein eigenes MQTT-Befehls-Topic. Die Home-Assistant-MQTT-Integration lässt das Messgerät mit wenigen Zeilen YAML als Sensor-Entitäten erscheinen.

Die Daten im Zeitverlauf lesen

Einzelne Messwerte sind weniger aussagekräftig als Tagesmuster. In einem typischen Besprechungsraum steigt CO2 während jeder Besprechung stetig an und fällt wieder ab, sobald der Raum leer ist; wie schnell es fällt, zeigt Ihnen, wie gut der Raum gelüftet wird. Ein Raum, der eine Stunde nachdem alle gegangen sind noch über 1.000 ppm liegt, hat ein Lüftungsproblem, kein Belegungsproblem. PM2.5 bleibt in Innenräumen dagegen meist niedrig und zeigt kurze Spitzen durch Kochen, Drucken, Putzen oder Ereignisse im Freien wie Verkehrsspitzen und Rauch.

Um diesen Verlauf zu speichern, betreiben Sie einen Subscriber, der jede Nachricht in eine Zeitreihendatenbank schreibt, oder lassen Sie Telegraf die Luft-Topics abonnieren. Vergleichen Sie Räume nebeneinander und betrachten Sie die schlechteste Stunde jedes Tages statt des Durchschnitts, denn Durchschnitte verbergen die Momente, in denen Menschen die Luft tatsächlich als verbraucht empfinden. Ein einfaches Wochendiagramm für das Facility Management reicht oft aus, um einen Lüftungsplan zu ändern.

Wenn Sie mehrere Messgeräte betreiben, benennen Sie jedes Topic nach dem Raum statt nach dem Gerät, damit eine ausgetauschte Platine an dieselbe Stelle veröffentlicht und der Verlauf durchgängig bleibt.

Tipps zu Kalibrierung und Genauigkeit

  1. CO2-Selbstkalibrierung. Beim SCD41 ist die automatische Selbstkalibrierung standardmäßig aktiv; sie setzt voraus, dass der Sensor regelmäßig frische Luft auf Außenniveau sieht. In Räumen, die nie vollständig gelüftet werden, deaktivieren Sie sie und führen stattdessen im Freien eine erzwungene Rekalibrierung durch.
  2. Aufwärmphase. Ignorieren Sie die Messwerte der ersten Minuten nach dem Einschalten.
  3. Eigenerwärmung. Halten Sie den ESP32 und eventuelle Spannungsregler vom SCD41 fern, sonst ist die Temperaturanzeige zu hoch.
  4. Lebensdauer des Partikelsensors. Lüfter und Laser des PMS5003 verschleißen. Versetzen Sie ihn bei langen Einsätzen zwischen den Messungen über seinen SET-Pin in den Schlafmodus.
  5. Luftfeuchte und PM. Optische Partikelsensoren messen bei sehr feuchter Luft zu hoch, weil Wassertröpfchen Licht streuen. Behandeln Sie PM-Werte oberhalb von etwa 80 Prozent relativer Luftfeuchte mit Vorsicht.

Skalierung auf ein ganzes Gebäude

Ein Messgerät pro Besprechungsraum oder Großraumzone ist eine gute Dichte. Jedes Gerät braucht eigene Zugangsdaten und ein Topic unterhalb der Gebäudehierarchie, zum Beispiel acme/muc01/zentrale/og3/besprechung-301/air. Das ist dasselbe Prinzip wie die ISA-95-Hierarchie eines Unified Namespace (UNS) in der Fabrik; wer beides betreibt, kann Gebäude- und Produktionsdaten unter einem gemeinsamen Unternehmenspräfix zusammenführen. Ein Pilotprojekt mit einem Dutzend Messgeräten plus Dashboard passt in die 25 Verbindungen des kostenlosen Serverless-Plans. Die Kombination von Luftqualität mit Präsenzsensoren im Büro und HLK-Überwachung zeigt, ob verbrauchte Luft von zu vielen Menschen oder zu wenig Lüftung kommt. Ein verwalteter MQTT-Broker erspart Ihnen den Betrieb eines Servers im Gebäude, und der Leitfaden zur IoT-Automatisierung listet weitere Projekte auf, die Sie auf demselben Broker aufbauen können.

Starten Sie mit ConnectMQ

Erstellen Sie unter app.connectmq.com einen kostenlosen Serverless-Broker, ohne Kreditkarte. Flashen Sie den Sketch mit Ihren Zugangsdaten, öffnen Sie den Live-Topic-Explorer und richten Sie Ihren ersten CO2-Alarm ein. ConnectMQ kümmert sich um TLS, Authentifizierung und Verfügbarkeit.

Häufig gestellte Fragen

Ab welchem CO2-Wert braucht ein Raum mehr Lüftung?

Viele Lüftungsrichtlinien werten dauerhafte Werte über etwa 1.000 ppm als Zeichen unzureichender Frischluft für die Belegung. Orientieren Sie sich an Ihren lokalen Vorgaben und beobachten Sie Trends statt Einzelwerte.

Kann ein VOC-Sensor einen echten CO2-Sensor ersetzen?

Nein. Sensoren, die ein "CO2-Äquivalent" melden, schätzen es aus VOCs und können weit danebenliegen. Verwenden Sie für echtes CO2 einen NDIR- oder photoakustischen Sensor wie den SCD41.

Wie oft sollte das Raumluftqualität-Messgerät veröffentlichen?

Alle 30 bis 60 Sekunden genügt völlig. Die Luftqualität ändert sich über Minuten, und selteneres Veröffentlichen spart Energie und Broker-Traffic.

Braucht der PMS5003 5 V?

Sein Lüfter benötigt eine 5-V-Versorgung, sein serieller Ausgang arbeitet jedoch mit 3,3-V-Logik, sodass der TX-Pin direkt an einen ESP32-Eingang angeschlossen werden kann.

Verwandte Artikel

Zigbee2MQTT einrichten: Vom Koordinator bis zum Dashboard

Installieren Sie Zigbee2MQTT mit Docker, konfigurieren Sie Koordinator und MQTT-Verbindung, lernen Sie Geräte an und benennen Sie sie um, steuern Sie sie per MQTT und zeigen Sie ihre Zustände auf einem Dashboard an, zuerst getestet auf einem kostenlosen öffentlichen MQTT-Broker.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.