ESP32-C6 Indoor Air Quality Monitor: Build a CO2, VOC & Matter-Ready Sensor (2026 Guide)

ESP32-C6 Indoor Air Quality Monitor: Build a CO2, VOC & Matter-Ready Sensor (2026 Guide)

I didn't plan to build this one. It started because my home office kept feeling stuffy by 3 PM, and I got tired of guessing whether it was the CO2 building up or just my own attention span giving out. So I did what I usually do — grabbed an ESP32-C6, ordered two sensors, and turned a mildly annoying problem into a weekend project.

What came out of it is a small, wall-mountable box that reads CO2, temperature, humidity, and barometric pressure, and reports all of it straight into Matter — which means it shows up natively in Apple Home, Google Home, Home Assistant, or Amazon Alexa without a single third-party bridge or cloud app. If you've already built the Matter energy monitor from a previous guide on this site, this project reuses a lot of the same Matter plumbing, so it'll feel familiar.

Why Indoor Air Quality Is Worth Building For

CO2 concentration indoors climbs fast in a closed room — a poorly ventilated bedroom or meeting room can push well past 1,000 ppm within an hour or two, and several building-science studies have linked CO2 levels in that range to measurably slower decision-making and reduced attention. You don't need a lab to notice it; you just need a sensor sitting on your desk telling you when to crack a window.

The other reason this project is worth your bench time in 2026 specifically: Matter's smart-home ecosystem keeps expanding its energy and environmental device categories, and air quality sensors are now a first-class Matter device type — not a workaround. That means the project you build this weekend will still talk natively to whatever smart-home hub you're using two years from now.

Why the ESP32-C6 Is the Right Board for This

The ESP32-C6 is the obvious pick here for a few concrete reasons:

  • 2.4 GHz Wi-Fi 6 for the Matter-over-Wi-Fi commissioning path
  • Bluetooth 5 (LE) for the initial device pairing / QR-code commissioning flow
  • 802.15.4 radio, so the same board can join a Thread network instead of Wi-Fi if you'd rather keep it on a low-power mesh
  • Native support in Espressif's esp-matter SDK, so you're not fighting the framework

If you only have an ESP32-S3 or classic ESP32 on hand, the sensor wiring and reading code below will still work — you'll just fall back to reporting values over Wi-Fi/MQTT into Home Assistant via ESPHome instead of joining a Matter fabric directly, since the classic ESP32 lacks the 802.15.4 radio and needs an external Thread border router for the full Matter-over-Thread path.

Parts List

ComponentPurposeNotes
ESP32-C6-DevKitC-1Main controllerN8 (8MB flash) variant is enough
Sensirion SCD41CO2, temperature, humidityPhotoacoustic NDIR-class sensor, I2C
Bosch BME280Temperature, humidity, barometric pressureI2C, used to cross-check and improve calibration
SSD1306 128×64 OLED (optional)Local readoutI2C, handy so the box is useful even offline
2× 4.7kΩ resistorsI2C pull-ups (if your breakout boards don't already include them)Skip if your modules have onboard pull-ups
USB-C cable + 5V supplyPower
Small enclosure with vent slotsHousingSealed boxes will trap CO2 and give you false highs

Total cost typically lands in the $25–$40 range depending on where you source the SCD41, which is the priciest part of the bill of materials.

Wiring It Up

All three modules — the SCD41, the BME280, and the optional OLED — share the same I2C bus, so wiring is simple:

  • SCD41 SDA / BME280 SDA / OLED SDA → GPIO 6 (or any free GPIO you assign as SDA)
  • SCD41 SCL / BME280 SCL / OLED SCL → GPIO 7
  • All VCC → 3.3V (do not run these sensors at 5V logic)
  • All GND → GND

Default I2C addresses: SCD41 is 0x62, BME280 is typically 0x76 or 0x77 depending on the breakout's SDO pin state, and a standard SSD1306 module is usually 0x3C. Since all three addresses are different, you can run everything on one bus without an address conflict.

Reading the Sensors (ESP-IDF)

Here's a trimmed-down version of the sensor read loop. This isn't the full Matter integration — just the core I2C polling logic you'll build the rest of the firmware around:

c
#include "driver/i2c.h"
#include "esp_log.h"

#define I2C_PORT        I2C_NUM_0
#define SCD41_ADDR      0x62
#define BME280_ADDR     0x76

static const char *TAG = "iaq_sensor";

// Trigger a periodic measurement on the SCD41
esp_err_t scd41_start_measurement(void) {
    uint8_t cmd[2] = {0x21, 0xB1}; // start_periodic_measurement
    return i2c_master_write_to_device(I2C_PORT, SCD41_ADDR, cmd, sizeof(cmd),
                                       pdMS_TO_TICKS(100));
}

// Read CO2 (ppm), temperature (°C), and humidity (%RH) from the SCD41
esp_err_t scd41_read_measurement(uint16_t *co2_ppm, float *temp_c, float *rh) {
    uint8_t cmd[2] = {0xEC, 0x05}; // read_measurement
    uint8_t data[9];

    esp_err_t err = i2c_master_write_to_device(I2C_PORT, SCD41_ADDR, cmd, sizeof(cmd),
                                                pdMS_TO_TICKS(100));
    if (err != ESP_OK) return err;

    vTaskDelay(pdMS_TO_TICKS(1));

    err = i2c_master_read_from_device(I2C_PORT, SCD41_ADDR, data, sizeof(data),
                                       pdMS_TO_TICKS(100));
    if (err != ESP_OK) return err;

    *co2_ppm = (data[0] << 8) | data[1];
    uint16_t raw_temp = (data[3] << 8) | data[4];
    uint16_t raw_rh   = (data[6] << 8) | data[7];

    *temp_c = -45.0f + 175.0f * ((float)raw_temp / 65535.0f);
    *rh     = 100.0f * ((float)raw_rh / 65535.0f);

    return ESP_OK;
}

Call scd41_start_measurement() once at boot, then poll scd41_read_measurement() roughly every 5 seconds — that matches the SCD41's own internal update rate, so polling faster just wastes cycles. Pair this with your BME280 driver of choice (Bosch's BME280 API or any of the maintained ESP-IDF component ports) to pull pressure and get a second, cross-checked humidity/temperature reading.

Wiring It Into Matter

Once you're reading real numbers over I2C, the Matter side is where this project actually pays off. Espressif's esp-matter SDK ships an air-quality-sensor device type out of the box, so instead of inventing your own protocol, you attach your sensor readings to the standard Matter clusters:

  • Carbon Dioxide Concentration Measurement cluster → feed it your SCD41 ppm reading
  • Temperature Measurement cluster → feed it either sensor's temperature value
  • Relative Humidity Measurement cluster → same idea
  • Air Quality cluster → derive a simple Good/Fair/Poor enum from your CO2 thresholds (under 800 ppm is generally considered good, 800–1200 fair, above 1200 poor) and report that as the summarized air-quality state

The practical workflow: start from Espressif's light example under the ESP-Matter framework (it already has the boilerplate for commissioning, fabric management, and the QR-code pairing flow), strip out the on/off cluster, and add the four clusters above with your own attribute-update calls inside your sensor read loop. Once flashed, you commission the device the same way you'd commission any Matter accessory — scan the QR code from your phone's Home app, and it joins your existing fabric.

Calibration Notes That Actually Matter

A few things that will save you a frustrating debugging session:

  • Give the SCD41 fresh air on first boot. It uses automatic self-calibration (ASC) that assumes the lowest reading it sees over a rolling multi-day window is roughly outdoor-level CO2 (~420 ppm). If you power it on inside a closed room and never open a window, your baseline will drift high.
  • Don't seal the enclosure. This is the single most common mistake in these builds — a "clean-looking" fully sealed 3D-printed box traps exhaled CO2 right next to the sensor and reports numbers that don't reflect the room.
  • Separate it from heat sources. The ESP32-C6 itself gives off a small amount of heat; keep at least a few centimeters of clearance around the BME280 so your temperature reading isn't just measuring your own PCB.

Where to Go From Here

If the CO2/temperature/humidity/pressure combo isn't enough and you want particulate matter (PM1.0/PM2.5/PM10) in the mix too, the natural upgrade path is swapping the SCD41 for a Sensirion SEN66 or SEN55, which report CO2, VOC, NOx, and particulates from a single I2C module — same wiring pattern, same Matter cluster approach, just more clusters to populate. It's a meaningfully pricier sensor, so it's worth starting with the SCD41 build above and upgrading only if you actually need particulate data.

FAQ

Does this work with Home Assistant instead of Matter? Yes — Home Assistant speaks Matter natively through its Matter integration, so a Matter-commissioned device from this build shows up automatically. If you'd rather skip Matter entirely, you can flash the same sensor logic with ESPHome and expose it over the native Home Assistant API or MQTT instead.

What CO2 level should trigger an alert? Most air-quality guidance treats readings under 800 ppm as good, 800–1,200 ppm as a sign to ventilate, and anything consistently above 1,200 ppm as poor and worth investigating (more fresh air, checking HVAC filters, or reducing room occupancy).

Can I use the classic ESP32 instead of the ESP32-C6? The I2C sensor code is identical. What you lose without the C6's 802.15.4 radio is direct Matter-over-Thread; you'd instead run Matter-over-Wi-Fi (which classic ESP32 can still do) or fall back to ESPHome/MQTT for smart-home integration.

How accurate is the SCD41 out of the box? Sensirion specifies it for indoor air-quality use with a measurement range up to 5,000 ppm, and accuracy is good enough for practical ventilation decisions once auto-calibration has run through a few days of normal use — it's not a lab-grade reference instrument, but it's well beyond what a cheap MQ-series gas sensor can give you.


If you're building projects like this one and want to know how a hobby build like an ESP32 sensor project can actually turn into income — not just a fun weekend box — it's worth reading through Pet Life Hacks, which breaks down practical ways to start earning online. It's outside the electronics niche, but it's a solid, straightforward read if that's a goal for you too.

Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.