eCO2-Traffic-Light
A cheap Traffic-Light-style gas detector (ESP32 + CCS811 + 3 LEDs) that gives an indication of when to open the windows in a room with a lot of exhaled and thus possibly polluted air.
The intention of this project was to have a simple device that gives an indication of when to open the windows in a (class)room because of exhaled and possibly polluted air.
I have used a cheap CCS811 sensor in combination with an ESP32 . This sensor does not directly measure the CO2 content, but in particular the amount of particles exhaled by the human lungs. From this, the sensor indirectly calculates an eTVOC and eCO2 value.
The CCS811 requires a one-time BURN-IN time of at least 48 hours and a regular RUN-IN time of 20 minutes after start before reliable values can be output.
The sensor assumes that the lowest eCO2 value measured in a longer interval (at the same temperature and humidity) corresponds to "clean" air in an in-between well-ventilated room with about 400ppm CO2.
This highly scattering baseline value is also dependent on various environmental factors. It is determined dynamically in each case, as the sensor exhibits principle-related short-term and long-term changes in sensitivity. However, the simple and cheap sensor itself does not store this baseline value.
I have tried to take this behaviour into account in my code.
The code is written in ANNEX32, a BASIC-programming language for ESP32
Find its documentation HERE.
Some more details about this project are HERE
Helpful links for me:
https://www.sciosense.com/products/environmental-sensors/ccs811-gas-sensor-solution/
https://cicciocb.com/annex32help/V1.508/
https://flasher.cicciocb.com/dist/index.html
The ANNEX32-Code:
I have used a cheap CCS811 sensor in combination with an ESP32 . This sensor does not directly measure the CO2 content, but in particular the amount of particles exhaled by the human lungs. From this, the sensor indirectly calculates an eTVOC and eCO2 value.
The CCS811 requires a one-time BURN-IN time of at least 48 hours and a regular RUN-IN time of 20 minutes after start before reliable values can be output.
The sensor assumes that the lowest eCO2 value measured in a longer interval (at the same temperature and humidity) corresponds to "clean" air in an in-between well-ventilated room with about 400ppm CO2.
This highly scattering baseline value is also dependent on various environmental factors. It is determined dynamically in each case, as the sensor exhibits principle-related short-term and long-term changes in sensitivity. However, the simple and cheap sensor itself does not store this baseline value.
I have tried to take this behaviour into account in my code.
The code is written in ANNEX32, a BASIC-programming language for ESP32
Find its documentation HERE.
Some more details about this project are HERE
Helpful links for me:
https://www.sciosense.com/products/environmental-sensors/ccs811-gas-sensor-solution/
https://cicciocb.com/annex32help/V1.508/
https://flasher.cicciocb.com/dist/index.html
The ANNEX32-Code:
'####################################################################### ' CO2-TRAFFIC-LIGHT ' ################## 'Shows AIR QUALITY with ' - CCS811 eCO2-TVOC-Sensor at I2C-pins of an ESP32 ' - 3 LEDs (green, yellow, red to show the range of CO2 ppm ' - saves the 2 BASELINE-bytes in /BASELINE1.txt and /BASELINE2.txt ' - SAVE OF BASELINE after 20 Minutes in an unpolluted environment ' as a kind of calibration if variable BASELINE_SAVE = 1 ' - AUTOMATED RESTORE OF SAVED BASELINE to avoid taking a baseline ' in polluted evir if variable RESTORE_BASELINE = 1 'to do: ' - Webinterface with CO2 ppm and TVOV ppb and settings ' - BEEP if poor quality for more than X seconds ' - eMail if poor quality for more than X seconds '####################################################################### ' DB9JG@me.com Version$ = "V1.4" '------SETTINGS------------- SAVE_BASELINE = 0 '1 => saves the two baseline-bytes to data file after 20 Minutes of run-in regularly RESTORE_BASELINE = 1 '1 => restores the two baseline-bytes from data file after 20 Minutes of run-in regularly SILENT = 1 '1 => suppress all messages 0 => show some status-messages via wlog LL_GREEN = 400 'lower limit of the range for the green LED LL_Yellow = 500 'lower limit of the range for the yellow LED LL_RED = 600 'lower limit of the range for the red LED '--------------------------- SDA_PIN = 21 'SDA=21 at ESP32 SCL_PIN = 22 'SCL=22 at ESP32 CCS811temp = 0 STATUS_REG = &h00 MEAS_MODE_REG = &h01 ALG_RESULT_DATA = &h02 ENV_DATA = &h05 NTC_REG = &h06 THRESHOLDS = &h10 BASELINE = &h11 HW_ID_REG = &h20 ERROR_ID_REG = &hE0 APP_START_REG = &hF4 SW_RESET = &hFF CCS811_I2C_ADR = &h5A GPIO_WAKE = &h5 DRIVE_MODE_IDLE = &h0 DRIVE_MODE_1SEC = &h10 DRIVE_MODE_10SEC = &h20 DRIVE_MODE_60SEC = &h30 INTERRUPT_DRIVEN = &h8 THRESHOLDS_ENABLED = &h4 BASELINE1$ = "??" BASELINE2$ = "??" count = 0 Dim BUFFER(10) 'I2C-Sende- / Empfangspuffer. LED_RED = 14 'GPIOs for LEDs LED_YELLOW = 13 LED_GREEN = 12 pin.mode LED_RED, OUTPUT pin.mode LED_YELLOW, OUTPUT pin.mode LED_GREEN, OUTPUT PIN(LED_RED) = 1 'show that the device is working PIN(LED_YELLOW) = 1 'show that the device is working PIN(LED_GREEN) = 1 'show that the device is working I2C.SETUP SDA_PIN, SCL_PIN ' set I2C ports if not silent GOSUB I2C_SCANNER ' show all I2C-bus-devices GOSUB SHOW_CCS811_STATUS ' show Status-byte GOSUB CCS811_GO_APP_MODE 'leave BOOT-Mode, start APP-Mode GOSUB SHOW_CCS811_STATUS GOSUB CCS811_SET_DRIVE_MODE 'CCS811 generates eCO2 and TVOC once per second GOSUB SHOW_CCS811_STATUS 'save baseline to files regularly after min 20Min of run-in !! IN CLEAN AIR CONDITION !! if SAVE_BASELINE then TIMER0 (20*60000), CCS811_SAVE_BASELINE_TO_FILE 'restore baseline from files regularly after min 20Min of run-in if RESTORE_BASELINE then TIMER0 (5*60000), CCS811_RESTORE_BL_FROM_FILE TIMER1 1050, CCS811_SHOW 'read and show eCO2 and TVOC from CCS811 sensor WAIT end '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! '###################################################################### CCS811_SHOW: GOSUB CCS811_READ_eCO2 gosub SET_CO2_TRAFFIC_LIGHT 'wlog "eCO2 = ";eCO2,"TVOC = ";TVOC if count = 0 then ' gosub CCS811_SAVE_BASELINE_TO_FILE 'TEST ONLY--------------------- GOSUB CSS811_READ_BASELINE wlog time$, "eCO2 = ";eCO2,"TVOC = ";TVOC ,"BASELINE: ";BASELINE1$, BASELINE2$ end if count = (count +1) mod 30 'count = (count +1) mod 60 ' 60 * 1 Sekunden 'count = (count +1) mod 300 ' x * 1 Sekunden !!!!!TEST!!!!!!!!!!!!!!!!! return '###################################################################### CCS811_READ_eCO2: ' Bit 3 = Data Ready 0:no new Data 1:new Data ' Bit 0 Error dedection 0:no Error 1: Error on i2c or Sensor '''''''''''''''''''''''''''''i2c.writeRegByte CCS811_I2C_ADR, STATUS_REG , 0 i2c.writeRegByte CCS811_I2C_ADR, STATUS_REG , 0 i2c.ReqFrom CCS811_I2C_ADR,1 CCS811temp = i2c.read If CCS811temp and 8 then 'DATA_available i2c.writeRegByte CCS811_I2C_ADR, ALG_RESULT_DATA ,0 'Select the Result mailbox i2c.ReqFrom CCS811_I2C_ADR, 8 for i = 1 to 8 'The Result mailbox contains 8 Bytes BUFFER(i) = i2c.read next eCO2 = 256*BUFFER(1) + BUFFER(2) 'eCO2 ppm TVOC = 256*BUFFER(3) + BUFFER(4) 'eTVOC else eCO2 = 0 TVOC = 0 end if return '###################################################################### CSS811_READ_BASELINE: 'read 2 bytes from baseline-mailbox if not silent wlog "Read the two baseline bytes:" i2c.begin CCS811_I2C_ADR I2c.write BASELINE i2c.end i2c.ReqFrom CCS811_I2C_ADR, 2 'Two bytes from baseline mailbox BASELINE1$ = hex$(i2c.read) BASELINE2$ = hex$(i2c.read) if not silent wlog " ",BASELINE1$,BASELINE2$ return '###################################################################### CCS811_SAVE_BASELINE_TO_FILE: GOSUB CSS811_READ_BASELINE File.save "/BASELINE1.txt", BASELINE1$ File.save "/BASELINE2.txt", BASELINE2$ if not silent wlog "Saved baseline-byte1 ";BASELINE1$ ; " to file /BASELINE1.txt" if not silent wlog "Saved baseline-byte2 ";BASELINE2$ ; " to file /BASELINE2.txt" return '###################################################################### CCS811_RESTORE_BL_FROM_FILE: 'restore 2 bytes from Datafile to baseline-mailbox if not silent wlog "Restore the two baseline bytes:" if file.exists("/BASELINE1.txt") then BASELINE1$ = File.read$("/BASELINE1.txt") if file.exists("/BASELINE2.txt") then BASELINE2$ = File.read$("/BASELINE2.txt") 'BASELINE1$ = "09" 'TEST 'BASELINE2$ = "81" 'TEST i2c.begin CCS811_I2C_ADR I2c.write BASELINE ' select baseline-mailbox I2c.write val("&h" + BASELINE1$) ' write byte #1 to baseline-mailbox I2c.write val("&h" + BASELINE2$) ' write byte #2 to baseline-mailbox i2c.end if not silent wlog "Restored baseline-byte1 ";BASELINE1$ ; " from file /BASELINE1.txt" if not silent wlog "Restored baseline-byte2 ";BASELINE2$ ; " from file /BASELINE2.txt" GOSUB CSS811_READ_BASELINE return '###################################################################### SET_CO2_TRAFFIC_LIGHT: SELECT CASE eCO2 CASE LL_GREEN to (LL_YELLOW -1) : 'GREEN range PIN(LED_GREEN) = 1 - PIN(LED_GREEN) PIN(LED_RED) = 0 PIN(LED_YELLOW) = 0 CASE LL_YELLOW to (LL_RED -1) : 'YELLOW range PIN(LED_YELLOW) = 1 - PIN(LED_YELLOW) PIN(LED_RED) = 0 PIN(LED_GREEN) = 0 CASE LL_RED to 9999 : 'RED range PIN(LED_RED) = 0 pause 200 PIN(LED_RED) = 1 - PIN(LED_RED) PIN(LED_GREEN) = 0 PIN(LED_YELLOW) = 0 CASE ELSE: PIN(LED_RED) = 1 PIN(LED_YELLOW) = 1 PIN(LED_GREEN) = 1 END SELECT return '###################################################################### SHOW_CCS811_STATUS: if silent return wlog "" wlog "-------------CCS811-Sensor_Status:-------------" 'Read the status-byte i2c.writeRegByte CCS811_I2C_ADR, STATUS_REG , 0 i2c.ReqFrom CCS811_I2C_ADR,1 CCS811temp = i2c.read wlog "STATUSBYTE :",CCS811temp,"= &b"; bin$(CCS811temp) If CCS811temp and 128 then wlog " CCS811 is in Application Mode" else wlog " CCS811 is in Boot Mode" end if If CCS811temp and 16 then wlog " A valid firmware is loaded" else wlog " NO valid firmware loaded!" end if If CCS811temp and 8 then wlog " DATA is available to read" else wlog " NO DATA available to read" end if If CCS811temp and 1 then wlog " Error on i2c or sensor!!" 'Read the ERROR-ID only if error bit is set i2c.writeRegByte CCS811_I2C_ADR, ERROR_ID_REG , 0 i2c.ReqFrom CCS811_I2C_ADR,1 CCS811temp = i2c.read wlog " ERROR_ID_REG : dec",CCS811temp,"= &b";bin$(CCS811temp) else wlog " NO Error detected" end if wlog "----------------------------------------" wlog " " RETURN '###################################################################### CCS811_GO_APP_MODE: if not silent wlog "Change from BOOT-Mode to App Mode" i2c.begin CCS811_I2C_ADR i2c.write APP_START_REG i2c.end return '###################################################################### CCS811_SET_DRIVE_MODE: 'Bit7=0reserved, 'Bit 6-4=001 read data 1 per sec '3=0 Interrupt off, 2=0 Interrupt Mode Normal, 1-0 =00 Reserved if not silent then wlog "Set MEAS_MODE_REG to &b:"; bin$(DRIVE_MODE_1SEC); " = read new data once per 1 second" end if i2c.writeRegByte CCS811_I2C_ADR, MEAS_MODE_REG, DRIVE_MODE_1SEC return '###################################################################### I2C_SCANNER: 'I2C Address Scanner 'wlog all addresses of the I2C-devices found 'I2C.SETUP SDA_PIN, SCL_PIN ' set I2C ports wlog "---start-I2C-scan---" for i = 0 to 120 i2c.begin i if i2c.end = 0 then wlog "Found a device at I2C-Adr dec "; i ,", hex "; hex$(i) pause 10 end if next i wlog "---end-I2C-scan---" return
Discussion (2 comments)