DIY Environmental Monitor

Early on in the pandemic, there was a good amount of discussion on Twitter about indoor CO2 levels as more people were spending time exclusively at home, often in a single, small room for hours on end. Since I was one of those people spending nearly the entire day in a single room, I decided to look around for a CO2 monitoring system. While a simple “alert after levels rise above x ppm” is sufficient, I was really looking for one that would be able to log data to a remote system so that I could monitor it throughout the day and/or look back on historical data from any computer. After being thoroughly disappointed with what was on Amazon (nothing at a reasonable price point seemed to be able to send data to a remote server), I decided it would be a nice little project to build my own.


Looking around on Adafruit, I settled on the SCD-30 - a combined CO2, temperature, and humidity sensor. As the Adafruit page said, this is a NDIR sensor so while it’s not the cheapest ($59), it’s actually measuring the CO2 in the air instead of approximating it from the concentration of volatile organic compounds (VOCs).

As for the “brains” of the device, I went with an ESP32-based development board. This gave me nice headers for all of the pins I’d need to get at (much like an Arduino or Raspberry Pi), but also includes WiFi out of the box so I could put it basically anywhere and not have to figure out how to get network to it.

I was also sure to grab the requisite cable to connect the sensor to the dev board.

Finally, as a last-minute addition, I grabbed a cheap light sensor (the VEML7700) since I figured that would also be fun to have logged. From the diagrams, it looked like it could be stacked directly on top of the SCD-30 with just some headers connecting the I2C and power pins, requiring no other changes.


With everything in hand, the assembly was simple. Just as planned, I was able to solder the 0.1” headers included with the light sensor to connect GND and the I2C SCL/SCA pins between the SCD-30 and the VEML7700.

But where does power for the VEML come from you may ask? Well it’s a hack but the VEML board schematic shows that the 3.3V “output” pin is a direct connection to the 3.3V “plane” of that board (including Vin for the sensor itself), so in theory it’s safe to feed 3.3V from the SCD-30 into the VEML7700s 3.3V “out” and ignore the voltage regulator on the VEML entirely. With that also bridged with a header, all that was left was to connect the STEMMA cable from the SCD-30 to the ESP32 board and start writing code.


After grabbing libraries and sample code from Adafruit for each of the sensors to make sure they were working, getting a basic “logger” working over serial output was trivial. As I mentioned above, the ESP32 has WiFi built in though, so I decided to use that to connect to an InfluxDB instance (just on Influx’s free cloud plan right now) and log everything there. Some more munging of sample code later, and I had basically the entire thing ready to go.

After getting it taped down on the side of a shelf (that should expose it to only indirect light for more accurate measurement) and let it run for a night.

The monitor all put together

However, when I checked the data the next day I found the readings were pretty far off.

At this point it was still July so I had the windows open most of the day which meant both that the temperature should be almost identical to what’s measured outside, and that the CO2 concentration should be ~400ppm. The temperature I was logging was nearly 2.5degC (~4.5degF) too high, and due to how the NDIR sensor works, that discrepancy was also affecting the CO2 reading. It looks like I’m not the only one with this issue, but either way the fix was quick - there’s a built-in temperature offset value that can be set from the ESP32.

Even after that was changed, the CO2 reading was still a bit off so I made one last change to the firmware adding a simple HTTP server. By hitting a specific route, I could remotely force a recalibration of the SCD-30 (basically on recalibration, the sensoe assumes it’s measuring ambient outside air with a CO2 concentration of 400ppm and adjusts its internal offset as appropriate).


The materials list and the full source for the firmware is available at