vd Brink Home Automations

Home automations with Home Assistant, ESPHome, Node-RED and more tech

ESPHome CO2 sensor

Based on the SCD40 sensor

Introduction

SCD40

Affordable CO2 sensors do exist, but many of them are expensive. You can also build one yourself with just a single sensor and an ESP board. It is easy, practical, and a nice project to do yourself.
You can even build this one without soldering.

Besides CO2, this sensor also measures the temperature and humidity.

ESPHome device sensors the CO2, temperature and humidity sensors in Home Assistant
Home Assistant mini-graph-card CO2 CO2 history on a dashboard with the mini-graph-card


CO2 stands for carbon dioxide and is measured in parts per million (ppm).

If you are in a room with a ppm level that is too high, you can feel tired, start yawning, and get a headache. For your health and your family’s health, it is important to act when these values are too high.
The value rises gradually, so you do not notice it directly. That’s why it’s important to get notified about it.

The baseline value is what you measure outside and is around 400 ppm.

This table shows which action is required for each value.

ppm condition action
400 - 800 good no action is required
800 - 1200 medium open a window
1200+ bad limit has reached, open all windows


In Home Assistant, you can visualize how the CO2 value changes over time. With an automation, you can get notified when the value is too high. Send the notification to your phone or make an announcement via a smart speaker.

CO2 history graph in Home Assistant

Read on to see how you can build this sensor yourself.

TIP: If you prefer a ready-to-go CO2 sensor, you can also choose the Netatmo Healthy Home Coach. It is an all-in-one solution with CO2, decibel, temperature, and humidity sensors. You can find it here: (Amazon)


My solution

This project is one of the easiest ways to get started with ESPHome.
No soldering is required.
You only need to connect the wires to the ESP and flash the configuration to it.

hardware connected the test setup, sensor connected to the ESP

Click on the photo to open it fullscreen.

These photos show how it works. For the finishing touch, put the sensor in a case with holes. It looks better, and the holes let enough air reach the CO2 sensor.

CO2 sensor in a case with holes the finished sensor in a case with holes, so air can reach the sensor

The finished sensor is compact, practical, and easy to place anywhere in the room. Everything fits inside this small case: the SCD40 sensor and an ESP board.


Table of Contents


Required hardware

These are the hardware components I used for this project.

NOTE: Affiliate links are used here. Same price, and you support this blog.

The required products, including the soldering version, are also bundled on these Amazon and Amazon NL pages.

SCD40 CO2 sensor

This small gas sensor measures CO2 (in a range of 400 to 2000 ppm), temperature (-10 to 60 degrees), and humidity (0 to 100 percent).
The SCD40 datasheet has all the technical details for this sensor.

NOTE: The SCD41 can also be used here; the pins are the same. The difference between these two models is their range and power consumption. The SCD41 can reach up to 5000 ppm, compared to 2000 ppm for the SCD40, and it uses less power when you measure periodically every 5 minutes instead of continuously.

CO2 sensors with or without soldering:

GY-SCD40 CO2 sensor with cable
SCD40 · No solderingGY-SCD40 CO2 sensor with cable

This sensor with an I2C interface is useful if you do not want to solder.

SCD40 CO2 sensor with pins
SCD40 · Soldering requiredSCD40 CO2 sensor with pins

The cheaper version where you need to solder the pins.

ESP8266 NodeMCU v3 (CH340)
ESP boardESP8266 NodeMCU v3 (CH340)

With pre-soldered pins. You can use any ESP board, like an ESP32 or ESP D1 mini, but on this page I use an ESP8266.

Plastic DIY case
CasePlastic DIY case

A case with holes is the finishing touch: it looks better than the bare hardware. Almost any object with holes works, minimum 7 cm long, 3 cm wide and 3 cm high, as long as enough air can reach the sensor. A plastic box with drilled holes or a porous decorative statuette works too.

Micro USB to USB-A cable
PowerMicro USB to USB-A cable

A cable to power the ESP.

5V USB EU power adapter
Power5V USB EU power adapter

A power adapter to power the ESP.


Found a dead link? Please inform me or look at ESPHome DIY sensors - Best Buy Tips for alternative links.


Connect the hardware

I have made a diagram showing how to connect the GY-SCD40 to the ESP.
This sensor uses an I2C bus interface to connect to the ESP. That also makes it possible to connect multiple sensors to the same bus.
The ESP has predefined pins for SDA (data) and SDL (clock).

ESP pin GPIO esp8266 pin SCD40 pin color
D2 GPIO4 SDA (data) white
D1 GPIO5 SDL (clock) yellow
G GND GND black
3V 3 V VCC red

Connect the SCD40 to the ESP

Connect the four wires directly to the ESP8266 NodeMCU like this.

hardware connected

Click on the photo to open it in fullscreen.

NOTE: This photo only shows how it works. For the finishing touch, put it in a case with holes.

Put it in a case

Place the sensor and the ESP together so they fit in a box.

You need to have holes in the box so the USB power cable can go through it and enough air to measure the CO2 value.

sensor and ESP fit in the case example of a sensor and ESP that fit in the case (here with a SenseAir S8 sensor)


Required software

ESPHome

ESPHome

The only software you need is ESPHome.

This software is used to flash the configuration file to the ESP board.

Their website contains a lot of information about how to configure and calibrate all kinds of sensors.
The relevant pages for this project are:

Flash the script

There are many ways to flash the configuration file with ESPHome to the board.
Read how to flash ESPHome to upload it.

Connect the ESP via USB to the computer.

One easy way is to run it from the command line:

esphome “co2_scd40.yaml” run

Python Flash output

The co2_scd40.yaml YAML script:

# Source code by vdbrink.github.io
# ESPHome configuration for the SCD40 CO2 sensor
esphome:
  name: espscd40
  comment: Room CO2 sensor
  platform: ESP8266
  board: nodemcuv2

wifi:
  ssid: "XXX"
  password: "XXX"
  fast_connect: true # only needed for hidden SSID

# Push the data also to MQTT topics:
# * CO2 espscd40/sensor/scd40_co2/state
# * Temperature espscd40/sensor/co2_temperature/state
# * Humidity espscd40/sensor/co2_humidity/state
mqtt:
  broker: 192.168.XXX.XXX
  port: 1883
  username: "XXXX"
  password: "XXXX"

# Enable logging to the console
logger:

# Home Assistant integration
api:

# Pins on an esp8266
i2c:
  sda: 4 # pin 21 for an ESP32
  scl: 5 # pin 22 for an ESP32

# Sensors definitions
sensor:
  - platform: scd4x
    co2:
      name: "SCD40 CO2"
    temperature:
      name: "CO2 temperature"
    humidity:
      name: "CO2 humidity"

Test if it works

Now that the data is flashed, check whether the sensor pushes the CO2, temperature, and humidity data correctly.

Via console

If you flash the ESP via the console and enable logging, you will see the readings in the console immediately after the ESP is flashed.

Logging cmd output

Via Home Assistant

Once the ESP is online, it automatically registers itself in Home Assistant if you installed ESPHome.

You will see a new device with three entities: CO2, temperature, and humidity.

New ESPHome device

ESPHome device sensors

Via MQTT

If you enabled MQTT in the config, you can also check for incoming messages. On Windows, you can use the MQTT Explorer application. The sensor sends its data to the topics espscd40/sensor/scd40_co2/state, espscd40/sensor/co2_temperature/state, and espscd40/sensor/co2_humidity/state.

MQTT data


Home Assistant Dashboards

Now that the data is available in Home Assistant, we can create dashboard elements to visualize it.

Dashboard Gauge

In a gauge, you can immediately see whether the current CO2 value is in a healthy range. I used different colors to show how serious the condition is, based on the values in the Introduction.

Home Assistant Gauge

# Source code by vdbrink.github.io
# Gauge card with the room CO2 value
# Dashboard card code
type: gauge
severity:
  green: 400
  yellow: 800
  red: 1200
entity: sensor.scd40_co2_value
min: 350
max: 1500
name: Room CO2 sensor

Dashboard Line Graphic

To show the history of the last X hours, you can use the line graph card. It’s a default presentation available in Home Assistant.

Home Assistant line graph

# Source code by vdbrink.github.io
# Default history graph card with the CO2 value
# Dashboard card code
type: sensor
graph: line
entity: sensor.scd40_co2_value
name: Room CO2 sensor
hours_to_show: 6

Dashboard Mini Graph Card

With a Mini Graph Card you can create a color gradient graph line with the history of the CO2 readings.

With a fixed sensor.co2_value_800 sensor I defined a visual threshold line for myself. You can create it as described in Dashboard Graphic with levels.

This card requires the HACS integration mini-graph-card. Install this integration via this button
Open your Home Assistant instance and show the app store.

Home Assistant mini-graph-card CO2

# Source code by vdbrink.github.io
# Mini-graph card with CO2 value
# Dashboard card code
type: custom:mini-graph-card
entities:
  - entity: sensor.scd40_co2_value
  - entity: sensor.co2_value_800
    show_fill: false
    color: "#999999"
    show_legend: false
show:
  labels: true
hours_to_show: 12
points_per_hour: 24
height: 150
color_thresholds:
  - value: 400
    color: "#008000"
  - value: 800
    color: "#0000FF"
  - value: 1000
    color: "#f39c12"
  - value: 1200
    color: "#d35400"
  - value: 2000
    color: "#c0392b"

Dashboard Graphic

To show the history of the last 6 hours, you can use the history graph card, or a line card for a simpler version.

CO2 value for the last 6 hours

The corresponding dashboard YAML code:

# Source code by vdbrink.github.io
# History graph card with the CO2 value of the last 6 hours
# Dashboard card code
type: history-graph
entities:
  - entity: sensor.scd40_co2_value
hours_to_show: 6

Dashboard Graphic with levels

Home Assistant History Graph

You can also show baseline values in the graph by creating a few extra custom sensors with fixed values.
This makes it easy to see at a glance whether the current value is still in range.

# Source code by vdbrink.github.io
# History graph card with the CO2 valueand threshold lines
# Dashboard card code
type: history-graph
entities:
  - entity: sensor.scd40_co2_value
  - entity: sensor.co2_value_800
  - entity: sensor.co2_value_1200
  - entity: sensor.co2_value_1500
hours_to_show: 24

This is how you create three custom lines in the graph to show the threshold values. Add this section to your configuration.yaml to create the three static helper sensors:


# Source code by vdbrink.github.io
# Three static template sensors (800, 1200 and 1500 ppm)
# configuration.yaml
template:
  - sensor:
      - name: "Co2 value 800"
        unique_id: co2_value_800
        state: 800
        unit_of_measurement: 'ppm'
      - name: "Co2 value 1200"
        unique_id: co2_value_1200
        state: 1200
        unit_of_measurement: 'ppm'
      - name: "Co2 value 1500"
        unique_id: co2_value_1500
        state: 1500
        unit_of_measurement: 'ppm'

Dashboard condition text

Home Assistant conditional CO2 text

This creates a new sensor that shows a text representation of the current condition.


# Source code by vdbrink.github.io
# Template sensor with a text for the current CO2 condition
# configuration.yaml
template:
  - sensor:
      - name: "Scd40 co2 value text"
        unique_id: scd40_co2_value_text
        icon: "mdi:molecule-co2"
        state: >-
          {% set state = states('sensor.scd40_co2_value') | int %}
          {% if state < 800 %}good
          {% elif state > 800 and state <= 1200 %}medium
          {% elif state > 1200 and state <= 1500 %}bad
          {% elif state > 1500%}very bad
          {% else %}unknown{% endif %}

Dashboard bad condition text

In my dashboard, I have a section for important messages. You only see it when action is needed. There is also a message when the CO2 value is not good. You can build this section with conditional entities.


# Source code by vdbrink.github.io
# Conditional row, only visible when the CO2 condition is not good
# Dashboard card code
type: entities
entities:
- type: conditional
  conditions:
    - entity: sensor.scd40_co2_value_text
      state_not: good
      row:
        entity: sensor.scd40_co2_value_text

Dashboard Mushroom entity

mushroom chips Show a green icon without any text if the level is up to 800 ppm, yellow up to 1200 ppm, orange up to 1500 ppm, and red above that.


# Source code by vdbrink.github.io
# Mushroom chips card with a CO2 icon
# Dashboard card code
type: custom:mushroom-chips-card
chips:
  - chip: null
    type: template
    icon: mdi:molecule-co2
    entity: sensor.scd40_co2_value
    content: ''
    icon_color: |-
      {% if is_state('sensor.scd40_co2_value', 'unavailable') %}
         blue
      {% elif states('sensor.scd40_co2_value')|int > 1500 %}
         red
      {% elif states('sensor.scd40_co2_value')|int > 1200 %}
         orange
      {% elif states('sensor.scd40_co2_value')|int > 800 %}
         yellow
      {% else %}
         green
      {% endif %}


Automations

When the CO2, temperature, or humidity value is too high, you can send a notification to your phone or smart speaker.
Or use a colored light or LED strip to indicate the condition and make it visible at a glance.

It’s now easy to present on a wall panel dashboard in your living room.



Keep your home healthy, measuring is knowing!




^^ Top | Homepage | Best Buy Tips | Automation Ideas | Blog posts | Sitemap