A smart CO2 traffic light built with an ESP32, the Sensirion SCD41 CO2/temperature/humidity sensor, and WS2812B NeoPixel LEDs. Fully local via Home Assistant – no cloud dependency.
- Real-time CO2 measurement with the Sensirion SCD41 (400–5000 ppm)
- Color-coded LED indicator via WS2812B NeoPixel strip
- Temperature & humidity as bonus sensors
- Text-based air quality rating directly in Home Assistant
- LED on/off switch – e.g. turn off at night via automation
- Manual calibration (FRC) button from Home Assistant
- Altitude compensation pre-configured
- Boot animation – pulsing blue until first readings arrive
| CO2 (ppm) | Color | Brightness | Rating |
|---|---|---|---|
| < 800 | 🟢 Green | 30% | Excellent |
| 800 – 1000 | 🟡 Yellow | 50% | Good – ventilate soon |
| 1000 – 1400 | 🟠 Orange | 70% | Moderate – open windows! |
| 1400 – 2000 | 🔴 Red | 100% | Poor – ventilate urgently! |
| > 2000 | 🔴 Red flashing | 100% | Critical – ventilate now! |
CO2 Sensor https://amzn.to/4ucHS8e * ESP32 C3 Super Mini https://amzn.to/4sqjfmU * Display https://amzn.to/40N0oGQ * Neopix https://amzn.to/4rOGJCj * Schrauben Set https://amzn.to/4smAsO3 *
Links marked with ‘*’ are promotional links -> Affiliate links to Amazon. If you use them, I earn a commission at no extra cost to you. Thanks!
ESP32 DevKit v1
┌─────────────────────┐
│ │
│ GPIO21 (SDA) ──────┼──── SCD41 SDA
│ GPIO22 (SCL) ──────┼──── SCD41 SCL
│ 3.3V ──────────────┼──── SCD41 VCC
│ │
│ GPIO16 ────────────┼──── NeoPixel DIN
│ 5V (VIN) ──────────┼──── NeoPixel VCC
│ │
│ GND ───────────────┼──── SCD41 GND + NeoPixel GND
│ │
└─────────────────────┘
- ESPHome installed (as Home Assistant add-on or CLI)
- Home Assistant with ESPHome integration
secrets.yamlwith your WiFi credentials
git clone https://github.com/DEIN_USERNAME/co2-ampel-esphome.git
cd co2-ampel-esphomeCreate a secrets.yaml in your ESPHome directory (if not already present):
wifi_ssid: "YOUR_WIFI_NAME"
wifi_password: "YOUR_WIFI_PASSWORD"Open co2_ampel_scd41.yaml and adjust if needed:
substitutions:
neopixel_pin: GPIO16 # Your NeoPixel data pin
num_leds: "8" # Number of LEDs
sda_pin: GPIO21 # I²C SDA
scl_pin: GPIO22 # I²C SCLAltitude compensation (in the sensor: block):
altitude_compensation: 400m # Your altitude above sea levelTemperature offset (SCD41 self-heating):
filters:
- offset: -3.0 # Adjust after comparing with a reference thermometeresphome run co2_ampel_scd41.yamlOr use the ESPHome Dashboard in Home Assistant.
After flashing, the device appears automatically in Home Assistant. The following entities are created:
| Entity | Type | Description |
|---|---|---|
sensor.co2_ampel_co2 |
Sensor | CO2 in ppm |
sensor.co2_ampel_temperatur |
Sensor | Temperature in °C |
sensor.co2_ampel_luftfeuchtigkeit |
Sensor | Relative humidity in % |
text_sensor.co2_ampel_luftqualitaet |
Text | Air quality rating as text |
light.co2_ampel_led |
Light | NeoPixel LED (manual control) |
switch.co2_ampel_led_aktiv |
Switch | LED automation on/off |
button.co2_ampel_neustart |
Button | ESP32 restart |
button.co2_ampel_co2_kalibrierung_frc |
Button | Manual calibration |
Enabled by default. The SCD41 calibrates itself automatically when regularly exposed to fresh air (e.g. ventilated room). Takes approximately 7 days for initial calibration.
- Let the sensor run for at least 3 minutes with fresh air (window wide open)
- Press the "CO2 Ampel CO2 Kalibrierung (FRC)" button in Home Assistant
- The sensor calibrates to 420 ppm (current outdoor CO2 reference value)
⚠️ Warning: Incorrect calibration leads to inaccurate readings! When in doubt, use ASC.
For SK6812 (RGBW), change the type:
light:
- platform: neopixelbus
type: GRBW
variant: SK6812Simply adjust num_leds in substitutions:
substitutions:
num_leds: "16" # e.g. for a 16-LED NeoPixel ringThe relevant blocks (i2c, sensor: scd4x, light: neopixelbus, script, interval) can be copied into any existing ESPHome YAML. Watch out for pin conflicts!
| Problem | Solution |
|---|---|
| SCD41 not found | Check I²C scan in logs, verify wiring (address: 0x62) |
| CO2 values unrealistically high/low | Perform manual calibration with fresh air |
| LEDs show wrong colors | Change type: GRB to RGB (depends on LED type) |
| LEDs flicker | Add 330Ω resistor on data pin, capacitor on VCC/GND |
| Temperature too high | Adjust offset in temperature filter |
| ESP32 won't boot | GPIO16 is safe; avoid GPIO12 (strapping pin) |
This project is licensed under the MIT License.
Eine smarte CO2-Ampel auf Basis eines ESP32, dem Sensirion SCD41 CO2-/Temperatur-/Feuchte-Sensor und WS2812B NeoPixel LEDs. Vollständig lokal über Home Assistant steuerbar – kein Cloud-Zwang.
💡 Du willst tiefer einsteigen? Schau dir den ESPHome Meisterkurs an – der umfassende deutschsprachige Kurs rund um lokale Smart-Home-Geräte mit ESP32 und Home Assistant.
- Echtzeit CO2-Messung mit dem Sensirion SCD41 (400–5000 ppm)
- Farbcodierte LED-Anzeige über WS2812B NeoPixel-Streifen
- Temperatur & Luftfeuchtigkeit als Bonus-Sensoren
- Text-Bewertung der Luftqualität direkt in Home Assistant
- LED ein/aus Schalter – z.B. nachts per Automation ausschalten
- Manuelle Kalibrierung (FRC) per Button aus Home Assistant
- Höhenkompensation vorkonfiguriert
- Boot-Animation – Blau pulsierend bis erste Messwerte vorliegen
| CO2 (ppm) | Farbe | Helligkeit | Bewertung |
|---|---|---|---|
| < 800 | 🟢 Grün | 30% | Sehr gut |
| 800 – 1000 | 🟡 Gelb | 50% | Gut – bald lüften |
| 1000 – 1400 | 🟠 Orange | 70% | Mäßig – Lüften! |
| 1400 – 2000 | 🔴 Rot | 100% | Schlecht – Dringend lüften! |
| > 2000 | 🔴 Rot blinkend | 100% | Kritisch – Sofort lüften! |
| Bauteil | Beschreibung | ca. Preis |
|---|
CO2 Sensor https://amzn.to/4ucHS8e * ESP32 C3 Super Mini https://amzn.to/4sqjfmU * Display https://amzn.to/40N0oGQ * Neopix https://amzn.to/4rOGJCj * Schrauben Set https://amzn.to/4smAsO3 * Links mit „*“ sind Werbe Links -> Affiliate-Links zu Amazon. Bei Nutzung erhalte ich eine Provision, ohne dass dir Mehrkosten entstehen. Danke!
Gehäuse gibt es in der Macherwerkstatt (monatliche Live Calls und 3D Druck Bibliothek) oder in dem ESPHome Meisterkurs
Hinweis: Bei mehr als 8 LEDs unbedingt separate 5V-Versorgung nutzen und einen 330Ω Widerstand am Datenpin sowie einen 1000µF Elko an der LED-Versorgung vorsehen.
- ESPHome installiert (als Home Assistant Add-on oder CLI)
- Home Assistant mit ESPHome-Integration
secrets.yamlmit deinen WLAN-Zugangsdaten
git clone https://github.com/DEIN_USERNAME/co2-ampel-esphome.git
cd co2-ampel-esphomeErstelle eine secrets.yaml im ESPHome-Verzeichnis (falls nicht vorhanden):
wifi_ssid: "DEIN_WLAN_NAME"
wifi_password: "DEIN_WLAN_PASSWORT"
api_key: "DEIN_API_KEY" # esphome generate-api-key
ota_password: "DEIN_OTA_PASSWORT"
ap_password: "DEIN_AP_PASSWORT"Öffne co2_ampel_scd41.yaml und passe ggf. an:
substitutions:
neopixel_pin: GPIO16 # Dein NeoPixel Data-Pin
num_leds: "8" # Anzahl deiner LEDs
sda_pin: GPIO21 # I²C SDA
scl_pin: GPIO22 # I²C SCLHöhenkompensation (im sensor: Block):
altitude_compensation: 400m # Deine Höhe über NNTemperatur-Offset (SCD41 Eigenerwärmung):
filters:
- offset: -3.0 # Ggf. anpassen nach Vergleichsmessungesphome run co2_ampel_scd41.yamlOder über das ESPHome Dashboard in Home Assistant.
Nach dem Flashen erscheint das Gerät automatisch in Home Assistant. Folgende Entities werden erstellt:
| Entity | Typ | Beschreibung |
|---|---|---|
sensor.co2_ampel_co2 |
Sensor | CO2 in ppm |
sensor.co2_ampel_temperatur |
Sensor | Temperatur in °C |
sensor.co2_ampel_luftfeuchtigkeit |
Sensor | Relative Luftfeuchtigkeit in % |
text_sensor.co2_ampel_luftqualitaet |
Text | Bewertung als Text |
light.co2_ampel_led |
Light | NeoPixel LED (manuell steuerbar) |
switch.co2_ampel_led_aktiv |
Switch | LED-Automatik ein/aus |
button.co2_ampel_neustart |
Button | ESP32 Neustart |
button.co2_ampel_co2_kalibrierung_frc |
Button | Manuelle Kalibrierung |
Ist standardmäßig aktiviert. Der SCD41 kalibriert sich automatisch, wenn er regelmäßig frischer Außenluft ausgesetzt wird (z.B. bei gelüftetem Raum). Braucht ca. 7 Tage für die erste Kalibrierung.
- Sensor mindestens 3 Minuten bei Frischluft (Fenster weit auf) laufen lassen
- In Home Assistant den Button "CO2 Ampel CO2 Kalibrierung (FRC)" drücken
- Der Sensor wird auf 420 ppm kalibriert (aktueller Außenluft-Referenzwert)
⚠️ Achtung: Falsche Kalibrierung führt zu ungenauen Werten! Im Zweifel ASC nutzen.
Für SK6812 (RGBW) den type ändern:
light:
- platform: neopixelbus
type: GRBW
variant: SK6812Einfach num_leds in den Substitutions anpassen:
substitutions:
num_leds: "16" # z.B. für einen 16er NeoPixel RingDie relevanten Blöcke (i2c, sensor: scd4x, light: neopixelbus, script, interval) können in jede bestehende ESPHome-YAML übernommen werden. Auf Pin-Konflikte achten!
| Problem | Lösung |
|---|---|
| SCD41 wird nicht gefunden | I²C-Scan im Log prüfen, Verkabelung checken (Adresse: 0x62) |
| CO2-Werte unrealistisch hoch/niedrig | Manuelle Kalibrierung bei Frischluft durchführen |
| LEDs zeigen falsche Farben | type: GRB ggf. auf RGB ändern (je nach LED-Typ) |
| LEDs flackern | 330Ω Widerstand am Datenpin, Kondensator an VCC/GND |
| Temperatur zu hoch | offset im Temperatur-Filter anpassen |
| ESP32 bootet nicht | GPIO16 ist safe, aber GPIO12 (Strapping Pin) vermeiden |
Pull Requests und Issues sind willkommen!
- Fork erstellen
- Feature-Branch anlegen (
git checkout -b feature/mein-feature) - Änderungen committen (
git commit -m 'feat: Beschreibung') - Branch pushen (
git push origin feature/mein-feature) - Pull Request erstellen
Dieses Projekt steht unter der MIT-Lizenz.
💡 Mehr lernen: Dieses Projekt ist Teil des ESPHome Meisterkurs Ökosystems – der praxisnahe Kurs, mit dem du lokale Smart-Home-Geräte mit ESP32 und Home Assistant baust.

