ESPHome CO2 sensor
Based on the SenseAir S8 sensor
Introduction

There are not many affordable out-of-the-box CO2 sensors available, but it’s easy to create one yourself. With only an ESP, a CO2 sensor, power cable and box, it’s a small and easy project with a lot of benefits!
If you are in a space with a too high ppm level, you can feel tired, you start yawning,
get red cheeks and can get a headache.
Without knowing, this happens because it goes very gradually.
To prevent this, use your smart home automations and take action if the levels are too high.
This keeps you and your family healthy!
CO2 stands for Carbon dioxide and is measured in Parts per million (ppm).
The average value outside is around 400 ppm, which is the baseline value. That’s also why you need to open a window when the value is too high, get some fresh air from outside.
| ppm | condition | action |
|---|---|---|
| 400 - 800 | good | no action is required |
| 800 - 1200 | medium | open a window |
| 1200+ | bad | limit has reached, open all windows |
My final result
The finished sensor is compact, practical, and easy to place anywhere in the room. Everything fits inside a small case: the SenseAir S8 sensor, an ESP mini.

NOTE: 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 NL, Amazon DE
Table of Contents
- Introduction
- My final result
- Required hardware
- Required software
- Connect the hardware
- ESPHome
- Home Assistant
- Automations
Required hardware
The links go to an overall page with all different kinds of DIY components with their webshop links.
Also affiliate links are used here. Same price, but you also sponsor this blog.
All the required products are also bundled on these Amazon US and Amazon NL pages.
These are the hardware components I used for this project:
SenseAir S8 CO2 sensor (AliExpress)
(AliExpress alternative link)
(Amazon US)
(Amazon NL)

ESP 12S Wemos D1 mini (no pro or V3) (AliExpress)
(Amazon US)
(Amazon NL)
- You can use any ESP chip, but I like this one because of its small size

Dupont male-to-male wires (AliExpress)
(Amazon US)
(Amazon NL)
- If you order these, it’s better to order all three types at once, also for any future projects

-
Plastic DIY case, I used a box with dimensions 70 x 45 x 30 mm. This one is not available anymore, but you can order a similar one (it’s in white still available) or at least one with dimensions of this size or larger.
A lot of boxes with all kinds of sizes (AliExpress)
(Amazon US)
(Amazon NL)

Micro USB cable to power the ESP board (AliExpress)
(Amazon US)
(Amazon NL)

5V USB wall power adapter to power the ESP (AliExpress)
(Amazon US)
(Amazon NL)

Soldering iron (AliExpress)
(Amazon US)
(Amazon NL).
I suggest this based on the reviews. I already had one. Please let me know whether you recommend this one.

Soldering tin wire (AliExpress)
(Amazon US)
(Amazon NL)

Also affiliate links are used here, so you also support my blog without paying extra for it.
Found a dead link? Please inform me
See ESPHome DIY sensors Best Buy Tips for more DIY hardware buy tips.
Required software
For this project, you only need the software to flash the ESP chip with the configuration file.
The only software you need is ESPHome.
There are a lot of ways to flash the config file with ESPHome to the board.
Read here how to upload it.
Connect the hardware
I’ve made a diagram showing how to connect the SenseAir S8 to the ESP.
The ESP and sensor don’t come with pins, so you need to solder on some pins to connect the male-to-male dupont cables between the sensor and the ESP.
Connect the SenseAir S8 to the ESP
Click on the image to see the full photos of the connected wires.
| Connected pins | ESP8266 Wemos D1 mini pins |
SenseAir S8 pins |
|---|---|---|
![]() |
![]() |
This image is 180 degrees rotated compared with the first image. |
This table shows how the ESP is connected with the SenseAir.
| Wemos pin | GPIO Wemos pin* | SenseAir pin |
|---|---|---|
| D7 | GPIO13 | UART_TxD |
| D8 | GPIO15 | UART_RxD |
| G | GND | G0 |
| 5V | 5 V | G+ |
* The GPIO Wemos pin is the port which is used in the ESPHome yaml.
If you place the sensor on top of the ESP mini, it fits perfectly in the DIY box.
You need to drill a hole in the edge of the box so the USB power cable can go through it.

I drilled some holes in the case. Now the air can reach the CO2 sensor inside the box.

ESPHome

Flash the ESP
Connect the ESP via USB to the computer.
Install ESPHome and compile the configuration code after you’ve configured your own wifi. And configure (or remove) the MQTT section. This is not needed if you integrate it directly in Home Assistant.
For more information about installing and flashing your ESP with ESPHome see the ESPHome website or Peyanski ESPHome Installation Guide.
The script:
# Sourcecode by vdbrink.github.io
esphome:
name: esp_co2
comment: Room CO2 sensor
platform: ESP8266
board: d1_mini
arduino_version: latest
wifi:
ssid: "xxx"
password: "xxx"
fast_connect: true # only needed for hidden SSID
mqtt:
broker: xxx.xxx.xxx.xxx
port: 1883
username: "xxx"
password: "xxx"
uart:
rx_pin: GPIO13
tx_pin: GPIO15
baud_rate: 9600
sensor:
- platform: senseair
co2:
id: senseair_co2
name: "SenseAir CO2 Value"
update_interval: 30s
Home Assistant
Once the sensor pushes the data, you can use and present the data on your dashboards or create notifications when the values are not good.
Dashboard Gauge
In a Gauge, you can directly see if the current CO2 value is correct. I used different colors to indicate how bad the condition is. I used the values from the table mentioned in the Introduction.

# Sourcecode by vdbrink.github.io
# Dashboard card code
type: gauge
severity:
green: 400
yellow: 800
red: 1200
entity: sensor.senseair_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.

# Sourcecode by vdbrink.github.io
# Dashboard card code
type: sensor
graph: line
entity: sensor.senseair_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.
This card required the HACS integration mini-graph-card,
install this integration via this button

# Sourcecode by vdbrink.github.io
# Dashboard card code
type: custom:mini-graph-card
entities:
- entity: sensor.senseair_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: "#c0392b"
- value: 1200
color: "#d35400"
- value: 2000
color: "#c0392b"
Dashboard History Graphic

Another graph entity is the history-graph.
You can also show baseline values by creating a custom sensor with a fixed value.
# Sourcecode by vdbrink.github.io
# Dashboard card code
type: history-graph
entities:
- entity: sensor.senseair_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 see the threshold values.
# Sourcecode by vdbrink.github.io
# configuration.yaml
- sensor:
- platform: template
sensors:
co2_value_800:
friendly_name: "good"
value_template: 800
unit_of_measurement: 'ppm'
co2_value_1200:
friendly_name: "average"
value_template: 1200
unit_of_measurement: 'ppm'
co2_value_1500:
friendly_name: "bad"
value_template: 1500
unit_of_measurement: 'ppm'
Dashboard condition text

This creates a new sensor that shows a text representation of the current condition.
# Sourcecode by vdbrink.github.io
# configuration.yaml
- platform: template
sensors:
senseair_co2_value_text:
icon_template: "mdi:molecule-co2"
friendly_name: "Room CO2 "
value_template: >-
{% set state = states('sensor.senseair_co2_value') | int %}
{% if state < 800 %}good
{% elif state > 800 and state <= 1200 %}average
{% elif state > 1200 and state <= 1500 %}bad
{% elif state > 1500%}very bad
{% else %}unknown{% endif %}
In my dashboard, I have a section with important messages. Only when action is required do you see that here. There is also a message when the CO2 value is not good. This section can be achieved by using conditional entities.
# Sourcecode by vdbrink.github.io
# Dashboard card code
type: entities
entities:
- type: conditional
conditions:
- entity: sensor.senseair_co2_value_text
state_not: good
row:
entity: sensor.senseair_co2_value_text
Dashboard Mushroom entity
Show a green icon, without any text, if the level is less than 800 ppm, less than 1200 ppm yellow, and less than 1500 ppm red.
# Sourcecode by vdbrink.github.io
# Dashboard card code
type: custom:mushroom-chips-card
chips:
- chip: null
type: template
icon: mdi:molecule-co2
entity: sensor.senseair_co2_value
content: ''
icon_color: |-
{% if is_state('sensor.senseair_co2_value', 'unavailable') %}
blue
{% elif states('sensor.senseair_co2_value')|int > 1500 %}
red
{% elif states('sensor.senseair_co2_value')|int > 1200 %}
orange
{% elif states('sensor.senseair_co2_value')|int > 800 %}
yellow
{% else %}
green
{% endif %}
Automations
When the CO2 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.
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


