HomeyDuino CO2 sensor

Just for who might be interested.

I was already looking for a long time for a good CO2 meter but either they were too expensive or too unreliable. So finally I decided to create one myself with the help of Homeyduino. The sensor itself is from Senseair (S8, auto calibrating), a reliable company when it concerns these type of sensors. The sensor is connected (via UART) to a TTGO ESP32 with display so the CO2 value can also be read directly. When the CO2 value exceeds 1000 parts per million I will be notified by my Google Home.

I must admit the enclosure looks horrible but by the lack of some good tools and patience it became what it is. At least it works perfectly well.

6 Likes

Hey Lars,

Nice one! Could you give a little more information how you did this?

Thanks in advance,
Wilfred

Cool! I also planned to go that way, bought a senseair s8 sensor, but never put it together. Have now three Netatmo healthy home coaches as CO2-sensors which is connected through Homey to control my roof fan.

But now I can just copy your code, hehe?

Hi Wilfred,

The device was constructed with:

The code was flashed to the ESP32 with the Arduino IDE. The libraries used are: TFT_eSPI and HomeyDuino. The TTGO communicates via UART/Serial2 to the Senseair sensor. If you’re handy with Arduino programming and soldering you can easily do it yourself. Most of the code I just borrowed from some samples and adapted it for my own purpose.

The total costs were about 50 Euro which is not much compared to what is available on the market. The sensor was most expensive but I wouldn’t recommand cheap Chinese rubbisch. They are not very accurate, brake easily or are not auto calibrating. Maybe there are some good ones but I haven’t seen any.

With Homey Insights every measurement is logged which can be seen in some nice graphics. When the CO2 values become too high the ventilation system is switched on automatically which prevents me from hallucinating (only when I am not on other stimulants of course) and other nasty things.

If you want I can send you the C++ code.

Hi Lars,

This is unfortunately a little too complex for me.

Greetings,
Wilfred

You don’t feel like experimenting with the VOC-sensor BME680. Seems to be the best VOC sensor, but the issue seems to convert the resistance value you get to ppm.https://www.bosch-sensortec.com/bst/products/all_products/bme680

With some help (thanks to maxxie01 on Slack) I did a Homeyduino CO2 sensor myself. Also a Senseair S8-sensor, connected to a wemos like described here https://www.letscontrolit.com/wiki/index.php/S8.

Here’s the Arduino code if anyone is interested. This is just the code for the board (wemos) and the sensor, no fancy display.

/*
    Basic Arduino example for K-Series sensor
    Created by Jason Berger
    Co2meter.com

    Adjusted to Homeyduino by maxxie01/rindlerblabla
*/

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <SoftwareSerial.h>
#include <Homey.h>

SoftwareSerial S8_Serial(13,15);  //Sets up a virtual serial port
                                  //Using pin 13 for Tx and pin 15 for Rx
                                    
unsigned long prevReadTime = 0;
unsigned int ReadTimeInterval = 300000; //Update interval in ms
unsigned long valCO2 = 0;
byte readCO2[] = {0xFE, 0X44, 0X00, 0X08, 0X02, 0X9F, 0X25}; 
byte response[] = {0,0,0,0,0,0,0};  

int valMultiplier = 1;
void wifi() {
  if (WiFi.status() != WL_CONNECTED) {
    WiFi.begin("SSID", "PASSWORD");
    uint8_t timeout = 30;
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
      if (timeout<1) break;
    }
    if (WiFi.status() == WL_CONNECTED) {
      //Print IP address
      Serial.print("Connected to WiFi! (");
      Serial.print(WiFi.localIP());
      Serial.println(")");
    }
  }
}


void setup()
{
  Serial.begin(115200);       
  S8_Serial.begin(9600);    
  Homey.begin("senseair-s8");
  Homey.setClass("sensor");
  Homey.addCapability("measure_co2");
}
void loop()
{
  wifi();
  Homey.loop();
  //to check not every loop use this interval method
  if(millis() - prevReadTime > ReadTimeInterval){
    prevReadTime = millis();
    sendRequest(readCO2);
    unsigned long tmpValCO2 = getValue(response);
    if(tmpValCO2 != valCO2){
      valCO2 = tmpValCO2;
      Serial.print("CO2 ppm = ");
      Serial.println(valCO2);
      Homey.setCapabilityValue("measure_co2", (int) valCO2 );
    }
  }
}

void sendRequest(byte packet[])
{
  while(!S8_Serial.available())  //keep sending request until we start to get a response
  {
    S8_Serial.write(readCO2,7);
    delay(50);
  }

  int timeout=0;  //set a timeoute counter
  while(S8_Serial.available() < 7 ) //Wait to get a 7 byte response
  {
    timeout++;
    if(timeout > 10)    //if it takes to long there was probably an error
      {
        while(S8_Serial.available())  //flush whatever we have
          S8_Serial.read();

          break;                        //exit and try again
      }
      delay(50);
  }

  for (int i=0; i < 7; i++)
  {
    response[i] = S8_Serial.read();
  }
}

unsigned long getValue(byte packet[])
{
    int high = packet[3];                        //high byte for value is 4th byte in packet in the packet
    int low = packet[4];                         //low byte for value is 5th byte in the packet


    unsigned long val = high*256 + low;                //Combine high byte and low byte with this formula to get value
    return val* valMultiplier;
}
1 Like


Just finished my second version. This time in a 3D printed enclosure and a Bosch BME680 (with official Bosch library). So now I can read CO2/Temp/Humidity/Pressure/VOC/IAQ. The BME680 can also act as a smoke detector. I tried it with setting a piece of paper on fire. The IAQ readings immediately went up to over 400 (range 0-500),


One can do the (WiFi) setup via a WiFi access point to fill in the parameters for WiFi/Homey and a MQTT client. Still need to calibrate the CO2 sensor by the way.

!
2020_01_02_08.10.22|237x500

4 Likes

If you use the official Bosch library the IAQ index (based on VOC) is calculated for you.

Did you have to do all steps described here? Do you also got the ppb-value? https://github.com/BoschSensortec/BSEC-Arduino-library/blob/master/README.md#installation-and-getting-started

The VOC sensor also works just fine as a smell detector if you control your ventilation in the bathroom and so on.

If you want to use the Bosch library you indeed need to go through the steps as described. Including a library for the ESP32 is slightly different. To include it it was little bit tricky but it can be done. Once you edit the Arduino files for the ESP32 the library is added for all projects so keep the originals.
I just use the IAQ (indoor air quality) index, I don’t think there is PPB value. There is a VOC resistence value though.

Using it in the WC is one of the main functions of this sensor.

Wow, really nice, wel done!
Any estimates for how much it cost, including the 3D printed enclosure?

Thanks. I would charge € 100,00 which is a bargain compared to what’s on the market.

Okey, solved most of my questions. I just wonder if you can share the code snippet that triggers the smoke alarm out of the IAQ values?

Hi Rindler,

SETUP

Blockquote
// BME680 setup
Wire.begin();
iaqSensor.begin(BME680_I2C_ADDR_SECONDARY, Wire);
output = "\nBSEC library version " + String(iaqSensor.version.major) + “.” + String(iaqSensor.version.minor) + “.” + String(iaqSensor.version.major_bugfix) + “.” + String(iaqSensor.version.minor_bugfix);
Serial.println(output);
checkIaqSensorStatus();
bsec_virtual_sensor_t sensorList[10] = {
BSEC_OUTPUT_RAW_TEMPERATURE,
BSEC_OUTPUT_RAW_PRESSURE,
BSEC_OUTPUT_RAW_HUMIDITY,
BSEC_OUTPUT_RAW_GAS,
BSEC_OUTPUT_IAQ,
BSEC_OUTPUT_STATIC_IAQ,
BSEC_OUTPUT_CO2_EQUIVALENT,
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
};
iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
checkIaqSensorStatus();
iaqSensor.setTemperatureOffset( tempOffset );

LOOP

bme680loop(); // sensor values will automatically update only every 3 seconds, no matter how many times it is called

Serial.println( " Raw temp. " + String(iaqSensor.rawTemperature) );
Serial.println( " Raw press. " + String(iaqSensor.pressure) );
Serial.println( " Raw hum. " + String(iaqSensor.rawHumidity) );
Serial.println( " Gas resis. " + String(iaqSensor.gasResistance));
Serial.println( " IAQ " + String(iaqSensor.iaq));
Serial.println( " IAQ accuracy " + String(iaqSensor.iaqAccuracy));
Serial.println( " Comp. temp. " + String(iaqSensor.temperature));
Serial.println( " Comp. hum. " + String(iaqSensor.humidity));
Serial.println( " StaticIAQ " + String(iaqSensor.staticIaq));
Serial.println( " eCO2 " + String(iaqSensor.co2Equivalent));
Serial.println( " BreathVocEq. " + String(iaqSensor.breathVocEquivalent));

Thanks. And the code for the smoke alarm? :slight_smile:

if ( iaqVal >= 250 ) {
  Serial.println("High pollution detected!");
  alarm_smoke = true;
}
else {
  //Serial.println("No pollution detected");
  alarm_smoke = false;
}

Homey.setCapabilityValue(“alarm_smoke”, (bool) alarm_smoke );

2 Likes

Maybe I did something wrong, but the code above just spammed my Homey with several alerts every second that there were no fire, ha! For sure it depends on were you put the setCapabilityValue-code. Anyhow, the code below solved it for me.

   if ((iaqSensor.staticIaq >= 250) && (alarm_smoke == false)) {
      Serial.println("High pollution detected!");
      alarm_smoke = true;
      Homey.setCapabilityValue("alarm_smoke", (bool) alarm_smoke );
    }
    else if ((iaqSensor.staticIaq <= 250) && (alarm_smoke == true)){
      Serial.println("No pollution detected");
      alarm_smoke = false;
      Homey.setCapabilityValue("alarm_smoke", (bool) alarm_smoke );
    }