[example] Indoor Air Quality text sensor using CCS811 sensor

Hi all,

I wanted to share a custom text sensor I made for the CCS811 sensor using ESPHome.

The CCS811 Air Quality Breakout is a digital gas sensor solution that senses a wide range of Total Volatile Organic Compounds (TVOCs) and CO2. You can also buy versions which also include a HDC1080 temperature and humidity sensor. Temperature and humidity can also be used to improve the CCS811’s internal calculations.

While having these measurements is great and all, I wanted a more human friendly approach in knowing the overall indoor air quality without having to study multiple graphs and look up threshold values. Other sensors such as the BME680 report such a value based on the multiple readings. So I decided to create my own using ESPHome templating.

The idea is based on this PDF http://www.iaquk.org.uk/ESW/Files/IAQ_Rating_Index.pdf for indoor air quality (IAQ) values. In my implementation it gives points (1-5) for certain thresholds of the TVOC, CO2 and Humidity values. Points combined from these sensors are then in its turn transformed to human readable text.

You need to start by setting up a basic node in ESPHome. If you do not know how to do that, please read this page first: https://esphome.io/guides/getting_started_hassio.html.

Then you can connect the CCS811 breakout module to your I2C bus. You also need a temperature and humidity sensor. If you are not using the CCS811 HDC1080 combo module, you probably need to change the code.

No temperature and humidity sensor connected to your node?
With ESPHome it is even possible to import the state of other temperature / humidity sensors from Home Assistant! https://esphome.io/components/sensor/homeassistant.html

Important: you also need to change the baseline value of the CCS811 sensor. Check the documentation on how to calibrate your own baseline https://esphome.io/components/sensor/ccs811.html#calibrating-baseline. If you just connected the CCS811 sensor it is also advised to take a 48 hour “burn-in” period before you read out the baseline and start using the sensor.

  sda: 21
  scl: 22
  scan: True

  - id: iaq_index
    type: int
    restore_value: no
    initial_value: '0'
  - platform: hdc1080
      name: "Livingroom Temperature"
      id: temp
      name: "Livingroom Humidity"
      id: humi
    address: 0x40
    update_interval: 60s
  - platform: ccs811
      name: "Livingroom eCO2"
      id: eco2
      name: "Livingroom TVOC"
      id: tvoc
    temperature: temp
    humidity: humi
    baseline: 0xD3BE
    address: 0x5A
    update_interval: 60s
  - platform: template
    name: "Livingroom IAQ"
    icon: "mdi:air-filter"
    lambda: |-
      id(iaq_index) = 0;
       * Transform indoor humidity values to IAQ points according to Indoor Air Quality UK: 
       * http://www.iaquk.org.uk/
      if (id(humi).state < 10 or id(humi).state > 90) {
        id(iaq_index) += 1;
      else if (id(humi).state < 20 or id(humi).state > 80) {
        id(iaq_index) += 2;
      else if (id(humi).state < 30 or id(humi).state > 70) {
        id(iaq_index) += 3;
      else if (id(humi).state < 40 or id(humi).state > 60) {
        id(iaq_index) += 4;
      else if (id(humi).state >= 40 and id(humi).state <= 60) {
        id(iaq_index) += 5;
       * Transform eCO2 values to IAQ points according to Indoor Air Quality UK: 
       * http://www.iaquk.org.uk/
      if (id(eco2).state <= 600) {
        id(iaq_index) += 5;
      else if (id(eco2).state <= 800) {
        id(iaq_index) += 4;
      else if (id(eco2).state <= 1500) {
        id(iaq_index) += 3;
      else if (id(eco2).state <= 1800) {
        id(iaq_index) += 2;
      else if (id(eco2).state > 1800) {
        id(iaq_index) += 1;
       * Transform TVOC values to IAQ points according to German environmental guidelines: 
       * https://www.repcomsrl.com/wp-content/uploads/2017/06/Environmental_Sensing_VOC_Product_Brochure_EN.pdf
      if (id(tvoc).state <= 65) {
        id(iaq_index) += 5;
      else if (id(tvoc).state <= 220) {
        id(iaq_index) += 4;
      else if (id(tvoc).state <= 660) {
        id(iaq_index) += 3;
      else if (id(tvoc).state <= 2200) {
        id(iaq_index) += 2;
      else if (id(tvoc).state > 2200) {
        id(iaq_index) += 1;

       * Transform IAQ index to human readable text according to Indoor Air Quality UK: 
       * http://www.iaquk.org.uk/
      ESP_LOGD("main", "Current IAQ index %d", id(iaq_index));
      if (id(iaq_index) <= 6) {
        return {"Unhealty"};
      else if (id(iaq_index) <= 9) {
        return {"Poor"};
      else if (id(iaq_index) <= 12) {
        return {"Moderate"};
      else if (id(iaq_index) <= 14) {
        return {"Good"};
      else if (id(iaq_index) > 14) {
        return {"Excellent"};
      return {};
    update_interval: 60s

thank you very much for your great code :slight_smile:

Thanks for sharing this interesting approach. Checking your aliexpress link, there is a customer review that says:
“Sensor board works ok, But please note the HDC1080 temperature corresponds to the pcb board temperature and not the environment temperature! This is because the CCS811 heats up the board. Thus the HCD1080 values can’t be used to correct the measurement of the CCS811.”

I wonder if you had the same discrepancies in HDC1080 temperature (if it happens, probably compromises the humidity sensor too) compared to another temperature sensor.
There is also a pcb version with CSS811 only, but then it would not be able to correct internally its measurement.
There is also the MH-Z19B sensor, that could be a choice. I would like to implement something like this but still trying to define the best choices.
Experiences from everybody with CO2 sensors are welcome.


Thanks for replying! I did see that comment actually. To test I also added a HTU21D temperature/humidity sensor to see if there are any huge differences. So far I haven’t noticed large temperature differences between the sensors however temperatures here haven’t dropped below 20 degrees and it might be a larger issue when temperatures are lower (?). Maybe something to investigate after the summer…

The CCS811 also has its own micro-controller on board which makes it easy to interface with (if you are not using ESPHome), you can even update its firmware. https://github.com/maarten-pennings/CCS811/tree/master/examples/ccs811flash

If you are interested in “air quality” and not raw CO2 readings you could also look at the BME680 sensor.

Yes, I am interested in air quality, but BME680 is focused in Volatile Organic Compounds. In addition CO2 is an important element in air quality, that’s why I use BME280 and looking for some CO2 sensor to complement it. Air particles in suspension would also be nice (PMS5003 / PMS7003), which also can interface with tasmota, being implemented very easily.

PS: I don’t think that that in the winter it´s going to be a bigger issue. It is in hot times that it will be harder do dissipate the heat. What would be the temperature difference between the sensors?
Are you satisfied how the IAQ meter is working. Its activity makes sense for what you have been using it for? Thanks for your feedback.

First of all if you are interested in differences between multiple sensors, checkout this great resource! https://www.jaredwolff.com/finding-the-best-tvoc-sensor-ccs811-vs-bme680-vs-sgp30/#show1

Your reply actually made me look at the BME680 datasheet. I read somewhere that the BME680 measured CO2 but used that together with the VOC values to output a AQI value… However you are totally right it indeed only measures VOC!

I will setup a graph soon in Grafana where I can compare the temperature values.

I couldn’t find to what temperature the CCS811 heats up so my hypothesis was that with higher environmental temperatures the difference in thermal energy will be lower than in the winter when the environment is colder.

In the datasheet of the CCS811 I found that you can actually change to a mode where the heating element turns only on for a measurement every minute so if heating of the element is an issue that also might be a solution. Sparkfun also has a document on mechanical considerations: https://cdn.sparkfun.com/assets/learn_tutorials/1/4/3/CC-000783-AN-1-Mechanical_Considerations_for_CCS811_0.pdf.

For my application the sensor seems to work well enough however I also have no accurate way of testing its accuracy. I am still curious about the performance of NDIR based sensors such as the MH-Z19B, I might order one of those just to check it out!

1 Like

That’s an interesting finding. Thanks.
And yes, MH-Z19B might be an good alternative. Also it reads from zero ppm, while ccs811 starts in 400 ppm. It can be not much relevant since 400ppm is a very healthy reading though.

Bought a MH-Z19B from China. Will take a month to arrive.

Please keep me updated. :slight_smile:


A good background reference to read:
I am afraid that SenseAir S8 could be a better option. It’s about 50% more expensive than MH-Z19B though. The MH-Z19B works good for the price, but if the room is not well ventiled everyday, it might turn into a problem.

We have been having some hot weather here and one of my CCS811 sensors “died” or it is showing a constant value of 4605ppm of CO2…

I guess I am also ordering a MH-Z19

That’s bad. My MH-Z19B should arrive next week.

Exactly after a month, my MH-Z19B just arrived and I already had it installed. It is working but I guess that I will have to let it work for some days to see how it is going to behave.


1 Like

A first observation: After leaving the room well ventilated, ppm dropped to exactly 400 ppm, recusing to go below this. I thought that it could measure below 400 (from specifications: 0-5000ppm). On the other hand, it may be related to this:
“Nevertheless exposure to concentrations below 400 ppm may result in incorrect operation of ABC algorithm and shall be avoided for model with ABC on”.
As far as I know, ABC algorithm is enabled by default.


Cool! I’ve had mine for a few weeks now however I did not have time to configure the sensor yet. I hope to do a comparison to the CCS811 soon!

Any news about your MHZ?

Hi, Could you gif us more information about your setup.
Im trying to get the MHZ to work asswell. But i havent seen the Tasmota anywhere. Im trying to integrate it with ESPhome. But unsuccessful. Gettings a sort of error message.

But I`m interested in your setup.

The tasmota you can get here.

Also take a look here for connections and configuration.

Come back if you get stuck somewhere. Good luck!

I got it working.
Now i need to connect the MQTT to the HA MQTT Broker.
Connection seems to be there but is cannot find the entity like
AM2301, BMP280, and MHZ10B.

Do you have it connected to HomeAssistant?

May be a bit offtopic: sorry