Build a CO2 monitor with an SCD40 and ESPHome
A closed bedroom with two people in it can pass 2,000 ppm of CO2 by morning, enough to leave you groggy. A Sensirion SCD40 or SCD41 sensor on an ESP32 measures it accurately and lets Home Assistant turn on ventilation before the air gets stale.
Why a real CO2 sensor matters
Many cheap "air quality" sensors report an estimated CO2 (eCO2) calculated from other gases. They're rough guesses. The SCD40 and SCD41 measure CO2 directly using a photoacoustic method, and they also report temperature and humidity.
- SCD40: specified for 400–2,000 ppm, which covers normal indoor air.
- SCD41: specified up to 5,000 ppm and supports low-power single-shot readings. Worth the extra cost for workshops, grow tents or battery builds.
Parts
- An ESP32 DevKit and USB power supply.
- An SCD40 or SCD41 breakout board (the bare sensor is tiny; buy it on a board with pins).
- A case with vent holes. Air has to reach the sensor.
Wiring
The sensor uses I2C, so it's four wires:
| SCD40 pin | ESP32 pin |
|---|---|
| VIN / VDD | 3.3V |
| GND | GND |
| SDA | GPIO21 |
| SCL | GPIO22 |
The ESPHome config
Add this to an ESP32 config (the config generator makes the base):
i2c:
sda: GPIO21
scl: GPIO22
sensor:
- platform: scd4x
co2:
name: "CO2"
temperature:
name: "Temperature"
humidity:
name: "Humidity"
automatic_self_calibration: true
altitude_compensation: 200m
update_interval: 60s
Set altitude_compensation to your home's elevation. CO2 readings depend on air pressure, and the default assumes sea level.
Calibration
With automatic_self_calibration on, the sensor assumes the lowest reading it sees over about a week is fresh outdoor air (roughly 420 ppm) and adjusts itself. That works well as long as the room gets aired out at some point each week — open a window for a few minutes now and then. In a space that's never ventilated, such as a sealed grow tent, turn it off.
Placement
- At breathing height, 1–1.5 m off the floor.
- Not right next to where someone sits or sleeps. Your breath will spike the reading.
- Away from windows and supply vents, which measure the incoming air instead of the room.
- Keep the ESP32 out of the sensor's airflow if you can. The board warms up, which skews the temperature reading. A short cable between them helps.
What the numbers mean
| CO2 (ppm) | What it usually means |
|---|---|
| ~420 | Outdoor air. |
| 400–800 | Well-ventilated room. |
| 800–1,000 | Getting stuffy. A common target to stay under. |
| 1,000–2,000 | Stale. Many people notice drowsiness and trouble concentrating. |
| Above 2,000 | Ventilate now. |
Automations
- Ventilate automatically. Turn on a bathroom fan, an ERV or the furnace fan when CO2 stays above 1,000 ppm for 10 minutes, and off below 800. The gap between the two numbers stops it from cycling on and off.
- Bedroom nudge. If you sleep with the door closed, a morning summary of the night's peak CO2 makes the case for leaving it cracked open.
- Room-by-room zoning. CO2 is a strong signal that a room is occupied, which makes it useful alongside presence sensors in an HVAC zoning plan.