Skip to content

Monitoring CO2 with ESPHome

I have done several posts about my smart home, and in particular about my instance of Home Assistant. In an effort to continuously improve this system, I have recently added CO2 level monitoring with a Pi Pico and an SCD30 CO2 sensor, which also monitors temperature and humidity.

I've been keen to see how the levels of CO2 affect my concentration and the stuffiness of the room, and with a big gauge in Home Assistant, my previously uncaring self can slowly start to become obsessed with the air quality in the office.

The Sensor

The SCD30 is an I2C sensor, with a built in temperature and humidity monitor, with self-calibration. The sensor comes in a small package and some small headers, which have to be soldered on. I've also got a bit of a problem with the damp at home, so having built in temperature and humidity monitoring was a plus, even if it isn't that precise.

The Firmware

I've always been a big believer in ESPHome and this project was no exception. It integrates well with the Pi Pico W and makes sensor configuration headaches a thing of the past. It's a declarative config file, where you specify the sensor you need, how to find it, what you want it to show Home Assistant, and you're off to the races.

My YAML config file
esphome:
  name: breathing-plant
  friendly_name: "Breathing Plant"

rp2040:
  board: rpipicow

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "[snip]"

ota:
  - platform: esphome
    password: "[snip]"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  domain: .iot.chza.me
  fast_connect: True

  # Enable fallback hotspot in case wifi connection fails
  ap:
    ssid: "Chaziot Fallback Hotspot"
    password: "[snip]"

i2c:
  sda: GPIO8
  scl: GPIO9

sensor:
  - platform: scd30
    co2:
      name: "CO2"
      accuracy_decimals: 1
    temperature:
      name: "Temperature"
      accuracy_decimals: 2
    humidity:
      name: "Humidity"
      accuracy_decimals: 1
    address: 0x61
    update_interval: 5s

light:
  - platform: rp2040_pio_led_strip
    name: led_strip
    id: led_strip
    pin: GPIO28
    num_leds: 1
    pio: 0
    rgb_order: GRB
    chipset: WS2812B

For this project, I simply needed to specify the SDA and SCL lines, connect VBUS and GND, then I could be on my way. For prototyping, I was using a Cytron Maker Pi Pico, which includes a WS2812 (more commonly known as NeoPixel) LED. As it was on the board and pre-connected to a GPIO PIN, why not expose it?

Running and Flashing with ESPHome

I run Home Assistant as a Docker container on running-lion, so I can't install ESPHome through the add-on store. To install, I simply amended the docker-compose.yaml file already in place for Home Assistant, to create a container for ESPHome.

docker-compose.yaml file
version: '3'
services:
  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - ./config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
    ports:
      - "127.0.0.1:8180:8180"
      - "127.0.0.1:8123:8123"
    restart: unless-stopped
    privileged: true
      #network_mode: host
    networks:
      - hass

  # Automatic Updates
  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: homeassistant esphome --interval 30

  # ESPHome Web UI
  # TODO: setup TLS/DNS record
  esphome:
    container_name: esphome
    image: ghcr.io/esphome/esphome
    volumes:
      - ./esphomeconfig:/config
      - /etc/localtime:/etc/localtime:ro
    restart: always
    privileged: true
    ports:
      - 0.0.0.0:6052:6052
    environment:
      - USERNAME=charlie
      - PASSWORD=[snip]

networks:
  hass:
    driver: bridge
    ipam:
      config:
        - subnet: 10.5.0.0/24
          gateway: 10.5.0.1

Theoretically, I could then flash my Pi Pico W through the web interface, but even generating the file to manually copy across was proving difficult. Eventually, I discovered the problem was that the underlying PlatformIO couldn't work properly with an NFS-backed filesystem that used locks. They did point this out to me on the installation page, but as always, I ignored this and ploughed on anyway, just to find that indeed, this did apply to me.

A quick edit to /etc/fstab to add nolock to the mount, and I was off to the races. From there, the web interface took care of compilation, and as if by magic a couple of seconds after copying across, the Pi came to life on the network.

Home Assistant

In the dashboard, it is then as easy as making a couple of cards. I track the current concentration as a gauge, then have a graph of the historical concentration and temperature.

Gauge and histories on the dashboard.

I toyed around with setting the NeoPixel LED to red, yellow, or green, depending on the gauge colour, but to get a more representative reading, I settled on leaving it in the other corner of the room, where I wouldn't be able to see the LED anyway.

The Data

It was a bit scary the first couple of days, seeing levels spike way above the maximum safe threshold. A typical office should be at between 400-1000ppm, with anything over 1000ppm being considered detrimental to productivity and causing drowsiness. At 2000ppm, you start to get headaches, sleepiness, and stale air.

Overnight, the room spiked to 3200ppm, but that was with the door shut and window closed. Leaving the room empty for an hour or so brought the value down to 1700ppm, then opening the window whilst working brought it down to around 580ppm. Outdoor air should have roughtly 400ppm, so a well ventilated room shouldn't be too far from this.

I can also use this data to see whether the damp is going to be an issue, as somewhere around 60-70% RH mould can start to form, so I can at least monitor and reduce this with a dehumidifier and opening the window where possible.

Conclusion

For a project that all in could probably be done for less than £20, this seems like an easy thing to get out of the way and start fleshing out your Home Assistant dashboard with. It might be more expensive than a standalone humidity monitor with a screen, but at least it collects the data so you can start to pick up the long term trends, and, unlike most humidity sensors it also tells you how much CO2 is in the air around you.