CCS811 integration in ESPHome delivers only default value 400ppm eCO2

Last year, I made a simple breadboard with a 8266 MOD 12-F, connected to CCS811 eCO2 / eVOC sensor. It worked on power on with this setup in ESPHome:

captive_portal:

# CCS811-Sensor für CO2-Messung
i2c:
  sda: 4
  scl: 5

sensor:
  - platform: ccs811
    eco2:
      name: "CCS811 eCO2-Wert"
    tvoc:
      name: "CCS811 Total Volatile Organic Compound"
    address: 0x5A
    update_interval: 10s

I used it for some time - then it went into a box.

I want to use it again now, so I checked the connections etc. and cleaned it. Quite sure, that the hardware is ok.

I got it online instantly, but it always shows the default values for eCO2 and eVOC: 400 ppm and 0 ppm.

I searched through the community postings and had a chat with AI, but there were now results. Maybe it’s a victim of an update.

Sorry for not checking what to do…. can anybody give me helpful advice?

What you get on esphome logs?
Also, that sensor has long warmup time.

I cannot tell you what is wrong. What I can tell you is that, even when working, this sensor will tell you nothing about CO2 levels. eCO2 values are based on wrong assumptions.

Thanks for all the information and advice. I’m well aware about measurement methods concerning CO2. I’ve built true CO2 measurement projects using the MH-Z19B, which is working fine. I have access to a professional CO2 meter, used by firefighters. The deviation is 10 - 20% … not much worse.

Using a semiconductor for this business - as the CCS811 does - is a completely different business (I’ve studied physics) and will lead to totally different results. But I’ve use the CCS811 for some time deliberately and with care. It is not worth what you pay for it, but I just wanted to try it.

My concern regarding my topic is simply:

  • did I do anything wrong with the configuration?
  • why are there no results after > 24hrs of „warmup“?
  • OK, I didn’t calibrate the sensor. Fine. But I don’t think that’s the reason why I get the baseline 400ppm / 0ppb all the time.

You find a copy of the log here:

INFO ESPHome 2025.11.2
INFO Reading configuration /config/esphome/esphome-web-0de79d.yaml...
WARNING The minimum WiFi authentication mode (wifi -> min_auth_mode) is not set. This controls the weakest encryption your device will accept when connecting to WiFi. Currently defaults to WPA (less secure), but will change to WPA2 (more secure) in 2026.6.0. WPA uses TKIP encryption which has known security vulnerabilities and should be avoided. WPA2 uses AES encryption which is significantly more secure. To silence this warning, explicitly set min_auth_mode under 'wifi:'. If your router supports WPA2 or WPA3, set 'min_auth_mode: WPA2'. If your router only supports WPA, set 'min_auth_mode: WPA'.
WARNING Captive portal is enabled but no WiFi AP is configured. The captive portal will not be accessible. Add 'ap:' to your WiFi configuration to enable the captive portal.
INFO Starting log output from 192.168.0.49 using esphome API
INFO Successfully resolved esphome-web-0de79d @ 192.168.0.49 in 0.000s
INFO Successfully connected to esphome-web-0de79d @ 192.168.0.49 in 0.005s
INFO Successful handshake with esphome-web-0de79d @ 192.168.0.49 in 0.047s
[18:18:57.653][I][app:190]: ESPHome version 2025.11.2 compiled on Dec  8 2025, 18:03:57
[18:18:57.702][C][logger:261]: Logger:
[18:18:57.702][C][logger:261]:   Max Level: DEBUG
[18:18:57.702][C][logger:261]:   Initial Level: DEBUG
[18:18:57.702][C][logger:267]:   Log Baud Rate: 115200
[18:18:57.702][C][logger:267]:   Hardware UART: UART0
[18:18:57.703][C][i2c.arduino:072]: I2C Bus:
[18:18:57.703][C][i2c.arduino:073]:   SDA Pin: GPIO4
[18:18:57.703][C][i2c.arduino:073]:   SCL Pin: GPIO5
[18:18:57.703][C][i2c.arduino:073]:   Frequency: 50000 Hz
[18:18:57.703][C][i2c.arduino:089]:   Recovery: bus successfully recovered
[18:18:57.703][I][i2c.arduino:099]: Results from bus scan:
[18:18:57.703][I][i2c.arduino:105]: Found device at address 0x5A
[18:18:57.703][C][ccs811:153]: CCS811
[18:18:57.704][C][ccs811:154]:   Address: 0x5A
[18:18:57.704][C][ccs811:363]:   Update Interval: 10.0s
[18:18:57.715][C][ccs811:017]:   CO2 Sensor 'CCS811 eCO2-Wert'
[18:18:57.715][C][ccs811:017]:     State Class: 'measurement'
[18:18:57.715][C][ccs811:017]:     Unit of Measurement: 'ppm'
[18:18:57.715][C][ccs811:017]:     Accuracy Decimals: 0
[18:18:57.716][C][ccs811:027]:     Device Class: 'carbon_dioxide'
[18:18:57.726][C][ccs811:031]:     Icon: 'mdi:molecule-co2'
[18:18:57.736][C][ccs811:017]:   TVOC Sensor 'CCS811 Total Volatile Organic Compound'
[18:18:57.736][C][ccs811:017]:     State Class: 'measurement'
[18:18:57.736][C][ccs811:017]:     Unit of Measurement: 'ppb'
[18:18:57.736][C][ccs811:017]:     Accuracy Decimals: 0
[18:18:57.751][C][ccs811:027]:     Device Class: 'volatile_organic_compounds_parts'
[18:18:57.751][C][ccs811:031]:     Icon: 'mdi:radiator'
[18:18:57.751][C][ccs811:162]:   Baseline: NOT SET
[18:18:57.764][C][captive_portal:122]: Captive Portal:
[18:18:57.777][C][wifi:1062]: WiFi:
[18:18:57.777][C][wifi:1062]:   Connected: YES
[18:18:57.777][C][wifi:827]:   Local MAC: 44:17:93:0D:E7:9D
[18:18:57.777][C][wifi:834]:   IP Address: 192.168.0.49
[18:18:57.797][C][wifi:838]:   SSID: 'DemandFlow'[redacted]
[18:18:57.797][C][wifi:838]:   BSSID: 1C:56:8E:2C:6A:50[redacted]
[18:18:57.797][C][wifi:838]:   Hostname: 'esphome-web-0de79d'
[18:18:57.797][C][wifi:838]:   Signal strength: -71 dB ▂▄▆█
[18:18:57.797][C][wifi:838]:   Channel: 1
[18:18:57.797][C][wifi:838]:   Subnet: 255.255.255.0
[18:18:57.797][C][wifi:838]:   Gateway: 192.168.0.1
[18:18:57.797][C][wifi:838]:   DNS1: 0.0.0.0
[18:18:57.797][C][wifi:838]:   DNS2: 0.0.0.0
[18:18:57.807][C][esphome.ota:093]: Over-The-Air updates:
[18:18:57.807][C][esphome.ota:093]:   Address: 192.168.0.49:8266
[18:18:57.807][C][esphome.ota:093]:   Version: 2
[18:18:57.818][C][safe_mode:018]: Safe Mode:
[18:18:57.818][C][safe_mode:018]:   Successful after: 60s
[18:18:57.818][C][safe_mode:018]:   Invoke after: 10 attempts
[18:18:57.818][C][safe_mode:018]:   Duration: 300s
[18:18:57.828][C][web_server.ota:241]: Web Server OTA
[18:18:57.835][C][api:223]: Server:
[18:18:57.835][C][api:223]:   Address: 192.168.0.49:6053
[18:18:57.835][C][api:223]:   Listen backlog: 1
[18:18:57.835][C][api:223]:   Max connections: 4
[18:18:57.842][C][api:235]:   Noise encryption: NO
[18:18:57.846][C][mdns:177]: mDNS:
[18:18:57.846][C][mdns:177]:   Hostname: esphome-web-0de79d
[18:19:05.461][D][ccs811:115]: Got co2=400 ppm, tvoc=0 ppb, baseline=0x66AD
[18:19:05.474][D][sensor:133]: 'CCS811 eCO2-Wert': Sending state 400.00000 ppm with 0 decimals of accuracy
[18:19:05.480][D][sensor:133]: 'CCS811 Total Volatile Organic Compound': Sending state 0.00000 ppb with 0 decimals of accuracy
[18:19:15.464][D][ccs811:115]: Got co2=400 ppm, tvoc=0 ppb, baseline=0x66AD
[18:19:15.478][D][sensor:133]: 'CCS811 eCO2-Wert': Sending state 400.00000 ppm with 0 decimals of accuracy
[18:19:15.481][D][sensor:133]: 'CCS811 Total Volatile Organic Compound': Sending state 0.00000 ppb with 0 decimals of accuracy
[18:19:25.461][D][ccs811:115]: Got co2=400 ppm, tvoc=0 ppb, baseline=0x66AD
[18:19:25.473][D][sensor:133]: 'CCS811 eCO2-Wert': Sending state 400.00000 ppm with 0 decimals of accuracy
[18:19:25.477][D][sensor:133]: 'CCS811 Total Volatile Organic Compound': Sending state 0.00000 ppb with 0 decimals of accuracy

Let it run for half an hour in clean air and observe what baseline you get.
Then insert that on your code like described on docs:

I’ve taken my sensor & breadboard outdoor for a whole day in fresh air. While everything was still connected I took the LOG and found a baseline figure of 0x66AD after 20 hours.

I put the baseline code into the ESPHome setup script and installed it.
After that I got a new log output showing that this baseline was set.

Then I disconnected everything and started it again. All this happend about 1 hour ago. But there is still the indication: eCO2 400 ppm eVOC 0 ppb.

My NDIR-sensor shows 520 ppm CO2

I’ve ordered a new sensor now. Has anyone an idea, what I can do to fix it, if it is not a hardware problem, as I think? Is there anything wrong in this setup:

# CCS811-Sensor für eCO2-Messung
i2c:
  sda: 4
  scl: 5

sensor:
  - platform: ccs811
    eco2:
      name: "CCS811 eCO2-Wert"
    tvoc:
      name: "CCS811 Total Volatile Organic Compound"
    address: 0x5A
    update_interval: 10s
    baseline: 0x66AD

Try with default update interval and make sure wake-pin is connected to gnd.

Thanks again for your kind advice.

I’ve checked each single pin. WAK = 0 V, ADR = 0 V (means address is correct), power =3.33 V, GND =0 V … etc.

No effect. Same result. I’m waiting now for the replacement of the sensor, which is on it’s way to me.

I’ll give you an update, asap.

You power the sensor from esp 3.3V pin to CCS811 VIN pin, right? 3V3 pin is output…

Hi dear ESPHome experts,

I got the new sensor. It’s in the setup mode at the moment. Every minute, I get a new log output with a new baseline. I will run it (at the window in front of my office), until I have a stable baseline output. At the moment „baseline“ is commented out.

One more question:

I saw, that there is a way to tell the ccs811-sensor through the ESPHome-integration the temperature and humidity to get better results.

But I’m not sure how the YAML-code should look like. What do they mean by ID?? If I have a temperature-sensor f.e.:

sensor.temperatur_az_touch

and my YAML looks like this at the moment:

# CCS811-Sensor für eCO2-Messung

i2c:

sda: 4

scl: 5

sensor:

- platform: ccs811

eco2:

name: "CCS811 eCO2-Wert“

tvoc:

name: "CCS811 Total Volatile Organic Compound“

address: 0x5A

#baseline: 0x66AD

how will the „ID“ ??? look like for this code?

Id is esphome sensor id, not HA entity id.

sensor:
  - platform: homeassistant
    name: "Temperature Sensor From Home Assistant"
    entity_id: sensor.temperatur_az_touch
    id: temp_az # whatever you want to name it

thank you Karosm, didN’t understand, that’s sooooo simple :slight_smile:

By the way: I found a note about livetime of CCS811 (and other sensors, that are heated).

Experts say, that their livetime is limited by the livetime of the heater - sounds quite logical. If you use the default response time of the CCS811 - 60s - the heater is dimmed a bit for xxx seconds and before the next value has to be generated, the heater goes back to 100%. This is to save livetime.

My (old) CCS811 - that got broken - was running on 10s for about 12 months.
You can check, if your sensor is alive, by using an infrared thermometer.

I just ordered a bunch of ENS160 sensor with integrated temp/hum - sensors to find out about their performance.

Yep but datasheet gives lifespan >5years and it doesn’t exclude any of the 5 modes. And if I understood well, yours died while it was stored…

that’s true.

Thank you to all ESPHome experts, especially a big „thank you“ to Karosm for his perfect and superfast support :slight_smile:

Everything is working now with a new sensor. The old sensor was broken after 1 year online sending data every 10 seconds (I’m using 60 s default now).

Next thing I will try is the ENS160 breakout board, that come with a separate temperature and humidity sensor on board for calibration. Seems to be a slighly enhanced version of a eCO2 sensor.

Have a good XMAS and a happy new year 2026

Yours
Mike

You too!

Can you confirm that you were able to detect the heater on/off cycle with IR?

The room has a temperature of 21,0 °C, the infrared thermometer shows 28,8 °C when the laser points at the CCS-chip.

When I try it with my broken CCS, then there is 21,0 °C.

So - it should be working. But it’s a bit tricky, because the chip is so small.