carbo - co2 meter from scratch

Introduction

As someone who works indoors for long periods of time, the emerging CO2 awareness of these past years has been interesting to follow. I frequently try to air out my workspace, but that is not always possible.

The industry standard of the CO2 meters at the moment is the aranet 4 with a steep price tag of ~200 euro. Which seemed to be a bit too much. After some discussions with a friend we decided to try making our own from scratch.

Which was perfect for me since my exposure to embedded development was limited to making an LED blink with an arduino years ago. So naturally this was the perfect opportunity to see what that area of software development is all about and dive a little deeper. Full code is available on github.

Hardware

There are a few different CO2 sensors out there, all with their own pros and cons but we ended up choosing the SCD41. This sensor reads CO2, temperature and humidity every 5 seconds which we bumped to 15 seconds to save on resources. Paired with an ESP32 microcontroller board. I chose to use an eink display, while my friend opted for an LCD screen. This was especially interesting so we could really see the differences in code and real life. We chose a modular approach for our shared codebase to easily switch between these two types of displays.

eink screen lcd screen

First implementation

We first utilized the ESP32 as the all-in-one solution, having a dashboard with latest values and a 24h chart running on the device itself. Getting the latest values with websockets. This was pretty cool but the drawback was that you’d have to know the device’s IP address before being able to visit the dashboard and viewing the values. And because of the limited RAM of the ESP, we’d only be able to store ~24h of CO2 values with an average of 5 minutes.

first implementation of the webpage

Going fullstack

So we continued development and chose to implement a universal backend, written in NodeJS paired with an InfluxDB database. We put the setup in a docker compose config and selfhosted it.

services:
  backend:
    build:
      context: .
    restart: always
    env_file: .env
    environment:
      - NODE_ENV=production
    ports:
      - "3000:3000"
  influxdb:
    image: influxdb:latest
    volumes:
      - ./influxdb/data:/var/lib/influxdb2:rw
    env_file: .env
    ports:
      - 8086:8086
    restart: unless-stopped

This was the perfect solution for us since we can now visit the built-in influxdb webpage and query precisely our data

influxdb dashboard example

Bill of materials (BOM)

For the eink setup the bill of materials ends up as:

NameDescriptionPrice (€)
SCD41CO2 sensor20
einkE-Ink display5
ESP32Microcontroller3
Wireswires to connect everything3
Total31