Reliability of measurements

From yesterday, every 10 minutes I take measurements (NodeMCU + CCS811). In the code I have delay (300000);. I send measurements to my InfluxDB database. I visualize them using Grafan. Here are my measurements from the last 24 hours.

A few minutes after midnight I restarted the NodeMCU, because the measurements seemed too high. After the reboot they dropped significantly.
It is strange that at night, with the windows and doors closed, there has been no increase in CO2 levels. I have poor ventilation in the flat, so it should be significant.
TVOC and CO2 levels seem to be strongly related. Which is rather strange.

Does CCS811 require any additional calibration? Do I only have such strange measurements?

Thank you for the comprehensive information. Especially about the correlation of TVOC and CO2.
I have a new CCS811 sensor. I just uploaded the firmware - https://github.com/maarten-pennings/CCS811/tree/master/examples/ccs811flash
In a few days I will send a new screenshot with measurements.

Thanks for the info - gives confidence!

Thanks for the compliments.
No, I didn't find any release notes of the firmware... (pity).
Let me know which board you have and whether the update works.
I now have a 100% success rate but only for two boards :-)

Hi Maarten! Success! 👍 3 out of 3!
I also got a Chinese one, I guess. I bought it at a web shop close to you: https://www.tinytronics.nl/shop/en/sensors/temperature-air-humidity/ccs811-air-quality-sensor
I flashed it with an ESP32, a LOLIN D32. Your library does not work with the 1.0.0 release of https://github.com/espressif/arduino-esp32 but it does work with the latest master.
Again, thanks a lot! I will definitely use your library for my IoT project.

Hi Maarten, I flashed only one making your success rate is 3 out of 3, still 100%!

I was a bit puzzled by your ESP32 remark. Are you saying that Arduino with ESP32 and CCS811 version 1.0.0 does not work?

No, I wanted to tell you that your CSS811 library doesn't work with the 1.0.0 release of the ESP32 Arduino Core (https://github.com/espressif/arduino-esp32). It does work with the latest github master.
I want to start using the latest master anyway, because the 1.0.0 release has some trouble with WiFi disconnects which might have been solved in the upcoming 1.1.0 release. I see there are some commits related to that.
I really like TinyTronics. I had some mail conversations with Loek over the years, he is always so helpful.

A bit out of subject, but I just like to mention that I also just successfully flashed a CJMCU-811 board from 1.1 to 2.0. I compiled it using platformio, I ran it on a wemos d1 mini (ESP8266).

Thanks @bertrik for logging the result (platformio is a new environment, so good to know).

@maarten-pennings I just got a Chinese sensor CCS811 HDC1080. Do you know if I can upload a new firmware for this sensor?

If you run the ccs811basic.ino example and open a serial console at 115200 bps, you should see it report the 'application version' after rebooting. If this is lower than 2.0 you can flash it with a newer firmware. If it's already updated to 2.0, the firmware update sketch does nothing (skips the update).

@maarten-pennings I just got a Chinese sensor CCS811 HDC1080. Do you know if I can upload a new firmware for this sensor?

I just uploaded the firmware to 2.0. Everything went without problems. CCS811 and HDC1080 works OK. I plan to compare the data from two Chinese CCS811 boards(one with HDC1080 and second without HDC1080)

Yeah, i also get that, typically in 24h intervals.

flashing worked for me.
i used an esp8266.

Have anyone figured out how to make both HDC1080 and CCS810 to work together...
it looks like as soon as you initialize ccs811 HDC1080 no longer works...
i get correct readings from HDC1080 prior to ccs811.begin but after it's 125C and 100%

Hi @pashar1, from code review, I see no reason why ccs811.begin would break hdc1080. I have no hdc1080.
What you could do to pinpoint the problem is to comment out more and more sections in ccs811.begin to see what breaks hdc1080.

so far I was not able to pinpoint where...
here is sketch I have put... I have tried with ClosedCube_HDC1080 library and with example from TI just using i2c

#include <Wire.h>
#include <ccs811.h>

CCS811 ccs811(D4);

void setup() {
  uint16_t eco2, etvoc, errstat, raw;
  double temperature;
  double humidity;
   //Initialize I2C Communication
  Serial.begin(115200);
  Serial.println("");
  Serial.println("setup: Starting HDC1080 basic demo");

  Wire.begin();

  //Configure HDC1080
  Wire.beginTransmission(0x40);
  Wire.write(0x02);
  Wire.write(0x90);
  Wire.write(0x00);
  Wire.endTransmission();

  //Delay for Startup of HDC1080
  delay(20);

  humidity = readSensor(&temperature);

  //Print the current temperature to the right of the label

  Serial.print("T="); Serial.print(temperature);

  Serial.print(" RH="); Serial.println(humidity);

  Serial.println (" ccs811.begin " );
  ccs811.set_i2cdelay(50); // Needed for ESP8266 because it doesn't handle I2C clock stretch correctly
  bool ok = ccs811.begin();
  if ( !ok ) Serial.println("setup: CCS811 begin FAILED");

  delay(100);

  humidity = readSensor(&temperature);

  //Print the current temperature to the right of the label

  Serial.print("T="); Serial.print(temperature);

  Serial.print(" RH="); Serial.println(humidity);

  delay(100);

  ok = ccs811.start(CCS811_MODE_1SEC);
  if ( !ok ) Serial.println("init: CCS811 start FAILED");

  delay(10000);

  humidity = readSensor(&temperature);

  //Print the current temperature to the right of the label

  Serial.print("T="); Serial.print(temperature);

  Serial.print(" RH="); Serial.println(humidity);

  ccs811.read(&eco2, &etvoc, &errstat, &raw);

    Serial.print("\neCO2 concentration: ");
    Serial.print(eco2);
    Serial.print(" ppm");

    Serial.print("\nTVOC concentration: ");
    Serial.print(etvoc);
    Serial.println(" ppb");

    delay(100);

    humidity = readSensor(&temperature);

    Serial.print("T="); Serial.print(temperature);

    Serial.print(" RH="); Serial.println(humidity);

}

void loop()
{

}

double readSensor(double* temperature)
{
  //holds 2 bytes of data from I2C Line
  uint8_t Byte[4];

  //holds the total contents of the temp register
  uint16_t temp;

  //holds the total contents of the humidity register
  uint16_t humidity;

  //Point to device 0x40 (Address for HDC1080)
  Wire.beginTransmission(0x40);
  //Point to register 0x00 (Temperature Register)
  Wire.write(0x00);
  //Relinquish master control of I2C line
  //pointing to the temp register triggers a conversion
  Wire.endTransmission();

  //delay to allow for sufficient conversion time
  delay(20);

  //Request four bytes from registers
  Wire.requestFrom(0x40, 4);

  delay(1);

  //If the 4 bytes were returned sucessfully
  if (4 <= Wire.available())
  {
    //take reading
    //Byte[0] holds upper byte of temp reading
    Byte[0] = Wire.read();
    //Byte[1] holds lower byte of temp reading
    Byte[1] = Wire.read();

    //Byte[3] holds upper byte of humidity reading
    Byte[3] = Wire.read();
    //Byte[4] holds lower byte of humidity reading
    Byte[4] = Wire.read();

    //Combine the two bytes to make one 16 bit int
    temp = (((unsigned int)Byte[0] <<8 | Byte[1]));

    //Temp(C) = reading/(2^16)*165(C) - 40(C)
    *temperature = (double)(temp)/(65536)*165-40;

   //Combine the two bytes to make one 16 bit int
    humidity = (((unsigned int)Byte[3] <<8 | Byte[4]));

    //Humidity(%) = reading/(2^16)*100%
    return (double)(humidity)/(65536)*100;
  }
}

here is output:
setup: Starting HDC1080 basic demo
T=24.35 RH=46.97
 ccs811.begin
T=24.35 RH=0.00
T=24.35 RH=0.00

eCO2 concentration: 400 ppm
TVOC concentration: 0 ppb
T=24.35 RH=0.00

Thanks for the awesome flashing sketch, just flashed two CCS811 / HDC1080 modules from Aliexpress (https://www.aliexpress.com/item/32871695408.html) using a WEMOS D1. 😄

Hi @regnerus, thanks for letting me know.  I have added your result to the table.

Hi Maarten
I use CCS811-HDC1080 Module with DOIT ESP32 DEVKIT V1 developer board to measure the CO2 level with the library you shared with us.
Here is the module I bought,
https://www.banggood.com/CCS811-HDC1080-Carbon-Dioxide-CO2-Temperature-And-Humidity-Sensor-VOCs-Air-quality-Monitor-Sensor-Module-Winder-p-1428378.html
Modules wired on breadboard is below

I have upgraded the firmware to 2.0 version as you have explained in CCS811 flash page.
I am not sure about the ppm values found by this electronic set. Please see the data I have shared in ccs811_hdc1080_eco2_etvoc_data.txt file.
ccs811_hdc1080_eco2_etvoc_data.txt
When I restart the ESP32, I receive around 400ppm in my office but after a while ppm values increases too much.
I had measured same office room with MH-Z19B sensor and had found about 400-600ppm range in my 2 weeks test.
Here is the code
`/*
ccs811basic.ino - Demo sketch printing results of the CCS811 digital gas sensor for monitoring indoor air quality from ams.
Created by Maarten Pennings 2017 Dec 11
*/
#include <Wire.h>    // I2C library
#include "ccs811.h"  // CCS811 library
#include "ClosedCube_HDC1080.h"
// Wiring for ESP8266 NodeMCU boards: VDD to 3V3, GND to GND, SDA to D2, SCL to D1, nWAKE to D3 (or GND)
//CCS811 ccs811(D3); // nWAKE on D3
// Wiring for Nano: VDD to 3v3, GND to GND, SDA to A4, SCL to A5, nWAKE to 13
//CCS811 ccs811(13);
// nWAKE not controlled via Arduino host, so connect CCS811.nWAKE to GND
//CCS811 ccs811;
// Wiring for ESP32 NodeMCU boards: VDD to 3V3, GND to GND, SDA to 21, SCL to 22, nWAKE to D3 (or GND)
CCS811 ccs811(23); // nWAKE on 23
ClosedCube_HDC1080 hdc1080;
void setup() {
// Enable serial
Serial.begin(115200);
Serial.println("");
Serial.println("setup: Starting CCS811 basic demo");
Serial.print("setup: ccs811 lib  version: ");
Serial.println(CCS811_VERSION);
// Enable I2C
Wire.begin();
// Enable CCS811
ccs811.set_i2cdelay(50); // Needed for ESP8266 because it doesn't handle I2C clock stretch correctly
bool ok = ccs811.begin();
if( !ok ) Serial.println("setup: CCS811 begin FAILED");
// Print CCS811 versions
Serial.print("setup: hardware    version: "); Serial.println(ccs811.hardware_version(),HEX);
Serial.print("setup: bootloader  version: "); Serial.println(ccs811.bootloader_version(),HEX);
Serial.print("setup: application version: "); Serial.println(ccs811.application_version(),HEX);
// Start measuring
ok = ccs811.start(CCS811_MODE_1SEC);
if (!ok)
Serial.println("setup: CCS811 start FAILED");
/*
// Default settings:
//  - Heater off
//  - 14 bit Temperature and Humidity Measurement Resolutions
hdc1080.begin(0x40);
Serial.print("Manufacturer ID=0x");
Serial.println(hdc1080.readManufacturerId(), HEX); // 0x5449 ID of Texas Instruments
Serial.print("Device ID=0x");
Serial.println(hdc1080.readDeviceId(), HEX); // 0x1050 ID of the device
*/
//printSerialNumber();
}
void loop() {
// Read
uint16_t eco2, etvoc, errstat, raw;
ccs811.read(&eco2,&etvoc,&errstat,&raw);
// Print measurement results based on status
if( errstat==CCS811_ERRSTAT_OK ) {
Serial.print("CCS811: ");
Serial.print("eco2=");  Serial.print(eco2);     Serial.print(" ppm  ");
Serial.print("etvoc="); Serial.print(etvoc);    Serial.print(" ppb  ");
//Serial.print("raw6=");  Serial.print(raw/1024); Serial.print(" uA  ");
//Serial.print("raw10="); Serial.print(raw%1024); Serial.print(" ADC  ");
//Serial.print("R="); Serial.print((16501000L/1023)(raw%1024)/(raw/1024)); Serial.print(" ohm");
Serial.println();
} else if( errstat==CCS811_ERRSTAT_OK_NODATA ) {
Serial.println("CCS811: waiting for (new) data");
} else if( errstat & CCS811_ERRSTAT_I2CFAIL ) {
Serial.println("CCS811: I2C error");
} else {
Serial.print("CCS811: errstat="); Serial.print(errstat,HEX);
Serial.print("="); Serial.println( ccs811.errstat_str(errstat) );
}
delay(1000);
/*
double t = hdc1080.readTemperature();
double h = hdc1080.readHumidity();
Serial.println("T=" + String(t) + "C, Hum=" + String(h) + "%");
*/
// Wait
delay(2000);
}
// Doesn't work. Not used in the project
void printSerialNumber() {
Serial.print("Device Serial Number=");
HDC1080_SerialNumber sernum = hdc1080.readSerialNumber();
char format[12];
sprintf(format, "%02X-%04X-%04X", sernum.serialFirst, sernum.serialMid, sernum.serialLast);
Serial.println(format);
}`
My questions are,

Should I rely on this results or do I have a right implementation with the electronic set and written code? I need your comments about that.

When I enable printSerialNumber() function, the application crashes, abort() is called and ESP32 restarts and this loops forever. Here is what I see when I enable the printSerialNumber function

17:24:53.819 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
17:24:53.819 -> configsip: 0, SPIWP:0xee
17:24:53.819 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
17:24:53.819 -> mode:DIO, clock div:1
17:24:53.819 -> load:0x3fff0018,len:4
17:24:53.819 -> load:0x3fff001c,len:928
17:24:53.819 -> ho 0 tail 12 room 4
17:24:53.819 -> load:0x40078000,len:8424
17:24:53.819 -> ho 0 tail 12 room 4
17:24:53.819 -> load:0x40080400,len:5868
17:24:53.819 -> entry 0x4008069c
17:24:53.965 ->
17:24:53.965 -> setup: Starting CCS811 basic demo
17:24:53.965 -> setup: ccs811 lib  version: 10
17:24:53.965 -> setup: hardware    version: 12
17:24:53.965 -> setup: bootloader  version: 1000
17:24:53.965 -> setup: application version: 2000
17:24:53.965 -> Device Serial Number=FFFF-FFFF-FFFF
17:24:54.019 ->
17:24:54.019 -> Stack smashing protect failure!
17:24:54.019 ->
17:24:54.019 -> abort() was called at PC 0x400d70b0 on core 1
17:24:54.019 ->
17:24:54.019 -> Backtrace: 0x40089a8c:0x3ffb1ee0 0x40089cb9:0x3ffb1f00 0x400d70b0:0x3ffb1f20 0x400d0d01:0x3ffb1f40 0x400d0dc1:0x3ffb1f80 0x400d2e2f:0x3ffb1fb0 0x40085aa1:0x3ffb1fd0
17:24:54.019 ->
17:24:54.019 -> Rebooting...
17:24:54.019 -> ets Jun  8 2016 00:22:57
17:24:54.019 ->
17:24:54.019 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
17:24:54.019 -> configsip: 0, SPIWP:0xee
17:24:54.019 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
17:24:54.066 -> mode:DIO, clock div:1
17:24:54.066 -> load:0x3fff0018,len:4
17:24:54.066 -> load:0x3fff001c,len:928
17:24:54.066 -> ho 0 tail 12 room 4
17:24:54.066 -> load:0x40078000,len:8424
17:24:54.066 -> ho 0 tail 12 room 4
17:24:54.066 -> load:0x40080400,len:5868
17:24:54.066 -> entry 0x4008069c
17:24:54.219 ->
17:24:54.219 -> setup: Starting CCS811 basic demo
17:24:54.219 -> setup: ccs811 lib  version: 10
17:24:54.219 -> setup: hardware    version: 12
17:24:54.219 -> setup: bootloader  version: 1000
17:24:54.219 -> setup: application version: 2000
17:24:54.219 -> Device Serial Number=FFFF-FFFF-FFFF
17:24:54.219 ->
17:24:54.219 -> Stack smashing protect failure!
17:24:54.266 ->
17:24:54.266 -> abort() was called at PC 0x400d70b0 on core 1
17:24:54.266 ->
17:24:54.266 -> Backtrace: 0x40089a8c:0x3ffb1ee0 0x40089cb9:0x3ffb1f00 0x400d70b0:0x3ffb1f20 0x400d0d01:0x3ffb1f40 0x400d0dc1:0x3ffb1f80 0x400d2e2f:0x3ffb1fb0 0x40085aa1:0x3ffb1fd0
17:24:54.266 ->
17:24:54.266 -> Rebooting...
17:24:54.266 -> ets Jun  8 2016 00:22:57
17:24:54.266 ->
17:24:54.266 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
17:24:54.266 -> configsip: 0, SPIWP:0xee
17:24:54.266 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
17:24:54.266 -> mode:DIO, clock div:1
17:24:54.266 -> load:0x3fff0018,len:4
17:24:54.266 -> load:0x3fff001c,len:928
17:24:54.266 -> ho 0 tail 12 room 4
17:24:54.266 -> load:0x40078000,len:8424
17:24:54.320 -> ho 0 tail 12 room 4
17:24:54.320 -> load:0x40080400,len:5868
17:24:54.320 -> entry 0x4008069c
17:24:54.420 ->
17:24:54.420 -> setup: Starting CCS811 basic demo
17:24:54.420 -> setup: ccs811 lib  version: 10
17:24:54.467 -> setup: hardware    version: 12
17:24:54.467 -> setup: bootloader  version: 1000
17:24:54.467 -> setup: application version: 2000
17:24:54.467 -> Device Serial Number=FFFF-FFFF-FFFF
17:24:54.467 ->
17:24:54.467 -> Stack smashing protect failure!
17:24:54.467 ->
17:24:54.467 -> abort() was called at PC 0x400d70b0 on core 1
17:24:54.467 ->
17:24:54.467 -> Backtrace: 0x40089a8c:0x3ffb1ee0 0x40089cb9:0x3ffb1f00 0x400d70b0:0x3ffb1f20 0x400d0d01:0x3ffb1f40 0x400d0dc1:0x3ffb1f80 0x400d2e2f:0x3ffb1fb0 0x40085aa1:0x3ffb1fd0
17:24:54.520 ->
17:24:54.520 -> Rebooting...
17:24:54.520 -> ets Jun  8 2016 00:22:57
.........
.........

When I enable the code for HDC1080 module I read similar values that pashar1 user shared in this thread

Temperature 125C and Humidity 100%
and sometimes correct temp and hum values are written on logs. This may not be the scope of this thread but I would like to know if you also have any comments about this case.
Kind Regards,
Arda

@ardatekin to fixed HDC1080 125/100 problem, check this link https://github.com/closedcube/ClosedCube_HDC1080_Arduino/pull/8/commits/724a233c76da13d4e559262eb6eeaa5037d33f09 and add Wire.begin(HDC1080_SDA, HDC1080_SCL); and of course #define HDC1080_SDA 18 #define HDC1080_SCL 19
you can of course change the pins to other.

Hello,
Using VSC and PIO, I was returned errors on the CCS811_SW000246_1-00.h file.
So I used Arduino IDE and it worked like a charm. Thank you so much !
I also observed that the upper boundaries are higher now.
It was so interesting to read about other experiences. I wasn't aware about the baseline and will for sure implement it to my use case.

You mean the value<PHONE_NUMBER>?
That is a funky value: it starts with 17 ones when you write it in binary,
I'm wondering whether that is an I2C line problem (SDA stuck hi) or an internal overflow.

Finally I come to conclusion that the sensor and MCU should be wired to each other by soldering on a perfboard or pcb rather than on breadboard in order to read the most reliable results.
I have measured that there is a variable amount of resistance between the pins of sensor and MCU such as 15-24ohm.
And I also replaced esp32 with NodeMCU which looks more stable in my tests.
Resetting the i2c bus line with I2Cbus_clear when getting CCS811_ERRSTAT_I2CFAIL would also prevent getting more errors in operation time.  (https://github.com/maarten-pennings/I2Cbus)
Sharing some screens from my environment. Thank you very much Maarteen and bfaliszek

I'm glad you got it working.
Your project looks good!

Thank you @maarten-pennings !
I got ESP32 Lolin and chinese CCS881 (CJMCU-811 ? ) on yeasterday. After couple hours it was running.
I also success to update firmware to V2.0 with Arduino.

Good work!

Updated my CCS811 from Aliexpress from App Version 1.1 -> 2.0 (Arduino Uno)
Thank you @maarten-pennings and everyone else in this thread

Hi @ciphercore thanks for the feedback, I added your success to the table  https://github.com/maarten-pennings/CCS811/blob/master/examples/ccs811flash/README.md

Hello! I want to consult. We use a sensor (version 2.0.0) in a system with a deep sleep mode. The sensor is not initialized. Once every 10 minutes we take measurements. We initialize the sensor to the measurement mode once every 10 seconds. We make several measurements, at least five (values are less - we do not get the value IAQ_ECO2 at all). Then de initialization and sleep until the next cycle. General indications obtained during the measurement process are far from expected. If you start the sensor in mode 3, we do not fit into the energy budget, but the readings are satisfactory. Next option: 10 minutes in mode 0, then several measurements in mode 3, then mode 0 for 10 minutes. And this also does not help to get real measurements. Plus - in mode 0, there is also an unacceptable consumption. Question - how to use the sensor in deep sleep mode?

I do not fully understand. When your system is in deep sleep mode, what is the mode of the CCS811? Is it (power) off, is it (power) on but idle, or is it (power) on and measuring in 10sec mode (mode 2)?
The bad news for you: MOX sensors do not mix well with low power. The reason is that a MOX sensor runs a (reversible) chemical process (oxidation), and in order to do so, it needs to heat the reaction chamber. Fortunately the chamber is small, but the temperature is high (think 300C). The cost of this is of course power for the heater.
There is more bad news. The chamber needs to be in balance (the chemical process needs to be at equilibrium). Getting in balance takes time; the datasheet calls this the "run-in period" and claims it takes 20 minutes (!) "After writing to MEAS_MODE to configure the sensor in mode 1-4, run CCS811 for 20 minutes, before accurate readings are generated."
So, what does this all mean: You have to switch on the sensor, you have to wait 20 min and only then you have accurate readings. And all that time the sensor eats power.
The good news is that this sensor has a special low-power mode: mode 3 (60 seconds). It pulses the heater thereby consuming less power (1.2mW vs 46mW), but still having an equilibrium. Maybe a different equilibrium then when the heater is always on, but the internal processing knows about the mode, and presumably adapts the numbers.
Switching to a different MOX chip will not help (significantly) - the heater is part of MOX technology.
Hope this helps you.

Hello,
The sensor in standby (sleep) mode is not initialized. In this mode, the lowest consumption is observed. When the system wakes up, I put it into mode 2 (10 sec) and wait for 5..7 measurement cycles. Then I put the sensor into sleep mode (reset). And this happens every 10 minutes.
Based on your description of the sensor, you need to do a measurement cycle in mode 3 (60 seconds) for at least 20. Then do a longer sleep mode to satisfy the energy consumption budget.

Succesfully upgraded sensor board with the HDC1080.
Now using esp32 with built in oled. Displays every 2 sec on display, and posts every minute to MQTT.
(Not sure why my code isnt interpreted as code?!? How to fix?!)
For those interested:
`
// CJMCU-8128 CCS811 + HDC1080
// Wemos Lolin oled builtin.
// sda/scl pin 5,4
// Display address 0x3C
// pin 5 -> SDA
// pin 4 -> SCL
// VCC   -> VCC
// GND   -> GND
// Hint: show all mqtt topics:  mosquitto_sub -h <IP_ADDRESS> -v -t '#'
#include <SparkFunCCS811.h>     //Click here to get the library: http://librarymanager/All#SparkFun_CCS811
#include <ClosedCube_HDC1080.h> //Click here to get the library: http://librarymanager/All#ClosedCube_HDC1080
// #include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <PubSubClient.h>
#include <WiFi.h>
// esp32:
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(128, 32, &Wire, OLED_RESET);
//#define CCS811_ADDR 0x5B  //Default I2C Address
#define CCS811_ADDR 0x5A    //Alternate I2C Address
CCS811 myCCS811(CCS811_ADDR);
ClosedCube_HDC1080 myHDC1080;
uint16_t eco2,etvoc;
double temp,hum;
// Update these with WiFi network values
const char* ssid       ="yourSSIDhere"; 				//  your network SSID (name)
const char* password   ="yourwifipassword"; 			// your network password
const char* mqtt_server="<IP_ADDRESS>"; 			//your mqtt server ip
#define OUT_topic_eco2 "kamer/sensor1/eco2"  	// eco2 topic
#define OUT_topic_tvoc "kamer/sensor1/tvoc"  	// tvoc topic
#define OUT_topic_temp "kamer/sensor1/temp"  	// temp topic
#define OUT_topic_hum "kamer/sensor1/hum"  		//humidity topic
WiFiClient espClient;
PubSubClient client(espClient);
long now=0;
long L5min=0;
long L5sec=0;
void setup() {
Serial.begin(115200);
Serial.print("setup: I2C ");
Wire.begin(5,4); // esp32 wemos  lolin
// Default settings:
//  - Heater off
//  - 14 bit Temperature and Humidity Measurement Resolutions

myHDC1080.begin(0x40);
Serial.print("HDC1080 Manufacturer ID=0x");
Serial.println(myHDC1080.readManufacturerId(), HEX); // 0x5449 ID of Texas Instruments
Serial.print("Device ID=0x");
Serial.println(myHDC1080.readDeviceId(), HEX); // 0x1050 ID of the device
Serial.println("Start CCS811");
if (myCCS811.begin() == false)
{
Serial.print("CCS811 error. Please check wiring. Freezing...");
while (1)
;
}
Serial.println("Start SSD1306");
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("Connected to ");
Serial.println(ssid);
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// MQTT
client.setServer(mqtt_server, 1883);
Serial.println("Show display");
// Show image buffer on the display hardware.
// Since the buffer is intialized with an Adafruit splashscreen
// internally, this will display the splashscreen.
display.display();
delay(500);
// Clear the buffer.
display.clearDisplay();
display.display();
delay(100);
Serial.println("IO test");
// text display tests
display.setTextSize(1);
display.setTextColor(WHITE);
Serial.println("End setup.");
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
now = millis();
if (now - L5sec > 2000) // 2 sec (was 5sec. too lazy to rename variable)
{
L5sec = now;
  display.setCursor(0,0);
  display.clearDisplay();

  //Check if data is ready
  if (myCCS811.dataAvailable())
  {
    //If so, have the sensor read and calculate the results.
    //Get them later
    myCCS811.readAlgorithmResults();
    eco2=myCCS811.getCO2();
	etvoc=myCCS811.getTVOC();
	temp=myHDC1080.readTemperature();
  	hum=myHDC1080.readHumidity();

    Serial.print("eCO2: ");
    Serial.print(eco2);
    Serial.print(" ppm, TVOC: ");
    Serial.print(etvoc);
    Serial.print(" ppb, Temp:");
    Serial.print(temp);
    Serial.print(" C, Hum:");
    Serial.print(hum);
    Serial.println(" %rel");

    display.print("eCO2: ");
    display.print(eco2);
    display.println(" ppm");

    display.print("TVOC: ");
    display.print(etvoc);
    display.println(" ppb");

    display.print("Temp: ");
    display.print(temp);
    display.println(" C");

    display.print("Hum: ");
    display.print(hum);
    display.println(" %rel");

    display.display();

    //compensating the CCS811 with humidity and temperature readings from the HDC1080
    myCCS811.setEnvironmentalData(myHDC1080.readHumidity(), myHDC1080.readTemperature());

    if (now - L5min > 60000) // 1 min (was 5min. too lazy to rename variable)
     {
       L5min = now;

//           char buffer [sizeof(unsigned int)*8+1];
//           char * eco2_s = (char *) utoa(eco2,buffer,10);
//           char * etvoc_s = (char *) utoa(etvoc,buffer,10);
//           client.publish(OUT_topic_eco2, eco2_s, true);
//           client.publish(OUT_topic_tvoc, etvoc_s, true);
Serial.println("Start publish MQTT");
client.publish(OUT_topic_eco2, String(eco2).c_str(), true);
client.publish(OUT_topic_tvoc, String(etvoc).c_str(), true);
client.publish(OUT_topic_temp, String(temp).c_str(), true);
client.publish(OUT_topic_hum, String(hum).c_str(), true);
Serial.println("End publish MQTT");
}
   }
   else if (myCCS811.checkForStatusError())
   {
      printSensorError();
   }
  } // 5sec loop

}
//printSensorError gets, clears, then prints the errors
//saved within the error register.
void printSensorError()
{
uint8_t error = myCCS811.getErrorRegister();
if (error == 0xFF) //comm error
{
Serial.println("Failed to get ERROR_ID register.");
}
else
{
Serial.print("Error: ");
if (error & 1 << 5)
Serial.print("HeaterSupply");
if (error & 1 << 4)
Serial.print("HeaterFault");
if (error & 1 << 3)
Serial.print("MaxResistance");
if (error & 1 << 2)
Serial.print("MeasModeInvalid");
if (error & 1 << 1)
Serial.print("ReadRegInvalid");
if (error & 1 << 0)
Serial.print("MsgInvalid");
Serial.println();
}
}
// functions
// MQTT
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
// If you do not want to use a username and password, change next line to
// if (client.connect("ESP8266Client")) {
/*    if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) { */
if (client.connect("ESP32Client")) {
Serial.println("connected");
// subscribe to intopic
//      client.subscribe("inTopic");
} else {
  Serial.print("failed, rc=");
  Serial.print(client.state());
  Serial.println(" try again in 5 seconds");
  // Wait 5 seconds before retrying
  delay(5000);
}

}
}
`

Thanks, I added to the success table.

(Not sure why my code isnt interpreted as code?!? How to fix?!)
Maybe use triple quotes, not a single.

Stay healthy - Maarten

I have just started to use the CCS811 chip, running it on an ESP32 together with a BME280 (temp, press, humid). While the Maarten lib for the CSS811 works well, I am not sure I can give the same compliment to the chip. Similar doubts were already raised in this thread.
The analysis was created with my program GeigerLog https://sourceforge.net/projects/geigerlog/, GeigerLog pulled the data from the ESP via WiFi, and logged them. In the next graph the right-hand-side graph shows the time course of only the tracks for eCO2 (black) and TVOC (orange). The two tracks are clearly correlated.

The left graph plots all the data shown on the right as eCO2 data on the Y-axis, and TVOC data on the X-axis. It shows a two-phase correlation, perfectly linear in each, with a break at 1500ppm eCO2 . As the sensor cannot measure CO2, this value is derived from the TVOC value. So much for the magic of calculating eCO2; just a single multiplication and an 'if'.
Next I wanted to challenge the chip a little bit, and uncorked an empty (sniff ;-)) bottle of fine Single Malt Scotch next to the chip. The little lad might have liked it, but really couldn't take it. Next graph same as the first: right side: both tracks eCO2 and TVOC run into their limit.

The left side graph shows again completely linear correlation, beginning at the 1500ppm point, continuing up to the limit. According to the data sheet, the eCO2 limit is 32768ppm, and the TVOC limit is 29206. Both limits are reached exactly.
But note that when TVOC ends at 29206, eCO2 is only at about 15000ppm! It can go up for an additional 15000+ ppm. I very much doubt that this makes sense.
Has anyone ever seen any data showing that this almost trivial derivation of eCO2 from TVOC makes sense? I have not.
And also, I'd like to see data demonstrating that TVOC as reported is anywhere near any real value. It looks suspicious.

Humans emit combustible gases like hydrogen, methane and some VOCs while breathing, to which the sensor reacts. There is a nice correlation of the signal from the metal oxide sensor (which is completely insensitive to CO2) and CO2 as long as the gases are produced by humans, the correlation breaks down if VOCs are from other sources like cooking, cleaning agents or a bottle of Malt Scoth. Regarding very high concentrations of VOCs I wonder about the application. If the sensor should trigger a fan or air cleaner in an inddor environment the threashold level will be much smaller than the concentration measured close to a bottle of alcohole.
CCS811 is not a calibrated analytical instrument and performance will differ from high price lab equipment.

@nordicp : if that is so clear, where are the data published? Please, provide a link.

In continuation of my previous work ( link ), I used the same method, including the Scotch Sniff Test, and additionally recorded the resistance. I then plotted TVOC and eCO2 vs resistance, as shown in the graph:

The TVOC vs R curve can be fitted reasonably well with a 5th degree polynomial (equation in the graph). But only up to a certain point. The TVOC curve then shows saturation at the known limit of 29206 at a resistance of about 10kOhm.
Strangely, they let the eCO2 curve continue to its max of 32768 until a resistance of about 5kOhm. This explains the strange data when eCO2 was plotted against TVOC, see above. This implies that the eCO2 data are derived directly from resistance, and not from the TVOC. Somewhat strange, given that eCO2 should be an indirect derivation from TVOC.
Within the eCO2 curve one can see the breakpoint between the two phases at ~95kOhm.
Obviously the sensor is extremely non-linear vs resistance with barely a change in TVOC from  150kOhm down to 80kOhm, followed by major and increasing steps for every 10kOhm down.
So, while the chip can be used as a breathalyzer for serious alcoholics ;-) I am still not sure what its value is for monitoring my home, or any other place?  Do others have more on the "Reliability of measurements"?

The first iAQ sensors using the "equivalent CO2" output were introduced 2007. eCO2 should be interpreted as an air quality unit and can directly be used for driving a ventilation system which was set to CO2 threasholds, so there is no need to change the settings when changing to another sensor principle.
https://www.co2meter.com/blogs/news/5185132-co2-sensors-vs-voc-sensors-for-iaq-whats-the-difference
"All VOC air quality sensors are basically the same. Some manufacturers of air quality sensors are now providing an output in “CO2 equivalent units.” This measure is considered misleading and may confuse many new to the indoor air quality industry."
Logging of eCO2 and CO2 from an NDIR sensor will most likely show good correlation in offices, sleeping rooms but bad or no correlation in kitchens and bathrooms.
Your finding that eCO2 is derived from resistance is almost correct. Because this sensor is running a baseline correction algorithm it will update its baseline in certain intervals. Adding the rh/T compensation feature (which is included in Maarten's library) will also change the resistance to eTVOC (and eCO2) correlation.
Please keep in mind that this sensor was developed for indoor air quality applications and correct absolute readings are not priority as long as bad air is ventilated off. Because MOX sensors are not selective and systems cannot be recalibrated by the end user the readings of simple breathalizers (available in electronic stores) are pretty unreliable. Same is true for this little sensor module, it is not designed for such an application.
I am running some iAQ sensors at home and I can easily see if somebody is at home, if cooking is  going on or if the windows are open.

@nordicp : thanks for the link to this 8 year old article, confirming my newly found skepticism to "misleading measures".
"Correct absolute readings" aren't my problem, selectivity, as you call it, may be a better description. When you check your luggage weight at home with a cheap scale, you know it isn't very accurate. But at least you know it is measuring weight, and not volume, or temperature, or else.
But here that device would also be telling you the eDistance value as the miles you will be travelling. Admittedly, there might be a correlation between suitcase weight and distance, but there may be plenty of other reasons for high or low weight. It is some hyping up the ability of the sensor.
Still, no data in the article, nor linked to. If you could share some of your logs that, as you say, show distinguishable home-style activity, I would highly appreciate that.
I am sitting here at my desk, nobody else nearby, at a reasonably well ventilated place, the sensor nearby, and every now and then the sensor output jumps very high, comes down, and slowly creeps up higher and higher. Does not make sense.

Something mysterious. Either this or a bug in the official CCS811 datasheet.
I keep experimenting, and this is another Scotch-Sniff-Test.

The sensor is overwhelmed and goes into its limits: orange is eTVOC and maxes out at 29206, and green is eCO2 and maxes out at 32769. This is exactly what I found previously.
And the very same numbers are mentioned in the data sheet - except they say it is the other way round. Here a quote from the ams Datasheet v1-06] 2019-Feb-07, Page 12:

eCO2:
The equivalent CO2 (eCO2) output range for CCS811 is from 400ppm up to 29206ppm.
eTVOC:
The equivalent Total Volatile Organic Compound (eTVOC) output range for CCS811 is from 0ppb up to 32768ppb.

On Feb 20, 2019 ams had issued a note saying that those values had been changed. So I looked up earlier datasheets and found in issue 2018-Mar-09:

eCO2
The equivalent CO2 (eCO2) output range for CCS811 is from 400ppm to 64000ppm.
TVOC
The Total Volatile Organic Compound (TVOC) output range for CCS811 is from 0ppb to 64000ppb.

And an even older one from  2016-Dec-23:

eCO2
The equivalent CO2 (eCO2) output range for CCS811 is from 400ppm to 8192ppm. Values outside this range are clipped.
TVOC
The Total Volatile Organic Compound (TVOC) output range for CCS811 is from 0ppb to 1187ppb. Values outside this range are clipped.
This is calibrated to a typical TVOC mixture in an indoor environment. If the ratio of compounds in the environment is significantly different the TVOC output  will  be  affected  as  some  VOC compounds will have greater or lesser effect on the sensor.

Very different! (As an aside, note that the caveat (bold by me) in the last sentence has been dropped completely in the later releases!)
Did the lib get it wrong? The *cpp has these lines:
      // Outputs
      if( eco2   ) *eco2   = buf[0]*256+buf[1];
      if( etvoc  ) *etvoc  = buf[2]*256+buf[3];
      if( errstat) *errstat= combined;
      if( raw    ) *raw    = buf[6]*256+buf[7];;

Nope, is at least in agreement with the datasheet, so it seems to me:

And my hard-, firm-, and software all seem to be the latest:

11:11:19.442    Found CCS811 lib version : 0x0A
11:11:19.555    Found hardware    version: 0x12
11:11:19.663    Found bootloader  version: 0x1000
11:11:19.770    Found application version: 0x2000
11:11:19.918    Found Sensor CCS811 w. ID: '0x81', ('129')

Can anybody confirm this screw-up?

Confirmed. The PCN for the changes of max. readings is missing and your findings regarding the confusion of the numbers are correct. The note on calibration to a VOC mixture is dropped because such information is not leading anywhere and explained in the second sentence "If the ratio of compounds in the environment is significantly different the TVOC output will be affected as some VOC compounds will have greater or lesser effect on the sensor." Consequently the text got removed in later versions of the datasheet. In section "Automatic Baseline Correction"  it is mentioned that "RA cannot be determined by a one-time calibration; it is maintained on-the-fly in software." Calibrated sensors will need recalibration after some time which is impossible in field installations and typically cannot be performed by the unexperienced end user. Calibration is also adding costs in productions. So all cheap gas sesor modules use some sort of auto calibration, same is true for single beam NDIR CO2 sensors.
I also do not recommend tests with very high concentrations of VOCs because the module is designed for the lower range 400 to 5000ppm eCO2 output.

Oh, good to know that I am not beginning to hallucinate ;-)
Your answer sounds as if you were somehow involved with the manufacture(r) of the chip. Are you?
In my years working as physicist I had to deal with data of even a lower quality than what one gets with a mox sensor. That in itself is not a problem. Neither is the non-calibrationability of the sensor. But I expect some reasonable reproducibility, and meaningful values, not muddled with some marketing language!
Here are data from last overnight run. The room window - ~1m apart - was open during the night, no people or other warm blooded animals (a fly bugged me) were around, so from midnight to 8am the room had time to equilibrate to the outside. So look what I got:

The eTVOC never went below 300 (ppb?). My experience does not allow me to judge TVOC, but eCO2 is estimated based on the same resistance data, and eCO2 at the end of the night was never lower than 1700ppm!
No way! CO2 should have been near the 400ppm value for outside air. And as I had logged data every second, the sensor sure should has had time to stabilize.
Given that CO2 is so far off, I can only conclude that TVOC is also way off any relevant value.
One thing I noticed: in one of the datasheets an example was given referring to a "resistance of 350kOhm". I never see anything higher than 150kOhm on my chip. Is that still within a normal manufacturing range for these CCS811 chips?

This CCS811 chip is giving nothing but troubles; I will give up on it. Is it a principle defect of the chip? Is my chip defect? Is the lib defect? (I don't think so. At least I did not find any issues).
I bought other MOX chips to compare it with: a SGP30 also produces eTVOC and eCO2, but no resistance, and a BME680, which only delivers resistance, but neither eTVOC nor eCO2. They offer a closed source blob to install, which is claimed to deliver some "Air Quality Index" (not tried yet).
With all 3 connected to my ESP32 at the same time, I got these results for overnight or daylong runs:

The BME680 resistance is around 400kOhm (divided by 5 for plotting), that of the CCS811 is only 55kOhm. This in itself is not a problem, but look at this garden fence of data. The inset shows an enlargement of one of the fence posts, which appear consistently every ~4.3minutes, and change the resistance by almost 20%!
Oh, maybe I should set the baseline, so here is the baseline overnight:

Which baseline should I pick? There are more than 64000 to choose from. Note the almost constant decrease from shortly before midnight for the next multiple hours, when everybody in the house was fast asleep, far away from the sensor.
I have really no sense of what the eTVOC should be. But I do have a sense for the CO2 level. Since all sensors CANNOT measure CO2, they estimate it from the eTVOC data, assuming some kind of a standard human. So, CO2 stands as a proxy for eTVOC. And in reverse conslusion: if CO2 is way off, then TVOC is also!
To get some better feeling at least for CO2 I purchased a little NDIR based CO2 monitor. A software for logging is on my Sourceforge site: https://sourceforge.net/projects/minimon/ . Its outside value of 430 ppm seems to be surprisingly good (expect anything greater than but near 416), and an inside value near my desk of 500 ... 700 ppm is at least consistent with expectations for a single person in a mid size room. What are the sensors showing:

The SGP30 gives eCO2 between 400 and 500 ppm. Seems like a bit on the low side, but at least the curve is smooth.
The CCS811 now gives the inverted garden fence. Now the fence poles dip down to 500...700ppm, which is exactly what I would expect, but the other end goes up to 1400 ppm, which is improbable.
Overall, this chip so far produces utter nonsense. If anyone finds a problem in the lib, I'll try again. But otherwise this is my end to the adventure with the CCS811.

I recognize find of your issues, but i have never seen this garden fence. Do you run it in mode always on? Did you try a second one?

I have seen similar "fences" with some example code for Arduino for the MH-Z14 NDIR sensors that various blogs copy-pasted from each other.
If I remember correctly, the fences were caused by a problem in the code. The data was received from the serial port as bytes, Temperature, CO2 ppm MSB, CO2 ppm LSB, checksum, etc. and put into a char array. When the "ppm" was calculated, the MSB and LSB where cast as "int" when calculating the ppm so the reported value would drop by 256  when the LSB went from 0x7F to 0x80.
Maybe adding some code to print out the raw bytes read from the sensor and calculated value could help find the cause of the fence.

@maarten-pennings  I use it with this setting ok = CCS811.start(CCS811_MODE_1SEC);. Are you asking for a second run? I bought this chip about 3 weeks ago, and I am running it basically continuously since then. Should have been well burned in, I think?
@mkgin  I see, so a problem because int was used when uint should have been? I went through the lib but found no issue. But don't take my word, do it also. @maarten-pennings May be you can also have a look re this idea of mkgin. I'd be surprised, but one never knows.

CCS811 does a 24h baseline correction, the baseline should not jump up/down during the time. Is this the value from address 0x11 "Baseline"? In case yes, it is an encoded basline which includes information about baseline and current eCO2 output (see application note Baseline Save and Restore). This specific value is not constant.
Regaring the fence: I have seen quite a lot of data from CCS811 but never such a signal. If the setup is on breadboard I would check for voltage drops caused by the ESP32.

The lib command to call the baseline is:
    #define CCS811_BASELINE         0x11 // 2 bytes
    ....
    // Reads (encoded) baseline from BASELINE (see datasheet). Returns false on I2C problems.
    bool CCS811::get_baseline(uint16_t *baseline) {
      uint8_t buf[2];
      wake_up();
      bool ok = i2cread(CCS811_BASELINE,2,buf);
      wake_down();
      *baseline= (buf[0]<<8) + buf[1];
      return ok;
    }

The wake_up/downs do nothing as my wake pin is permanently grounded. So, it is using 0x11 address, and it should give the correct reading. The application note on baseline is not helpful here, as the baseline is everywhere between zero and 64k, and does not seem to stabilize anywhere. Not even temporarily.
Blaming a voltage drop is a perhaps dangerous proposition, as the competing sensors SGP30 and BME680 are not as finicky about such a presumed voltage drop, and do perform smoothly!
I have no idea where the fence comes from, but it is reproducible.

@maarten-pennings i am new to flashing. The .bin files where to put this for using your tutorial
https://github.com/maarten-pennings/CCS811/tree/master/examples/ccs811flash
And does this fixe the going to static value when unplugging usb?
Regard
Eric

Hi Eric, it's all explained in the readme:

I have downloaded the 2.0.x firmware from the ams pages. I have written a Python script hex.py, Python 3.x - not Python 2.x. It is used to convert a binary firmware file to a C-array [...] Finally, I have written a sketch ccs811flash.ino that uses this array to flash the CSS811.

In other words, you don't need the bin, its converted version is included in the ino.
Your second question ... do not understand. The ccs811 doors not have USB.

thank you, clear
i mean wheb powerup(i use the MicroBit coded with arduino software) the css811 runs and give proper value. When disconnect the usb(power) the value goes to some standard tvos 6501 and co2 4301

The CCS811 has a resistor Rs that varies with air quality. The tvoc value is roughly Rs/R0, where R0 is the resistance for cleanest air seen by the sensor. R0 is also known as "baseline" and is maintained by the sensor as long as it is powered. Once the sensor is switched off, R0 is lost. After a new power on, it takes some time before a good R0 is determined by the sensor.
So, what you can do is x=get_baseline () before you switch off, and set_baseline(x) after you switch on. But read the datasheet for details on when to do this exactly.

I'm currently also playing around with a CCS811.  Then it jumps ahead - weird.
I try to read the "baseline" and monitored the values. Here is the log from while starting the module (already burned in module, but freshly fired up - after 2 hours of cool down)
Baseline: 65279
Heating up. Wait 1174 seconds till system is initialized
Humidity: 44.50%  Temperature: 21.20°C
1111 1110 1111 1111

Baseline: 65471
Heating up. Wait 1024 seconds till system is initialized
Humidity: 44.10%  Temperature: 21.40°C
1111 1111 1011 1111

Baseline: 50879
Heating up. Wait 965 seconds till system is initialized
Humidity: 43.90%  Temperature: 21.50°C
1100 0110 1011 1111

Baseline: 40127
Heating up. Wait 907 seconds till system is initialized
Humidity: 43.70%  Temperature: 21.60°C
1001 1100 1011 1111

Baseline: 33983
Heating up. Wait 848 seconds till system is initialized
Humidity: 43.50%  Temperature: 21.60°C
1000 0100 1011 1111

Baseline: 29119
Heating up. Wait 790 seconds till system is initialized
Humidity: 43.40%  Temperature: 21.70°C
0111 0001 1011 1111

Baseline: 24767
Heating up. Wait 730 seconds till system is initialized
Humidity: 43.20%  Temperature: 21.80°C
0110 0000 1011 1111

Baseline: 19903
Heating up. Wait 671 seconds till system is initialized
Humidity: 43.10%  Temperature: 21.90°C
0100 1101 1011 1111

Baseline: 17343
Heating up. Wait 615 seconds till system is initialized
Humidity: 42.90%  Temperature: 21.90°C
0100 0011 1011 1111

Baseline: 14271
Heating up. Wait 549 seconds till system is initialized
Humidity: 42.80%  Temperature: 22.00°C
0011 0111 1011 1111

Baseline: 11711
Heating up. Wait 493 seconds till system is initialized
Humidity: 42.60%  Temperature: 22.00°C
0010 1101 1011 1111

Baseline: 9151
Heating up. Wait 436 seconds till system is initialized
Humidity: 42.70%  Temperature: 22.10°C
0010 0011 1011 1111

Baseline: 7103
Heating up. Wait 380 seconds till system is initialized
Humidity: 42.70%  Temperature: 22.10°C
0001 1011 1011 1111

Baseline: 2751
Heating up. Wait 323 seconds till system is initialized
Humidity: 42.60%  Temperature: 22.20°C
0000 1010 1011 1111

Baseline: 1215
Heating up. Wait 204 seconds till system is initialized
Humidity: 42.40%  Temperature: 22.20°C
0000 0100 1011 1111

Baseline: 447
Heating up. Wait 144 seconds till system is initialized
Humidity: 42.40%  Temperature: 22.20°C
0000 0001 1011 1111

Baseline: 447
Humidity: 42.40%  Temperature: 22.20°C
0000 0001 1011 1111

After that, it stayed constantly for a good hour at this baseline value.
Now, I opened the window and moved the sensor (within a case already) out the window (below 10°C out here):
Baseline: 26302
Humidity: 32.10%  Temperature: 22.80°C
0110 0110 1011 1110

Baseline: 58045
Humidity: 32.40%  Temperature: 21.70°C
1110 0010 1011 1101

Baseline: 26045
Humidity: 34.70%  Temperature: 20.00°C
0110 0101 1011 1101

Baseline: 62652
Humidity: 37.70%  Temperature: 18.40°C
1111 0100 1011 1100

Baseline: 28604
Humidity: 41.90%  Temperature: 16.40°C
0110 1111 1011 1100

Baseline: 444
Humidity: 46.60%  Temperature: 14.40°C
0000 0001 1011 1100

After turning back into my room, the baseline value didn't changed again.
Feels like, the upper two bytes (most left) are an indicator for warming up the sensor. The second lower byte (second from left) kept constant and the lower byte (most left) changed (baseline value itself?).
With raising temperature, the eCO2 values raised a lot. Before the window-experiment I was at 410 ppm, after the window-experiment I'm now at 1730 ppm. I don't think, that opening the window polluted my room air in such a way.
I will monitor this behavior for the next days and will do some experiments. I guess, I will include a "calibration-button" in my project, so when I press it while starting, the system will run into a calibration mode. Then it will heat-up for 30 minutes, test, if the upper 2 bytes are equal "1" and will record the threshold. Then it will set the threshold right before every measurement.
Anyone else with more knowledge about this "encoded threshold" and how it is build?

I'm sure what you want to achieve.
The baseline register contains ...  the ... baseline. That is, the highest resistor value seen in the last day. The resistor has a large dynamic range, so maybe it is coded in a short of floating point format. Likely, another parameter is also stored (time?). All of this is packed, and maybe encrypted.
Why do you care?
You retrieve it before the ccs811 goes to sleep, and restore it after the ccs811 wakes up and is stable again.

You retrieve it before the ccs811 goes to sleep, and restore it after the ccs811 wakes up and is stable again.
depends on what "go to sleep" means. Switch off and let cool down, and switch it on again later?

Or do yo mean during runtime, pushing ccs811 to sleep, wake up, restore, measure and sleep again?
If you mean the second one, then the pushing to sleep and waking up is handled by your library. So if I request a measure, the lib automatically wake-up, get measurements and set to sleep again.
Nevertheless, it's quite interesting, how this baseline behaves.
In an additional test-round (after a night of cool-down) I monitored the changes on the baseline again.
It changed a lot during the warm-up (20 minutes), but changed another 17 times right after this 20 minutes - in a period of  another half an hour.
Every 2-3 minutes a new baseline was stored/calculated by the ccs811.
After this, the baseline kept constant for hours.
I still stay with the idea of "calibration" or "nullification". So add a button to my measure box. If this button was pressed during start, warm-up for 20 minutes and then watch the baseline till there is no change (for 20 minutes?). Then record this baseline value, store it in a non-volatile register and check right before every requested measurement, if the baseline is still the same. If not, push the stored to ccs811.
If the whole system was switched off and on again, wait for 20 minutes and restore the "calibrated" baseline into the ccs811 (if the calibration button was not pressed at start-up).
But it would be still interesting, what exactly this two bytes about. Looking to the last record of base-lines, the lower byte stayed quite constant, and the upper byte decreased from record to record (just in the first few seconds, the upper byte jumped a bit around, but still lower byte kept more or less constant)

BL save and restore is a nice feature to speed up the tie the sensor needs to get used to the environment. In case the sensor is not moved to another location and a previously saved BL is available the readings will be close to the readings before power off. The BL itself carries information about sensor resistance and air quality at the time the BL was saved.
A guideline is available here:
https://www.sciosense.com/wp-content/uploads/2020/01/Application-Note-Baseline-Save-and-Restore-on-CCS811.pdf
Copied from page #4: "The baseline value is not simply the raw resistance value of the sensor in clean air, it also includes some additional factors used by the internal algorithms.  It is not in a human readable format..."
The BL save and restore feature is not meant for power saving but to cover maintenance or power outage.
For checking the warm-up I read sensor resistance and write back the BL when resistance data starts leveling out, a well run-in sensor typically does not need more than 5min of warm-up.
I like the idea of "nulling", unfortunately CCS811 does not offer setting the reading to a self defined air quality. If the sensor would run in a room and it would be possible to press button and restore a BL coded from resistance and "good air" 400ppm eCO2 (or another value of choice) the sensor could be set to perceived or measured air quality from another device.
Unfortunately I can not find further information on how the BL is calculated and the development team is no longer available.

Indeed I didn't use the correct wording. Baseline extract and restore is over power down and up (I edited the wording in my previous post). If you put the ccs811 to sleep with the wake pin it turns itself on now and then to heat up (see Figure 10: CCS811 Power States).

Tried to flash a chinese version of ccs811 sensor and it failed for some reason.
Starting CCS811 flasher
setup: library     version: 10
ccs811: Wrong HW_ID: FD
setup: CCS811 begin FAILED
setup: hardware    version: 12
setup: bootloader  version: 1000
setup: application version: FFFFFFFF
setup: starting flash of 'CCS811_FW_App_v2-0-0.bin' in 5 seconds
ccs811: ping ok
ccs811: reset ok
ccs811: status (reset1) 20 ERROR - ignoring
ccs811: app-erase ok
ccs811: status (app-erase) 40 ok
ccs811: writing 5120 ...........................ccs811: app data failed
setup: CCS811 flash FAILED
pin WAK connected to GND, the version of firmware was 1100 (before flashing),
flashing on pro mini atmega168*16Mhz, what can I do make it flash?

Hi,
I am a bit confused about the output you get.
Starting CCS811 flasher
setup: library version: 10
ccs811: Wrong HW_ID: FD    setup: CCS811 begin FAILED
setup: hardware version: 12
setup: bootloader version: 1000
setup: application version: FFFFFFFF
The bold lines suggest that the application firmware is not on the ccs811.
Typically due to a failed flash before.
Not to worry though, the bootloader is still there. We see that from the
green lines.
I do not know why the flash is not working. Is there an electrical problem?
I'm not happy with the atmega? Isn't that a 5V controller? The ccs811 runs
on 3v3.
Need i2c pull-ups?
Are you using hardware i2c bus?
Stable power supply?
Good luck,
Maarten
On Sat, May 22, 2021, 18:56 rost12345 @.***> wrote:

Tried to flash a chinese version of ccs811 sensor and it failed for some
reason.
Starting CCS811 flasher
setup: library version: 10
ccs811: Wrong HW_ID: FD
setup: CCS811 begin FAILED
setup: hardware version: 12
setup: bootloader version: 1000
setup: application version: FFFFFFFF
setup: starting flash of 'CCS811_FW_App_v2-0-0.bin' in 5 seconds
ccs811: ping ok
ccs811: reset ok
ccs811: status (reset1) 20 ERROR - ignoring
ccs811: app-erase ok
ccs811: status (app-erase) 40 ok
ccs811: writing 5120 ...........................ccs811: app data failed
setup: CCS811 flash FAILED
pin WAK connected to GND, the version of firmware was 1100 (before
flashing),
flashing on pro mini atmega168*16Mhz, what can I do make it flash?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/maarten-pennings/CCS811/issues/8#issuecomment-846435387,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AHH3FQ26UDBUAJFMFBRP7LLTO7O4FANCNFSM4GKU4KKQ
.

Tried on another arduino board (at328) and it flashed OK. Thanks a lot.

Hello, I have had the same problem with two CJMCU-811 sensors (Chinese modules).
The first one connected (I2C, 3v3) to a Raspberry Pi 4 worked correctly until after about two months (approx.) it began to give read errors.
The second one connected (I2C, 3v3) in a Blue Pill also worked correctly and after two months (approx.) it also began to fail to read.
This shows up on both sensors with Arduino Uno and ccs811basic.ino:
CCS811: errstat = 1091 = - vHxmrwF - Ad-iE
CCS811: errstat = 1091 = - vHxmrwF - Ad-iE
CCS811: errstat = 1891 = - vHXmrwF - Ad-iE
CCS811: errstat = 1899 = - vHXmrwF - AD-iE
CCS811: errstat = 1899 = - vHXmrwF - AD-iE
CCS811: errstat = 1899 = - vHXmrwF - AD-iE
As I have read, the sensors may be bad, the transducer heater could be broken, but I have a hunch that it is a problem with this module that after a while the heater breaks.
It can be that? Any other idea?
Any solution?

The uppercase HX in errstat are explained in the datasheet.

The sensor resistance measurement has reached or exceeded the maximum -- MAX RESISTANCE
The Heater current in the CCS81 I is not in range -- HEATER FAULT

(By the way, a status string has 5 times a "-", in yours I see only three)
I have had css811 sensors in use for more than two month, and they didn't burn their heater. Isn't the raspberry pi 5 volt? That is too much for the ccs811.

Hi Maarten, thank you for your quick reply.
Actually the status string has 5 times "-", during "copy and paste" process it would be changed. Here it is:
setup: Starting CCS811 basic demo
setup: ccs811 lib  version: 10
setup: hardware    version: 12
setup: bootloader  version: 1000
setup: application version: 2001
CCS811: waiting for (new) data
CCS811: errstat=1091=--vHxmrwF--Ad-iE
CCS811: errstat=1091=--vHxmrwF--Ad-iE
CCS811: errstat=1891=--vHXmrwF--Ad-iE
CCS811: errstat=1899=--vHXmrwF--AD-iE
CCS811: errstat=1899=--vHXmrwF--AD-iE
CCS811: errstat=1899=--vHXmrwF--AD-iE
CCS811: errstat=1899=--vHXmrwF--AD-iE
I connected the sensor (on both boards: RPI4 and BluePill) to 3v3 and used a level shifter module (5v - 3v3) for I2C bus.
Pictures show the two sensors (addresses 0x5A and 0x5B) connected (to I2C via 4 way Level converter module) to my Arduino UNO in order to the current tests.

You covered everything - I'm out of options... :-( sorry

I believe the new firmware uses special algorithm at the early life of the sensor. When early life is over, it uses the standard algorithm. The firmware knows when early life is over by persistently storing the run time on the sensor. The special algorithm at early life is needed, because the chemicals in the sensor are not yet "baked" to stability. Again, all this is what I mentally model from the documentation.
Since the old firmware did not have this algorithm, it also did not store the run time.
So, the 2.0.0 firmware is good for fresh "non-baked" sensors. But if you would use it on a baked sensor, the firmware would (incorrectly) apply the special algorithm the first couple of days, giving suboptimal readings. However, after the first couple of days, the persistent timer reaches the end-of-early-life value and switches to the standard algorithm, so your readings will be good from then onwards.

Hello Maarten, first of all, I appreciated a lot your work! Thanks!
We included your library on several thousands of our ESP32 based Indoor Environment Monitors (see https://www.aircare.it/en/home-eng/ and also https://www.daikinapplied.eu/news-center/introducing-the-new-daikin-iaq-sensor/ ).
We recently upgraded CCS811 firmware on field (from 1.1 to 2.0.x) using our FOTA service and your CCS811 firmware update code.
Your code worked fine and firmware is properly upgraded on thousands of sensors on field. Thank you again!
Our application software selects the rigth new firmware (2.0.0 or 2.0.1) based on single device/sensor working time, stored on device file system at baseline save time as stated in AMS application note.
Now we gain larger TVOC/eCO2 range compared with 1.1 sensor firmware, but we aren't so confident that now the sensor works well on first 48/60 hours after a power-cycle.
Are you sure/confident that the 2.0.0 firmware uses internal persistent storage for accounting sensor early-life, or it's just your guess?
If so, why don't use the internal persistent storage also for tracking baseline without host save/restore needs?
It'seems that using the 2.0.0 firmware on new sensor the measurements are higher than expected during first 48/60 hours after a power-on then, suddently TVOC drops (and stays) to more plausible values.
We never seen such amplified behaviour while using 1.1 firmware in more or less three years on the field
Your opinion on such behaviour will be appreciated
Thanks
Paolo

Hi Paolo,
Thanks. The monitors look great. It scares me a bit that you use my hobby project for so many products!
The difference between 2.0.0 and 2.0.1 is how I interpreted the documents, so my guess.
I believe the internal storage is not reliable enough for many write cycles; so it is good enough for recording the first couple of days the time, but not good enough for recording baselines for 10 years.
I believe you can downgrade the 2.0.x firmware back to 1.1.
Good luck!

I have a quick question. I am using the CCS811 chip to measure TVOC and CO2 only, as I have a temp reader set up separately. In demos/dry runs, the CCS811 chip works well for ~14 hours. However around that 14th hour, the chip experiences a massive drift upwards for approx 10-15 min usually to near its max values, before slowly dropping down to normal levels over the course of the next few hours. Using many different chips to test the issue, I have found that it does in fact drift for all them. Reviewing cameras and such, I have found that nothing has actually changed to affect the TVOC or CO2 levels. Is this a common issue found by everyone? Have I set the baseline incorrectly causing the large shift? I have not recently (read ever) updated the firmware on the chips, would possibly upgrading it to 2.0.0 or 2.0.1 or downgrading it back solve it? Is it another issue that I am unaware of? Obviously I have many solutions to test but was wondering if anyone had this issue and knew how to fix it quickly.

I do not believe the baseline is involved here. Roughly the baseline works as follows.
A sensor has a resistance depending on the gases in the air.  The cleaner the air the lower the resistance. Unfortunately, this relation is not stable in time (resistance changes due to what happened in the past). So what the firmware of a mox sensor does is record the lowest resistance it has seen - the cleanest air. Let's call this R0. When you perform a measurement, the sensor measures the current resistance, let's call it Rc, and it roughly outputs Rc/R0. The problem is, that the sensor resistance for clean air changes in time, so the sensor needs to adapt R0. The firmware has algorithms to measure R0 on a daily basis (24 hour cycle). R0 is called the "baseline", and can also be set manually. So setting the baseline might cause a jump at that moment, and they might be changes at the 24hr cycle. I do not expect jumps after 14 hours, due to baseline management.

I'm currently also playing around with a CCS811.  Then it jumps ahead - weird. I try to read the "baseline" and monitored the values. Here is the log from while starting the module (already burned in module, but freshly fired up - after 2 hours of cool down)
Baseline: 65279
Heating up. Wait 1174 seconds till system is initialized
Humidity: 44.50%  Temperature: 21.20°C
1111 1110 1111 1111

Baseline: 65471
Heating up. Wait 1024 seconds till system is initialized
Humidity: 44.10%  Temperature: 21.40°C
1111 1111 1011 1111

Baseline: 50879
Heating up. Wait 965 seconds till system is initialized
Humidity: 43.90%  Temperature: 21.50°C
1100 0110 1011 1111

Baseline: 40127
Heating up. Wait 907 seconds till system is initialized
Humidity: 43.70%  Temperature: 21.60°C
1001 1100 1011 1111

Baseline: 33983
Heating up. Wait 848 seconds till system is initialized
Humidity: 43.50%  Temperature: 21.60°C
1000 0100 1011 1111

Baseline: 29119
Heating up. Wait 790 seconds till system is initialized
Humidity: 43.40%  Temperature: 21.70°C
0111 0001 1011 1111

Baseline: 24767
Heating up. Wait 730 seconds till system is initialized
Humidity: 43.20%  Temperature: 21.80°C
0110 0000 1011 1111

Baseline: 19903
Heating up. Wait 671 seconds till system is initialized
Humidity: 43.10%  Temperature: 21.90°C
0100 1101 1011 1111

Baseline: 17343
Heating up. Wait 615 seconds till system is initialized
Humidity: 42.90%  Temperature: 21.90°C
0100 0011 1011 1111

Baseline: 14271
Heating up. Wait 549 seconds till system is initialized
Humidity: 42.80%  Temperature: 22.00°C
0011 0111 1011 1111

Baseline: 11711
Heating up. Wait 493 seconds till system is initialized
Humidity: 42.60%  Temperature: 22.00°C
0010 1101 1011 1111

Baseline: 9151
Heating up. Wait 436 seconds till system is initialized
Humidity: 42.70%  Temperature: 22.10°C
0010 0011 1011 1111

Baseline: 7103
Heating up. Wait 380 seconds till system is initialized
Humidity: 42.70%  Temperature: 22.10°C
0001 1011 1011 1111

Baseline: 2751
Heating up. Wait 323 seconds till system is initialized
Humidity: 42.60%  Temperature: 22.20°C
0000 1010 1011 1111

Baseline: 1215
Heating up. Wait 204 seconds till system is initialized
Humidity: 42.40%  Temperature: 22.20°C
0000 0100 1011 1111

Baseline: 447
Heating up. Wait 144 seconds till system is initialized
Humidity: 42.40%  Temperature: 22.20°C
0000 0001 1011 1111

Baseline: 447
Humidity: 42.40%  Temperature: 22.20°C
0000 0001 1011 1111

After that, it stayed constantly for a good hour at this baseline value. Now, I opened the window and moved the sensor (within a case already) out the window (below 10°C out here):
Baseline: 26302
Humidity: 32.10%  Temperature: 22.80°C
0110 0110 1011 1110

Baseline: 58045
Humidity: 32.40%  Temperature: 21.70°C
1110 0010 1011 1101

Baseline: 26045
Humidity: 34.70%  Temperature: 20.00°C
0110 0101 1011 1101

Baseline: 62652
Humidity: 37.70%  Temperature: 18.40°C
1111 0100 1011 1100

Baseline: 28604
Humidity: 41.90%  Temperature: 16.40°C
0110 1111 1011 1100

Baseline: 444
Humidity: 46.60%  Temperature: 14.40°C
0000 0001 1011 1100

After turning back into my room, the baseline value didn't changed again. Feels like, the upper byte (most left) are an indicator for warming up the sensor. The lower byte (left) kept constant before opening the window and changed (slightly) after I placed sensor outdors. With raising temperature, the eCO2 values raised a lot. Before the window-experiment I was at 410 ppm, after the window-experiment I'm now at 1730 ppm. I don't think, that opening the window polluted my room air in such a way.
I will monitor this behavior for the next days and will do some experiments. I guess, I will include a "calibration-button" in my project, so when I press it while starting, the system will run into a calibration mode. Then it will heat-up for 30 minutes, test, if the upper byte is equal "1" and will record the threshold. Then it will set the threshold right before every measurement.
Anyone else with more knowledge about this "encoded threshold" and how it is build?

Baseline is encoded as big-endian uint16, just swap bytes with __builtin_bswap16

Seems like I solved baseline mystery finally.

As @shooorf mentioned, you should swap 2 bytes: 0x48B6 -> 0xB648
Then invert it bitwise ~baseline: 0xB648 -> 0x49B7. After that you already will see some sensible correlations between resistance and baseline.
Finally using modifiend baseline we can calculate R0 as: R0 = 2 ** (baseline / 2 ** 10), or the other way if current resistance is higher than baseline, baseline could be updated as: baseline = 2 ** 10 * log2(R)

So, when sensor is exposed to fresh air, the baseline gets updated to store higher resistance level, however if you monitor baseline for some time, you'll notice that it also changes continuously in not so fresh air. If you apply those 3 transformations above, you can easily spot, that when air is not fresh, baseline gets decreased every few minutes with a steady pace equivalent to ~2,5 kOhms/hour or 60 kOhms/day. It means, that if you will put your sensor in very dirty space and keep it there for a few days, eventually the baseline will reach current resistance level corresponding to dirty air, and it will report that the air is fresh (tVOC=0, eCO2=400). In more realistic scenario, if you open window before going to bed and keep it closed for the whole night, then R0_night used to calculate tVOC in the beginning of night will be different from R0_morning by 2500 * 8 hours = 20 kOhms. So, to have tVOC & eC02 measurements comparable and realistic, you need either ventilate your place quite frequently or restore baseline periodically (maybe every hour or even minute), especially if your place not being ventilated too often.
Knowing R and R0, we can see pretty simple relationship between R/R0 and tVOC:

Also, I faced similar "fence" as @ullix mentioned:

I already started to blame CCS811, however after some experiments I found that if I removed the other sensor (Senseair S8), that was connected to same ESP32 GND pin, the fence went away. I don't know why it is so, and whether or not there was a similar issue in @ullix case, but that's just my experience.

hi All, I'm trying to verify the firmware version in my CCS811 sensor, but I get this error while bulding a ccs811basic.ino example:
Linking .pio/build/d1_mini_lite/firmware.elf /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.setup+0x2c): undefined reference to _ZN6CCS81112set_i2cdelayEi'
/Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.setup+0x30): undefined reference to _ZN6CCS8115beginEv' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.setup+0x34): undefined reference to _ZN6CCS81116hardware_versionEv'
/Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.setup+0x38): undefined reference to _ZN6CCS81118bootloader_versionEv' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.setup+0x3c): undefined reference to _ZN6CCS81119application_versionEv'
/Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.setup+0x40): undefined reference to _ZN6CCS8115startEi' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o: in function setup':
main.cpp:(.text.setup+0x97): undefined reference to _ZN6CCS81112set_i2cdelayEi' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: main.cpp:(.text.setup+0x9f): undefined reference to _ZN6CCS8115beginEv'
/Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: main.cpp:(.text.setup+0xbb): undefined reference to _ZN6CCS81116hardware_versionEv' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: main.cpp:(.text.setup+0xd4): undefined reference to _ZN6CCS81118bootloader_versionEv'
/Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: main.cpp:(.text.setup+0xed): undefined reference to _ZN6CCS81119application_versionEv' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: main.cpp:(.text.setup+0x100): undefined reference to _ZN6CCS8115startEi'
/Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.loop+0x24): undefined reference to _ZN6CCS8114readEPtS0_S0_S0_' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.loop+0x28): undefined reference to _ZN6CCS81111errstat_strEt'
/Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.loop+0x43): undefined reference to _ZN6CCS8114readEPtS0_S0_S0_' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o: in function loop':
main.cpp:(.text.loop+0xce): undefined reference to _ZN6CCS81111errstat_strEt' /Users/wookash/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: .pio/build/d1_mini_lite/src/main.cpp.o:(.text.startup._GLOBAL__sub_I_ccs811+0x0): undefined reference to _ZN6CCS811C1Eii'
collect2: error: ld returned 1 exit status
*** [.pio/build/d1_mini_lite/firmware.elf] Error 1`

My first feeling is that the ccs811 library is not part of your
project/build. I see "undefined reference to" many library functions:
set_i2cdelay
begin
hardware_version
bootloader_version
Are you sure the ccs811 library is downloaded and found by your compiler?

My first feeling is that the ccs811 library is not part of your project/build. I see "undefined reference to" many library functions: set_i2cdelay begin hardware_version bootloader_version Are you sure the ccs811 library is downloaded and found by your compiler?

Thanks. I since deleted my comment as I managed to update the firmware from 2.0.0 to 2.0.1, included your library in platformio and worked well. On another note - as this sensor is mostly an indicator of co2 (I have it working together with Vindrikting from IKEA) and calibrated with external sonoff temp sensor - is there any “true” co2 sensor you would recommend?

I benchmarked various NDIR-CO2 sensors and all of them performed ok. The more expensive ones just had a smaller device to device variation and less noise which might not be of importance for the hobbyist. I ran the low-cost Winsen MH-Z19 at home and results were just fine. I also like the small size. The smallest CO2 sensor available on breakout is SCD40 (photo acoustic), it comes with an integrated rh/T sensor. I found this sensor to be influenced by air flow and cannot recommend it for installation into an air duct. This might be true for other "classical" CO2 sensors, too. NDIR-CO2 sensors should be protected from air stream by a housing or filter.
Please note that almost all available NDIR-CO2 sensors run an automatic baseline correction, setting the lowest signal value to the lowest value detected over a period of several days. This means they will adjust to a high CO2 level and get off calibration. This might be dangerous for certain applications e.g. fermentation cellars. Such applications require an absolute (calibrated) CO2 sensor (dual beam), which is more expensive but not needed for a standard indoor air quality application.

Thanks @nordicp. I can see various models of MH-Z19  - MH-Z19b, MH-Z19c and MH-Z19e. Is the cheapest version (e) ok?

The variants differ in warm-up time and max. ppm. Have a look to the datasheets and pick the sensors most suited for your application. Version "e" measures up to 10,000ppm, a pretty high value not expected in normal indoor air applications. If it is a real bargain then go for it. I bought the MH-Z19b some years ago before other models were launched (at least I do not remember that I came across other variants).

The variants differ in warm-up time and max. ppm. Have a look to the datasheets and pick the sensors most suited for your application. Version "e" measures up to 10,000ppm, a pretty high value not expected in normal indoor air applications. If it is a real bargain then go for it. I bought the MH-Z19b some years ago before other models were launched (at least I do not remember that I came across other variants).

Thanks, it seems MH-Z19e is the cheapest of them all..
https://www.aliexpress.com/item/4001296615950.html?spm=a2g0o.productlist.main.1.3cc04Jbn4Jbnfa&algo_pvid=2d2a76b0-0811-4b63-b023-8679edd55fe9&algo_exp_id=2d2a76b0-0811-4b63-b023-8679edd55fe9-0&pdp_npi=3%40dis!GBP!22.15!13.28!!!!!%40214527fd16817537803758772d06e0!12000024558309219!sea!UK!0&curPageLogUid=aii07uyY0eNG

No, sorry. I know some exist, I believe they are quite expensive, but I
never tried any of them, so I wouldn't be able to recommend one.
On Sun, Apr 16, 2023, 10:52 wookash @.***> wrote:

My first feeling is that the ccs811 library is not part of your
project/build. I see "undefined reference to" many library functions:
set_i2cdelay begin hardware_version bootloader_version Are you sure the
ccs811 library is downloaded and found by your compiler?
Thanks. I since deleted my comment as I managed to update the firmware
from 2.0.0 to 2.0.1, included your library in platformio and worked well.
On another note - as this sensor is mostly an indicator of co2 (I have it
working together with Vindrikting from IKEA) and calibrated with external
sonoff temp sensor - is there any “true” co2 sensor you would recommend?
—
Reply to this email directly, view it on GitHub
https://github.com/maarten-pennings/CCS811/issues/8#issuecomment-1510248872,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AHH3FQ63KXA6VTCYL6BHGXDXBO6PLANCNFSM4GKU4KKQ
.
You are receiving this because you modified the open/close state.Message
ID: @.***>

no worries, thanks @nordicp and @maarten-pennings !

I am supporting various CO2 devices in my GeigerLog software.
As I prefer I2C based hardware, I support e.g. the SCD30 and SCD41 devices, but also a MiniMon device. A review is found on my Articles site https://sourceforge.net/projects/geigerlog/files/Articles/
Main Interest perhaps on Measuring CO2 with SCD30, SCD41, and MiniMon
but for using them on a regular computer the use of dongles for I2C may also be of interest: Review of USB-To-I2C Dongles as used by GeigerLog
The USB-ISS dongle is by far the preferred one!
I tried the SCD30, and SCD41 also on Raspberry with NO success! Unfortunately, Sensirion support was more than lacking :-(

@wookash real good NDIR sensor in Senseair S8 (58 revision, do not use others).
I've made custom device based on it for my home. Simple connection, adequate autocalibration and precise measurements.
It is quite expensive (20$ on ali), but It's definitely worth every penny.
I not recommend MH-Z19*, this sensors is one big mess (especially insane auto calibration).
For me personally CCS811 turned out to be just a toy that shows the weather on Mars rather than the concentration of CO2 in your room ;) especially amazed by it's MOX sensor that burns out over time.

Perfect, thanks @shooorf, I will try it out. I think with D1 mini board I will need to power this sensor directly? Regular 5v 1a will suffice?


simple.p4

Not sure what's going wrong with examples/checker_tests/good/simple.p4 but it should typecheck and currently doesn't.

It doesn't typecheck under p4c either. It's passing a NoOp() control with the wrong number of parameters.


Added entry for the Trafford Data Lab leaflet.reachability plugin.

Added at the beginning of the Routing section.

Hi James!
Great job on that plugin!
Can you please move it to the bottom of the section? We're trying to update plugins list on first-come basis.

Hi Andrew,
Many thanks for the feedback and for merging so quickly, much appreciated!


Edit tool improvements

Multiple fixes around screen refresh
Drag and Drop fixed for groups
Some new drawing modes for circles, ellipses, rectangles

Display current filename in titlebar
Fixed snap-functionality
Enhanced Node-Editor, able to deal with polylines/polygons and paths
Editing a polyline/polygon

Editing a path

Dragging the (cubic bezier) curve directly

Smoothen a curve

BTW Codacy will always deem this PR as not up to standard due to the numerous changes done hence increasing the complexity beyond a threshold value. The code itself took (nearly) all recommendations on board.

Merge to avoid further drifting apart. Necessary to go on with some fixes


docker-machine rm -f doesn't work with new Azure driver

cc @ahmetalpbalkan @ppadala @dgageot
I had a couple of Azure VMs created using the driver which I removed by hand.
I ran into https://github.com/docker/machine/issues/3201 attempting to docker-machine rm -f these hosts.  docker-machine rm -f should always be possible to remove the local references if desired.
So, we most likely need to add some max retries logic (so that if authentication is not successful after, say, 5 attempts, Driver.Remove will actually return).  If we don't get an error back https://github.com/docker/machine/blob/master/commands/rm.go#L31 will block forever.

What's the error you ran into while rm? Or did it simply block at the authentication prompt?

@ahmetalpbalkan It blocks at the authentication prompt.

@nathanleclaire then "doesn't work" probably isn't entirely accurate. The same situation could occur if any other driver blocks on some HTTP request as well? What do you suggest in this case?

then "doesn't work" probably isn't entirely accurate.

If the authentication prompt won't let me through, or error out at some reasonable point, it doesn't work :stuck_out_tongue: Today it's easy to get stuck in an impossible situation where you can't authenticate with the driver, and you can't remove the machines through docker-machine rm. Ideally, users should be able to recover from every kind of failure using only the porcelain.

The same situation could occur if any other driver blocks on some HTTP request as well? What do you suggest in this case?

They just shouldn't (usually if there's an authentication failure in a driver it directly returns an error instead of blocking).  If another driver does that, it's a bug.
Drivers should have their own timeout / failure handling logic to ensure that situations like that can be recovered from.  In this case it looks like the actual blocking bit might be inside of the vendored Azure code itself -- is that correct?

We can probably refactor driver to not to prompt for auth (and just fail) for cmds such as ls, rm, ip, env that either timeout or people use with eval directly or indirectly.

Yep, that would be my preference.  Maybe we could make only one of them the "authentication" method, although I dislike the magic nature of that.
In the meanwhile, this (cred management) is one of many issues that I'd like to solve with libmachine v2.  So maybe I should start jotting some notes down about what I'd like v2 to look like to get the conversation moving.

@nathanleclaire that'd be great, I'd love to explain what's the deal with this Azure ActiveDirectory's unusual auth flow some time and why we require users to prompt every 2 weeks etc. and brainstorm about what we can do. I was hoping to propose a cmd like docker-machine auth.

@nathanleclaire the new azure auth mode in #3665 solves this problem, feel free to close.


Documentation problem for gradient clipping parameter

This issue reflect documentation of this: https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Optimizer
As stated in the above documentation (relevant section c and pasted below), there is no difference between clipnorm and global_clipnorm.  However, looking at the code shows these parameters do 2 different things.  Recommend to update documentation.
clipnorm | float or None. If set, clips gradients to a maximum norm.
clipvalue | float or None. If set, clips gradients to a maximum value.
global_clipnorm | float or None. If set, clips gradients to a maximum norm.
iterations

You didn't answer my question in my last response so I cannot move forward.

@isaacgerg
Can you please create the PR for the statement mentioned above.Thanks

@tilakrayal Unfortunately, my employer does not allow me to submit PRs.

@isaacgerg,
clipnorm | float or None. If set, the gradient of each weight is individually clipped so that its norm is no higher than this value.
clipvalue | float or None. If set, the gradient of each weight is clipped to be no higher than this value.
global_clipnorm | float or None. If set, the gradient of all weights is clipped so that their global norm is no higher than this value
Do you think modifying the description as mentioned above would make it clear? I can submit a PR if that looks good. Thanks!

@sanatmpa1 It is unclear to me if the description you list is correct.
A gradient is really a group of scalar values.  Its unclear how clipnorm differs from global_clipnorm.  My guess is that clipnorm groups weights by their function but its not clear to me.  Having an example to make this completely clear would be very helpful.
Also, does clipvalue clip truly  by value only or by magnitude and preserve the sign?

Let's consider the below optimizer,
optimizer = keras.optimizers.SGD(clipvalue=1.0)
model.compile(loss="mse", optimizer=optimizer)

This optimizer will clip every component of the gradient vector to a value between –1.0 and 1.0. This means that all the partial derivatives of the loss (with regard to each and every trainable parameter) will be clipped between –1.0 and 1.0. Note that it may change the orientation of the gradient vector. For instance, if the original gradient vector is [0.9, 100.0], it points mostly in the direction of the second axis; but once you clip it by value, you get [0.9,1.0], which points roughly in the diagonal between the two axes.
In practice, this approach works well. If you want to ensure that Gradient Clipping does not change the direction of the gradient vector, you should clip by norm by setting clipnorm instead of clipvalue. This will clip the whole gradient if its ℓ norm is greater than the threshold you picked. For example, if you set clipnorm=1.0, then the vector [0.9, 100.0] will be clipped to [0.00899964, 0.9999595], preserving its orientation but almost
eliminating the first component.

@sachinprasadhs You should add your commentary to the documentation.
What does global_clipnorm do?

As mentioned below also as per the document about global_clipnorm.

@isaacgerg if clipnorm(float) is set, the gradient of each weight is individually clipped so that its norm is no higher than this value. If global_clipnorm (float) is set the gradient of all weights is clipped so that their global norm is no higher than this set value. This has been clearly mentioned in **kwargs.

I don't see how clip_norm is different than global_clipnorm.
Clip_ value examines each scalar and hard clips based on threshold but preserves sign.  Clip_norm rescales all the scalars so that the norm is the specified value.  But, what does global_clipnorm do?

Does the description provided here helps you.

I'm still here but its approaching the holidays in the USA.

@sachinprasadhs Sort of.  Is the gradient of a model broken up into sections?  I presume it must otherwise, clip_by_global_norm will never take in a list.

Note that gradient of NN is a list of Tensors, not scalars and in the code it internally calls tf.clip_by_global_norm check here and here.
In future with the new release of keras optimizer it will be more easier for users and will be clear.
I will create a PR for the changes pointing to the tf.clip_by_global_norm for the users to understand better. Thanks!

@ sachinprasadhs  This sounds great!  Thank you for your efforts!


DOC-9810 Update Observability docs for Cloud 2.0 - Metrics and Custom Metrics

Fixes DOC-9810
(1) ported Serverless Canned Metrics PR 18283.
(2) In metrics.yml, replaced deploy_serverless with deploy_standard.
(3) In sidebar-data/cloud-deployments.json, (a) removed serverless in metrics-essential link, (b) added Metrics in Standard/Basic and Metrics in Advanced sections.
(4) In metrics-*.md files, (a) replaced serverless with standard and basic, (b) replaced dedicated with advanced, (c) removed serverless in metrics-essential and resource-usage links.
(5) In metrics-page.md file, (a) replaced metrics-overview link with metrics-essential link.
(6) In resource-usage.md, (a) added links to new canned metrics pages, particularly Request Units metrics, (b) promoted General Tips section to header 2 and created sections for types of resource usage, (c) incorporated recommendations from internal wiki page.
(7) In custom-metrics-chart-page.md, updated See also.

Hi @Amruta-Ranade, please review resource-usage.md.
Hi @rmloveland, please review all files.
thanks in advance!

Hi @Amruta-Ranade and @rmloveland, I removed you as reviewers from this PR because I need to make further changes upon new information. I will re-assign you when I have completed the additional changes.
As discussed with @kevin-v-ngo:

Basic will not have canned metrics. Basic will use existing Metrics page, similar to Advance, since there will be no new features for Basic.

For Cloud 2.0 I reorganized the metrics pages under the guidance of @mikeCRL.
@kevin-v-ngo: Please review the new Metrics Overview.
@Amruta-Ranade: Please review Resource Usage . You previously reviewed these changes in [PR 18283] (https://github.com/cockroachdb/docs/pull/18283)  The metrics info on this page only applies to Standard, not Basic. I am not sure how I should indicate that on the page. I will send you an invite to discuss.
@rmloveland: Please review all changes. You previously reviewed these changes in [PR 18283] (https://github.com/cockroachdb/docs/pull/18283). I then adapted the changes for Cloud 2.0, see the description for more details.
Thanks you all!

@Amruta-Ranade: Please review Resource Usage with the changes we discussed. I created filter sections for Standard (from PR 18283) and Basic (from PR 18344).

DOC-3381 Cannot navigate directly to anchors within non-default page filters: is a known issue with Resource Usage page.

@Amruta-Ranade said that her approval is not blocking since resource-usage.md will be further updated. So I am going to merge this PR into cloud-2.0


Data list for quantitative comparison

Hello, thank you for sharing your great research and releasing the code.
I would like to ask whether the test set used for the quantitative comparison on the UnrealStereo4K dataset is 'splits/test_in.txt' or 'splits/test_out.txt'.
Additionally, could you please clarify which test set was used for the quantitative comparison on the CityScapes dataset?
Thank you!

Hi, thanks for your interest in our work.
We use val.txt to evaluate the model as default following previous metric depth papers. The numbers and in-domain results in our paper are all from this set. The text_in.txt indicates in-domain test set, which could also be good for evaluation but we just left it here. The text_out.txt is a set for out-of-domain evaluation. The model performs much worse here, indicating some generalization issues waiting for solutions.
For the CityScapes, the training set includes the standard training data plus the extra set. The validation set is also just the official one. I put the corresponding files right below. Note that the evaluation protocal presented in this paper is slightly different from the original paper. As we claimed, the current one makes much more sense and we encourage researchers to report the updated results in their paper for fair comparisons. Please check here for more information.
train.txt
val.txt
Btw, I just noticed some small issues in the cityscapes training docs. I'm working on it and I will also add the specific number for comparison for the cistyscapes. I will leave a message here after finishing it.

Thanks for fast reply.
It is really helpful.


Release link broken?

I think the release link for Darwin/mac is broken.  I downloaded it from https://force-cli.heroku.com/ and from the releases page on github but they both are unmarked files containing bits or something.
The https://force-cli.heroku.com/ makes it seem like you are getting a .dmg and I am not sure if that is what I should expect or if this bits file is actually the correct file.
Please let me know if the release link is working
Thanks,
Jack
EDIT:
Dave I just read your previous post on the future of this project.  All sounds good and very cool actually.   I am still curious if an older release will be available?

It is a file with no extension, you may need to run chmod +x force.
Sent from my iPhone
On Aug 28, 2016, at 11:44 AM, Jack Ziesing<EMAIL_ADDRESS>wrote:
I think the release link for Darwin/mac is broken. I downloaded it from
https://force-cli.heroku.com/ and from the releases page on github but they
both are unmarked files containing bits or something.
The https://force-cli.heroku.com/ makes it seem like you are getting a .dmg
and I am not sure if that is what I should expect or if this bits file is
actually the correct file.
Please let me know if the release link is working
Thanks,
Jack
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/heroku/force/issues/310, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAHGHr-l-Aipa3VVhDwhEdHwTkeM-_E2ks5qkdcIgaJpZM4Ju_M5
.

oh I got it, thank you!


.exp file format does not handle jump stitches.

Because Embroidermodder wrongly doesn't accept 80 04 as a jump but calls it a trim, sequences which are jumps are not processed as jumps. Jump sequence seems to properly be 80 04 xx yy means this is a jump stitch. When it loads the format it calls those 80 04 trims and manages to draw the first jump stitch in the format.
See this error properly demonstrated here:
http://www.wasbo.net/svg2embr/phpBB3/viewtopic.php?f=8&t=11

The proper encoding:

80 80 07 00

is Trim.

80 01 00 00

is Color change.

80 04 xx yy

is Jump.

80 01 00 00

is Stop (same as color change)

xx yy

is Stitch

There is no end stitch value, it just ends.


Add ruby 2.4.0 to .travis.yml

Description
Adds Ruby 2.4.0 to the list of tested rubies on Travis CI.
Related Issue
Motivation and Context
We'd like to ensure that Sensu can run on the latest ruby version.
How Has This Been Tested?
N/A
Screenshots (if appropriate):
Types of changes

[ ] Bug fix (non-breaking change which fixes an issue)
[x] New feature (non-breaking change which adds functionality)
[ ] Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

[ ] My change requires a change to the documentation.
[ ] I have updated the documentation accordingly.
[x] I have read the CONTRIBUTING document.
[ ] I have added tests to cover my changes.
[ ] All new and existing tests passed.

Provided 2.4.0 passes, we should also add it to the travis.yml files in our internal dependencies (redis, transport, settings, etc).


What about uploadin the IHaskell.Display packages on hackage?

Hi, could you upload the display packages on hackage? This could enable, eg, the nixos users to have them in their package manager, and benefit from build cache and not be forced to configure the package from source.

Want to back this issue? Place a bounty on it! We accept bounties via Bountysource.

ihaskell, ghc-parser, ipython-kernel, as well as all of the display packages except ihaskell-widgets (which is not a real package, just experimental) have been updated on Hackage.
Sadly I have not been keeping track of changes (or API changes) and so bumped the versions of all the packages. Also not respecting PVP. Sorry about that, will try to be better about that in the future.

If there are any issues, open a new issue and let me know. I have gotten all of them to build on my box with the current version constraints.

Thanks, great work. They builded fine from this github repo last time I build it. I'll keep you posted, thanks again :)


Re-working registers and peripherals

As discussed in https://github.com/rust-embedded/book/issues/46

I think I'm done? Feedback welcome!

@japaric What I wanted to cover in rusty.md is more or less covered in src/start/registers.md now - I intended to show how to interact with -pac and microarchitecture crates, which I think @thejpster has already done and excellent job with.
I'm OK to merge this as-is now.


[PWX-36732] vendor update kvdb

What this PR does / why we need it:
udpate go packages majorly etcd and its dependencies
Which issue(s) this PR fixes (optional)
Closes #PWX-36732

Can you put more description on why we are doing this change? For example, in porx, there's conflict in x,y,z.. Changes in the PR would resolve x,y,z because ....
Many thanks


Question on the CSV file with bounding boxes info

Hello Thanks for releasing this treasure trove of images.
I am wondering what does the Xmin, Ymin, XMax, YMax represents?
When I use LabelImg to make bounding boxes the Xmin, Ymin, XMax, YMax  are all pixels.
Looking at the CSV provided for openimages Xmin, Ymin, XMax, YMax are all decimal number smaller than 1. What do they represents? Thanks

@patrickmlaw try this:
https://github.com/cesarandreslopez/open-images-dataset-pascal-voc


GraphQL

Section of the docs about the graphQL API. Explains about the general concept of GraphQL and goes into the detail of mutations and queries and their format. Also has some code samples of how to use the graphQL API with amplify.

[ ] Concept

[ ] Queries

[ ] Mutations