diff --git a/ESP8266_Transmitter/Arduino/libraries/CCS811/CCS811.cpp b/ESP8266_Transmitter/Arduino/libraries/CCS811/CCS811.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..912ce761986d70be0fe8fc8aa0b9897911a24a52
--- /dev/null
+++ b/ESP8266_Transmitter/Arduino/libraries/CCS811/CCS811.cpp
@@ -0,0 +1,154 @@
+#include "CCS811.h"
+
+int CCS811::begin(void)
+{
+    uint8_t id=0;
+    Wire.begin();
+    softReset();
+    delay(100);
+    if(readReg(CCS811_REG_HW_ID,&id,1) != 1){DBG("");
+        DBG("bus data access error");DBG("");
+        return ERR_DATA_BUS;DBG("");
+    }
+
+    DBG("real sensor id=");DBG(id);
+    if(id != CCS811_HW_ID){DBG("");
+        delay(1);
+        return ERR_IC_VERSION;
+    }
+    writeReg(CCS811_BOOTLOADER_APP_START, NULL, 0);
+    setMeasurementMode(0,0,eMode4);
+    setInTempHum(25, 50);
+    return ERR_OK;
+}
+
+void CCS811::softReset(){
+    uint8_t value[4] = {0x11, 0xE5, 0x72, 0x8A};
+    writeReg(CCS811_REG_SW_RESET, value, 4);
+}
+
+bool CCS811::checkDataReady()
+{
+    int8_t status[1] = {0};
+    readReg(CCS811_REG_STATUS, status, 1);
+    DBG(status[0],HEX);
+    if(!((status[0] >> 3) & 0x01))
+        return false;
+    else 
+        return true;
+}
+
+uint16_t CCS811::readBaseLine(){
+    uint8_t buffer[2];
+    readReg(CCS811_REG_BASELINE, buffer, 2);
+    return buffer[0]<<8|buffer[1];
+}
+
+void CCS811::writeBaseLine(uint16_t baseLine){
+    uint8_t buffer[2];
+    
+    buffer[0] = baseLine>>8;
+    buffer[1] = baseLine;
+    writeReg(CCS811_REG_BASELINE, buffer, 2);
+}
+
+void CCS811::setMeasurementMode(uint8_t thresh, uint8_t interrupt, eDRIVE_MODE_t mode){
+    uint8_t measurement[1] = {0};
+    measurement[0] = (thresh << 2) | (interrupt << 3) | (mode << 4);
+    writeReg(CCS811_REG_MEAS_MODE, measurement, 1);
+}
+
+void CCS811::setMeasCycle(eCycle_t cycle){
+    uint8_t measurement[1] = {0};   
+    measurement[0] = cycle << 4;
+    writeReg(CCS811_REG_MEAS_MODE, measurement, 1);
+}
+
+uint8_t CCS811::getMeasurementMode(){
+    uint8_t meas[1] = {0};
+    readReg(CCS811_REG_MEAS_MODE, meas, 1);
+    return meas[0];
+}
+
+void CCS811::setThresholds(uint16_t lowToMed, uint16_t medToHigh)
+{
+    uint8_t buffer[] = {(uint8_t)((lowToMed >> 8) & 0xF),
+                        (uint8_t)(lowToMed & 0xF),
+                        (uint8_t)((medToHigh >> 8) & 0xF),
+                        (uint8_t)(medToHigh & 0xF)};
+    
+    writeReg(CCS811_REG_THRESHOLDS, buffer, 5);
+    uint8_t buf[1];
+    readReg(CCS811_REG_THRESHOLDS, buf, 1);
+    Serial.println(buf[0],HEX);
+}
+
+uint16_t CCS811::getCO2PPM(){
+    uint8_t buffer[8];
+    readReg(CCS811_REG_ALG_RESULT_DATA, buffer, 8);
+    eCO2 = (((uint16_t)buffer[0] << 8) | (uint16_t)buffer[1]);
+    return eCO2;
+}
+
+uint16_t CCS811::getTVOCPPB(){
+    uint8_t buffer[8];
+    readReg(CCS811_REG_ALG_RESULT_DATA, buffer, 8);
+    eTVOC = (((uint16_t)buffer[2] << 8) | (uint16_t)buffer[3]);
+    return eTVOC;
+}
+
+void CCS811::setInTempHum(float temperature, float humidity)    // compensate for temperature and relative humidity
+{
+    int _temp, _rh;
+    if(temperature>0)
+        _temp = (int)temperature + 0.5;  // this will round off the floating point to the nearest integer value
+    else if(temperature<0) // account for negative temperatures
+        _temp = (int)temperature - 0.5;
+    _temp = _temp + 25;  // temperature high byte is stored as T+25°C in the sensor's memory so the value of byte is positive
+    _rh = (int)humidity + 0.5;  // this will round off the floating point to the nearest integer value
+    
+    uint8_t envData[4];
+    
+    envData[0] = _rh << 1;  // shift the binary number to left by 1. This is stored as a 7-bit value
+    envData[1] = 0;  // most significant fractional bit. Using 0 here - gives us accuracy of +/-1%. Current firmware (2016) only supports fractional increments of 0.5
+    envData[2] = _temp << 1;
+    envData[3] = 0;
+    
+    writeReg(CCS811_REG_ENV_DATA, &envData, 4);
+}
+
+void CCS811::writeReg(uint8_t reg, const void* pBuf, size_t size)
+{
+    if(pBuf == NULL){
+        DBG("pBuf ERROR!! : null pointer");
+    }
+    uint8_t * _pBuf = (uint8_t *)pBuf;
+    _pWire->beginTransmission(_deviceAddr);
+    _pWire->write(&reg, 1);
+    
+    for(uint16_t i = 0; i < size; i++){
+        _pWire->write(_pBuf[i]);
+    }
+    _pWire->endTransmission();
+}
+
+uint8_t CCS811::readReg(uint8_t reg, const void* pBuf, size_t size)
+{
+    if(pBuf == NULL){
+        DBG("pBuf ERROR!! : null pointer");
+    }
+    uint8_t * _pBuf = (uint8_t *)pBuf;
+    _pWire->beginTransmission(_deviceAddr);
+    _pWire->write(&reg, 1);
+    
+    if( _pWire->endTransmission() != 0){
+        return 0;
+    }
+
+    _pWire->requestFrom(_deviceAddr, (uint8_t) size);
+    for(uint16_t i = 0; i < size; i++){
+        _pBuf[i] = _pWire->read();
+    }
+    _pWire->endTransmission();
+    return size;
+}
diff --git a/ESP8266_Transmitter/Arduino/libraries/CCS811/CCS811.h b/ESP8266_Transmitter/Arduino/libraries/CCS811/CCS811.h
new file mode 100644
index 0000000000000000000000000000000000000000..657a06452a8d729b5196b16d1b98111af48d4198
--- /dev/null
+++ b/ESP8266_Transmitter/Arduino/libraries/CCS811/CCS811.h
@@ -0,0 +1,224 @@
+#ifndef _CCS811_H
+#define _CCS811_H
+
+#if ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+#include <Wire.h>
+
+
+/*I2C ADDRESS*/
+#define CCS811_I2C_ADDRESS1                      0x5A
+#define CCS811_I2C_ADDRESS2                      0x5B
+
+#define CCS811_REG_STATUS                        0x00
+#define CCS811_REG_MEAS_MODE                     0x01
+#define CCS811_REG_ALG_RESULT_DATA               0x02
+#define CCS811_REG_RAW_DATA                      0x03
+#define CCS811_REG_ENV_DATA                      0x05
+#define CCS811_REG_NTC                           0x06
+#define CCS811_REG_THRESHOLDS                    0x10
+#define CCS811_REG_BASELINE                      0x11
+#define CCS811_REG_HW_ID                         0x20
+#define CCS811_REG_HW_VERSION                    0x21
+#define CCS811_REG_FW_BOOT_VERSION               0x23
+#define CCS811_REG_FW_APP_VERSION                0x24
+#define CCS811_REG_INTERNAL_STATE                0xA0
+#define CCS811_REG_ERROR_ID                      0xE0
+#define CCS811_REG_SW_RESET                      0xFF
+
+#define CCS811_BOOTLOADER_APP_ERASE              0xF1
+#define CCS811_BOOTLOADER_APP_DATA               0xF2
+#define CCS811_BOOTLOADER_APP_VERIFY             0xF3
+#define CCS811_BOOTLOADER_APP_START              0xF4
+
+#define CCS811_HW_ID                             0x81
+//Open the macro to see the detailed program execution process.
+//#define ENABLE_DBG
+
+#ifdef ENABLE_DBG
+#define DBG(...) {Serial.print("[");Serial.print(__FUNCTION__); Serial.print("(): "); Serial.print(__LINE__); Serial.print(" ] "); Serial.println(__VA_ARGS__);}
+#else
+#define DBG(...)
+#endif
+
+class CCS811
+{
+public:
+    #define ERR_OK             0      //OK 
+    #define ERR_DATA_BUS      -1      //error in data bus
+    #define ERR_IC_VERSION    -2      //chip version mismatch
+    
+    uint8_t _deviceAddr;
+    typedef enum{
+        eMode0, //Idle (Measurements are disabled in this mode)
+        eMode1, //Constant power mode, IAQ measurement every second
+        eMode2, //Pulse heating mode IAQ measurement every 10 seconds
+        eMode3, //Low power pulse heating mode IAQ measurement every 60 seconds
+        eMode4  //Constant power mode, sensor measurement every 250ms 1xx: Reserved modes (For future use)
+    }eDRIVE_MODE_t;
+    
+    typedef enum{
+        eClosed,      //Idle (Measurements are disabled in this mode)
+        eCycle_1s,    //Constant power mode, IAQ measurement every second
+        eCycle_10s,   //Pulse heating mode IAQ measurement every 10 seconds
+        eCycle_60s,   //Low power pulse heating mode IAQ measurement every 60 seconds
+        eCycle_250ms  //Constant power mode, sensor measurement every 250ms 1xx: Reserved modes (For future use)
+    }eCycle_t;
+    /**
+     * @brief Constructor 
+     * @param Input in Wire address
+     */
+    CCS811(TwoWire *pWire = &Wire, uint8_t deviceAddr = 0x5A){_pWire = pWire; _deviceAddr = deviceAddr;};
+    
+              /**
+               * @brief Constructor
+               * @return Return 0 if initialization succeeds, otherwise return non-zero.
+               */
+    int       begin();
+              /**
+               * @brief Judge if there is data to read 
+               * @return Return 1 if there is, otherwise return 0. 
+               */
+    bool      checkDataReady();
+              /**
+               * @brief Reset sensor, clear all configured data.
+               */
+    void      softReset(),
+              /**
+               * @brief Set environment parameter 
+               * @param temperature Set temperature value, unit: centigrade, range (-40~85℃)
+               * @param humidity    Set humidity value, unit: RH, range (0~100)
+               */
+              setInTempHum(float temperature, float humidity),
+              /**
+               * @brief Measurement parameter configuration 
+               * @param thresh:0 for Interrupt mode operates normally; 1 for interrupt mode only asserts the nINT signal (driven low) if the new
+               * @param interrupt:0 for Interrupt generation is disabled; 1 for the nINT signal is asserted (driven low) when a new sample is ready in
+               * @param mode:in typedef enum eDRIVE_MODE_t
+               */
+              setMeasurementMode(uint8_t thresh, uint8_t interrupt, eDRIVE_MODE_t mode),
+              /**
+               * @brief Measurement parameter configuration 
+               * @param mode:in typedef enum eDRIVE_MODE_t
+               */
+              setMeasCycle(eCycle_t cycle),
+              /**
+               * @brief Set interrupt thresholds 
+               * @param lowToMed: interrupt triggered value in range low to middle 
+               * @param medToHigh: interrupt triggered value in range middle to high 
+               */
+              setThresholds(uint16_t lowToMed, uint16_t medToHigh);
+              /**
+               * @brief Get current configured parameter
+               * @return configuration code, needs to be converted into binary code to analyze
+               *         The 2nd: Interrupt mode (if enabled) operates normally,1: Interrupt mode (if enabled) only asserts the nINT signal (driven low) if the new
+               *         The 3rd: Interrupt generation is disabled,1: The nINT signal is asserted (driven low) when a new sample is ready in
+               *         The 4th: 6th: in typedef enum eDRIVE_MODE_t
+               */
+    uint8_t   getMeasurementMode();
+
+              /**
+               * @brief Get the current carbon dioxide concentration
+               * @return current carbon dioxide concentration, unit:ppm
+               */
+    uint16_t  getCO2PPM(),
+              /**
+               * @brief Get current TVOC concentration
+               * @return Return current TVOC concentration, unit: ppb
+               */
+              getTVOCPPB();
+    uint16_t  readBaseLine();
+    void      writeBaseLine(uint16_t baseLine);
+    
+protected:
+
+    typedef struct{
+        /*
+         * The CCS811 received an I²C write request addressed to this station but with invalid register address ID
+         */
+        uint8_t sWRITE_REG_INVALID: 1;
+        /*
+         * The CCS811 received an I²C read request to a mailbox ID that is invalid
+         */
+        uint8_t sREAD_REG_INVALID: 1;
+        /*
+         * The CCS811 received an I²C request to write an unsupported mode to MEAS_MODE
+         */
+        uint8_t sMEASMODE_INVALID: 1;
+        /*
+         * The sensor resistance measurement has reached or exceeded the maximum range
+         */
+        uint8_t sMAX_RESISTANCE: 1;
+        /*
+         * The The Heater current in the CCS811 is not in range
+         */
+        uint8_t sHEATER_FAULT: 1;
+        /*
+         * The Heater voltage is not being applied correctly
+         */
+        uint8_t sHEATER_SUPPLY: 1;
+    } __attribute__ ((packed))sError_id;
+    
+    typedef struct{
+        /* 
+         * ALG_RESULT_DATA crosses one of the thresholds set in the THRESHOLDS register 
+         * by more than the hysteresis value (also in the THRESHOLDS register)
+         */
+        uint8_t sINT_THRESH: 1;
+        /* 
+         * At the end of each measurement cycle (250ms, 1s, 10s, 60s) a flag is set in the
+         * STATUS register regardless of the setting of this bit.
+         */
+        uint8_t sINT_DATARDY: 1;
+        /* 
+         * A new sample is placed in ALG_RESULT_DATA and RAW_DATA registers and the
+         * DATA_READY bit in the STATUS register is set at the defined measurement interval.
+         */
+        uint8_t sDRIVE_MODE: 3;
+    } __attribute__ ((packed))sMeas_mode;
+    
+    typedef struct{
+        /* 
+         * This bit is cleared by reading ERROR_ID
+         * It is not sufficient to read the ERROR field of ALG_RESULT_DATA and STATUS
+         */
+        uint8_t sERROR: 1;
+        /* 
+         * ALG_RESULT_DATA is read on the I²C interface
+         */
+        uint8_t sDATA_READY: 1;
+        uint8_t sAPP_VALID: 1;
+        /* 
+         * After issuing a VERIFY command the application software must wait 70ms before 
+         * issuing any transactions to CCS811 over the I²C interface
+         */
+        uint8_t sAPP_VERIFY: 1;
+        /* 
+         * After issuing the ERASE command the application software must wait 500ms 
+         * before issuing any transactions to the CCS811 over the I2C interface.
+         */
+        uint8_t sAPP_ERASE: 1;
+        uint8_t sFW_MODE: 1;
+    } __attribute__ ((packed))sStatus;
+    
+    
+    void getData(void);
+    
+    void writeConfig();
+         
+    virtual void writeReg(uint8_t reg, const void* pBuf, size_t size);
+    virtual uint8_t readReg(uint8_t reg, const void* pBuf, size_t size);
+    
+    
+
+private:
+    TwoWire *_pWire;
+    
+    uint16_t eCO2;
+    uint16_t eTVOC;
+};
+
+#endif
diff --git a/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/Arduino/libraries/CS811/CCS811.cpp b/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/Arduino/libraries/CS811/CCS811.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..912ce761986d70be0fe8fc8aa0b9897911a24a52
--- /dev/null
+++ b/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/Arduino/libraries/CS811/CCS811.cpp
@@ -0,0 +1,154 @@
+#include "CCS811.h"
+
+int CCS811::begin(void)
+{
+    uint8_t id=0;
+    Wire.begin();
+    softReset();
+    delay(100);
+    if(readReg(CCS811_REG_HW_ID,&id,1) != 1){DBG("");
+        DBG("bus data access error");DBG("");
+        return ERR_DATA_BUS;DBG("");
+    }
+
+    DBG("real sensor id=");DBG(id);
+    if(id != CCS811_HW_ID){DBG("");
+        delay(1);
+        return ERR_IC_VERSION;
+    }
+    writeReg(CCS811_BOOTLOADER_APP_START, NULL, 0);
+    setMeasurementMode(0,0,eMode4);
+    setInTempHum(25, 50);
+    return ERR_OK;
+}
+
+void CCS811::softReset(){
+    uint8_t value[4] = {0x11, 0xE5, 0x72, 0x8A};
+    writeReg(CCS811_REG_SW_RESET, value, 4);
+}
+
+bool CCS811::checkDataReady()
+{
+    int8_t status[1] = {0};
+    readReg(CCS811_REG_STATUS, status, 1);
+    DBG(status[0],HEX);
+    if(!((status[0] >> 3) & 0x01))
+        return false;
+    else 
+        return true;
+}
+
+uint16_t CCS811::readBaseLine(){
+    uint8_t buffer[2];
+    readReg(CCS811_REG_BASELINE, buffer, 2);
+    return buffer[0]<<8|buffer[1];
+}
+
+void CCS811::writeBaseLine(uint16_t baseLine){
+    uint8_t buffer[2];
+    
+    buffer[0] = baseLine>>8;
+    buffer[1] = baseLine;
+    writeReg(CCS811_REG_BASELINE, buffer, 2);
+}
+
+void CCS811::setMeasurementMode(uint8_t thresh, uint8_t interrupt, eDRIVE_MODE_t mode){
+    uint8_t measurement[1] = {0};
+    measurement[0] = (thresh << 2) | (interrupt << 3) | (mode << 4);
+    writeReg(CCS811_REG_MEAS_MODE, measurement, 1);
+}
+
+void CCS811::setMeasCycle(eCycle_t cycle){
+    uint8_t measurement[1] = {0};   
+    measurement[0] = cycle << 4;
+    writeReg(CCS811_REG_MEAS_MODE, measurement, 1);
+}
+
+uint8_t CCS811::getMeasurementMode(){
+    uint8_t meas[1] = {0};
+    readReg(CCS811_REG_MEAS_MODE, meas, 1);
+    return meas[0];
+}
+
+void CCS811::setThresholds(uint16_t lowToMed, uint16_t medToHigh)
+{
+    uint8_t buffer[] = {(uint8_t)((lowToMed >> 8) & 0xF),
+                        (uint8_t)(lowToMed & 0xF),
+                        (uint8_t)((medToHigh >> 8) & 0xF),
+                        (uint8_t)(medToHigh & 0xF)};
+    
+    writeReg(CCS811_REG_THRESHOLDS, buffer, 5);
+    uint8_t buf[1];
+    readReg(CCS811_REG_THRESHOLDS, buf, 1);
+    Serial.println(buf[0],HEX);
+}
+
+uint16_t CCS811::getCO2PPM(){
+    uint8_t buffer[8];
+    readReg(CCS811_REG_ALG_RESULT_DATA, buffer, 8);
+    eCO2 = (((uint16_t)buffer[0] << 8) | (uint16_t)buffer[1]);
+    return eCO2;
+}
+
+uint16_t CCS811::getTVOCPPB(){
+    uint8_t buffer[8];
+    readReg(CCS811_REG_ALG_RESULT_DATA, buffer, 8);
+    eTVOC = (((uint16_t)buffer[2] << 8) | (uint16_t)buffer[3]);
+    return eTVOC;
+}
+
+void CCS811::setInTempHum(float temperature, float humidity)    // compensate for temperature and relative humidity
+{
+    int _temp, _rh;
+    if(temperature>0)
+        _temp = (int)temperature + 0.5;  // this will round off the floating point to the nearest integer value
+    else if(temperature<0) // account for negative temperatures
+        _temp = (int)temperature - 0.5;
+    _temp = _temp + 25;  // temperature high byte is stored as T+25°C in the sensor's memory so the value of byte is positive
+    _rh = (int)humidity + 0.5;  // this will round off the floating point to the nearest integer value
+    
+    uint8_t envData[4];
+    
+    envData[0] = _rh << 1;  // shift the binary number to left by 1. This is stored as a 7-bit value
+    envData[1] = 0;  // most significant fractional bit. Using 0 here - gives us accuracy of +/-1%. Current firmware (2016) only supports fractional increments of 0.5
+    envData[2] = _temp << 1;
+    envData[3] = 0;
+    
+    writeReg(CCS811_REG_ENV_DATA, &envData, 4);
+}
+
+void CCS811::writeReg(uint8_t reg, const void* pBuf, size_t size)
+{
+    if(pBuf == NULL){
+        DBG("pBuf ERROR!! : null pointer");
+    }
+    uint8_t * _pBuf = (uint8_t *)pBuf;
+    _pWire->beginTransmission(_deviceAddr);
+    _pWire->write(&reg, 1);
+    
+    for(uint16_t i = 0; i < size; i++){
+        _pWire->write(_pBuf[i]);
+    }
+    _pWire->endTransmission();
+}
+
+uint8_t CCS811::readReg(uint8_t reg, const void* pBuf, size_t size)
+{
+    if(pBuf == NULL){
+        DBG("pBuf ERROR!! : null pointer");
+    }
+    uint8_t * _pBuf = (uint8_t *)pBuf;
+    _pWire->beginTransmission(_deviceAddr);
+    _pWire->write(&reg, 1);
+    
+    if( _pWire->endTransmission() != 0){
+        return 0;
+    }
+
+    _pWire->requestFrom(_deviceAddr, (uint8_t) size);
+    for(uint16_t i = 0; i < size; i++){
+        _pBuf[i] = _pWire->read();
+    }
+    _pWire->endTransmission();
+    return size;
+}
diff --git a/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/Arduino/libraries/CS811/CCS811.h b/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/Arduino/libraries/CS811/CCS811.h
new file mode 100644
index 0000000000000000000000000000000000000000..657a06452a8d729b5196b16d1b98111af48d4198
--- /dev/null
+++ b/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/Arduino/libraries/CS811/CCS811.h
@@ -0,0 +1,224 @@
+#ifndef _CCS811_H
+#define _CCS811_H
+
+#if ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+#include <Wire.h>
+
+
+/*I2C ADDRESS*/
+#define CCS811_I2C_ADDRESS1                      0x5A
+#define CCS811_I2C_ADDRESS2                      0x5B
+
+#define CCS811_REG_STATUS                        0x00
+#define CCS811_REG_MEAS_MODE                     0x01
+#define CCS811_REG_ALG_RESULT_DATA               0x02
+#define CCS811_REG_RAW_DATA                      0x03
+#define CCS811_REG_ENV_DATA                      0x05
+#define CCS811_REG_NTC                           0x06
+#define CCS811_REG_THRESHOLDS                    0x10
+#define CCS811_REG_BASELINE                      0x11
+#define CCS811_REG_HW_ID                         0x20
+#define CCS811_REG_HW_VERSION                    0x21
+#define CCS811_REG_FW_BOOT_VERSION               0x23
+#define CCS811_REG_FW_APP_VERSION                0x24
+#define CCS811_REG_INTERNAL_STATE                0xA0
+#define CCS811_REG_ERROR_ID                      0xE0
+#define CCS811_REG_SW_RESET                      0xFF
+
+#define CCS811_BOOTLOADER_APP_ERASE              0xF1
+#define CCS811_BOOTLOADER_APP_DATA               0xF2
+#define CCS811_BOOTLOADER_APP_VERIFY             0xF3
+#define CCS811_BOOTLOADER_APP_START              0xF4
+
+#define CCS811_HW_ID                             0x81
+//Open the macro to see the detailed program execution process.
+//#define ENABLE_DBG
+
+#ifdef ENABLE_DBG
+#define DBG(...) {Serial.print("[");Serial.print(__FUNCTION__); Serial.print("(): "); Serial.print(__LINE__); Serial.print(" ] "); Serial.println(__VA_ARGS__);}
+#else
+#define DBG(...)
+#endif
+
+class CCS811
+{
+public:
+    #define ERR_OK             0      //OK 
+    #define ERR_DATA_BUS      -1      //error in data bus
+    #define ERR_IC_VERSION    -2      //chip version mismatch
+    
+    uint8_t _deviceAddr;
+    typedef enum{
+        eMode0, //Idle (Measurements are disabled in this mode)
+        eMode1, //Constant power mode, IAQ measurement every second
+        eMode2, //Pulse heating mode IAQ measurement every 10 seconds
+        eMode3, //Low power pulse heating mode IAQ measurement every 60 seconds
+        eMode4  //Constant power mode, sensor measurement every 250ms 1xx: Reserved modes (For future use)
+    }eDRIVE_MODE_t;
+    
+    typedef enum{
+        eClosed,      //Idle (Measurements are disabled in this mode)
+        eCycle_1s,    //Constant power mode, IAQ measurement every second
+        eCycle_10s,   //Pulse heating mode IAQ measurement every 10 seconds
+        eCycle_60s,   //Low power pulse heating mode IAQ measurement every 60 seconds
+        eCycle_250ms  //Constant power mode, sensor measurement every 250ms 1xx: Reserved modes (For future use)
+    }eCycle_t;
+    /**
+     * @brief Constructor 
+     * @param Input in Wire address
+     */
+    CCS811(TwoWire *pWire = &Wire, uint8_t deviceAddr = 0x5A){_pWire = pWire; _deviceAddr = deviceAddr;};
+    
+              /**
+               * @brief Constructor
+               * @return Return 0 if initialization succeeds, otherwise return non-zero.
+               */
+    int       begin();
+              /**
+               * @brief Judge if there is data to read 
+               * @return Return 1 if there is, otherwise return 0. 
+               */
+    bool      checkDataReady();
+              /**
+               * @brief Reset sensor, clear all configured data.
+               */
+    void      softReset(),
+              /**
+               * @brief Set environment parameter 
+               * @param temperature Set temperature value, unit: centigrade, range (-40~85℃)
+               * @param humidity    Set humidity value, unit: RH, range (0~100)
+               */
+              setInTempHum(float temperature, float humidity),
+              /**
+               * @brief Measurement parameter configuration 
+               * @param thresh:0 for Interrupt mode operates normally; 1 for interrupt mode only asserts the nINT signal (driven low) if the new
+               * @param interrupt:0 for Interrupt generation is disabled; 1 for the nINT signal is asserted (driven low) when a new sample is ready in
+               * @param mode:in typedef enum eDRIVE_MODE_t
+               */
+              setMeasurementMode(uint8_t thresh, uint8_t interrupt, eDRIVE_MODE_t mode),
+              /**
+               * @brief Measurement parameter configuration 
+               * @param mode:in typedef enum eDRIVE_MODE_t
+               */
+              setMeasCycle(eCycle_t cycle),
+              /**
+               * @brief Set interrupt thresholds 
+               * @param lowToMed: interrupt triggered value in range low to middle 
+               * @param medToHigh: interrupt triggered value in range middle to high 
+               */
+              setThresholds(uint16_t lowToMed, uint16_t medToHigh);
+              /**
+               * @brief Get current configured parameter
+               * @return configuration code, needs to be converted into binary code to analyze
+               *         The 2nd: Interrupt mode (if enabled) operates normally,1: Interrupt mode (if enabled) only asserts the nINT signal (driven low) if the new
+               *         The 3rd: Interrupt generation is disabled,1: The nINT signal is asserted (driven low) when a new sample is ready in
+               *         The 4th: 6th: in typedef enum eDRIVE_MODE_t
+               */
+    uint8_t   getMeasurementMode();
+
+              /**
+               * @brief Get the current carbon dioxide concentration
+               * @return current carbon dioxide concentration, unit:ppm
+               */
+    uint16_t  getCO2PPM(),
+              /**
+               * @brief Get current TVOC concentration
+               * @return Return current TVOC concentration, unit: ppb
+               */
+              getTVOCPPB();
+    uint16_t  readBaseLine();
+    void      writeBaseLine(uint16_t baseLine);
+    
+protected:
+
+    typedef struct{
+        /*
+         * The CCS811 received an I²C write request addressed to this station but with invalid register address ID
+         */
+        uint8_t sWRITE_REG_INVALID: 1;
+        /*
+         * The CCS811 received an I²C read request to a mailbox ID that is invalid
+         */
+        uint8_t sREAD_REG_INVALID: 1;
+        /*
+         * The CCS811 received an I²C request to write an unsupported mode to MEAS_MODE
+         */
+        uint8_t sMEASMODE_INVALID: 1;
+        /*
+         * The sensor resistance measurement has reached or exceeded the maximum range
+         */
+        uint8_t sMAX_RESISTANCE: 1;
+        /*
+         * The The Heater current in the CCS811 is not in range
+         */
+        uint8_t sHEATER_FAULT: 1;
+        /*
+         * The Heater voltage is not being applied correctly
+         */
+        uint8_t sHEATER_SUPPLY: 1;
+    } __attribute__ ((packed))sError_id;
+    
+    typedef struct{
+        /* 
+         * ALG_RESULT_DATA crosses one of the thresholds set in the THRESHOLDS register 
+         * by more than the hysteresis value (also in the THRESHOLDS register)
+         */
+        uint8_t sINT_THRESH: 1;
+        /* 
+         * At the end of each measurement cycle (250ms, 1s, 10s, 60s) a flag is set in the
+         * STATUS register regardless of the setting of this bit.
+         */
+        uint8_t sINT_DATARDY: 1;
+        /* 
+         * A new sample is placed in ALG_RESULT_DATA and RAW_DATA registers and the
+         * DATA_READY bit in the STATUS register is set at the defined measurement interval.
+         */
+        uint8_t sDRIVE_MODE: 3;
+    } __attribute__ ((packed))sMeas_mode;
+    
+    typedef struct{
+        /* 
+         * This bit is cleared by reading ERROR_ID
+         * It is not sufficient to read the ERROR field of ALG_RESULT_DATA and STATUS
+         */
+        uint8_t sERROR: 1;
+        /* 
+         * ALG_RESULT_DATA is read on the I²C interface
+         */
+        uint8_t sDATA_READY: 1;
+        uint8_t sAPP_VALID: 1;
+        /* 
+         * After issuing a VERIFY command the application software must wait 70ms before 
+         * issuing any transactions to CCS811 over the I²C interface
+         */
+        uint8_t sAPP_VERIFY: 1;
+        /* 
+         * After issuing the ERASE command the application software must wait 500ms 
+         * before issuing any transactions to the CCS811 over the I2C interface.
+         */
+        uint8_t sAPP_ERASE: 1;
+        uint8_t sFW_MODE: 1;
+    } __attribute__ ((packed))sStatus;
+    
+    
+    void getData(void);
+    
+    void writeConfig();
+         
+    virtual void writeReg(uint8_t reg, const void* pBuf, size_t size);
+    virtual uint8_t readReg(uint8_t reg, const void* pBuf, size_t size);
+    
+    
+
+private:
+    TwoWire *_pWire;
+    
+    uint16_t eCO2;
+    uint16_t eTVOC;
+};
+
+#endif
diff --git a/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/readData.ino b/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/readData.ino
new file mode 100644
index 0000000000000000000000000000000000000000..86f1410f9fb8a94e229356011ba2801bf7736d02
--- /dev/null
+++ b/ESP8266_Transmitter/Arduino/libraries/CCS811/examples/readData/readData.ino
@@ -0,0 +1,48 @@
+#include <CCS811.h>
+
+/*
+ * IIC address default 0x5A, the address becomes 0x5B if the ADDR_SEL is soldered.
+ */
+//CCS811 sensor(&Wire, /*IIC_ADDRESS=*/0x5A);
+CCS811 sensor;
+
+void setup(void)
+{
+    Serial.begin(115200);
+    /*Wait for the chip to be initialized completely, and then exit*/
+    while(sensor.begin() != 0){
+        Serial.println("failed to init chip, please check if the chip connection is fine");
+        delay(1000);
+    }
+    /**
+     * @brief Set measurement cycle
+     * @param cycle:in typedef enum{
+     *                  eClosed,      //Idle (Measurements are disabled in this mode)
+     *                  eCycle_1s,    //Constant power mode, IAQ measurement every second
+     *                  eCycle_10s,   //Pulse heating mode IAQ measurement every 10 seconds
+     *                  eCycle_60s,   //Low power pulse heating mode IAQ measurement every 60 seconds
+     *                  eCycle_250ms  //Constant power mode, sensor measurement every 250ms
+     *                  }eCycle_t;
+     */
+    sensor.setMeasCycle(sensor.eCycle_250ms);
+}
+void loop() {
+  delay(1000);
+    if(sensor.checkDataReady() == true){
+        Serial.print("CO2: ");
+        Serial.print(sensor.getCO2PPM());
+        Serial.print("ppm, TVOC: ");
+        Serial.print(sensor.getTVOCPPB());
+        Serial.println("ppb");
+        
+    } else {
+        Serial.println("Data is not ready!");
+    }
+    /*!
+     * @brief Set baseline
+     * @param get from getBaseline.ino
+     */
+    sensor.writeBaseLine(0x847B);
+    //delay cannot be less than measurement cycle
+    //delay(1000);
+}
diff --git a/ESP8266_Transmitter/ESP8266_Transmitter.ino b/ESP8266_Transmitter/ESP8266_Transmitter.ino
index a5d7062f98ed746c8aeffb0b0faf463e8490092b..d80a17c3e9c88201b4e2f89df48dea1af3db196f 100644
--- a/ESP8266_Transmitter/ESP8266_Transmitter.ino
+++ b/ESP8266_Transmitter/ESP8266_Transmitter.ino
@@ -25,6 +25,7 @@
  *    ArduinoJson v6 Library - https://arduinojson.org/v6/doc/ (https://github.com/sandeepmistry/arduino-LoRa/blob/master/LICENSE)
  *    LoRa Library - https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md (https://github.com/bblanchon/ArduinoJson/blob/6.x/LICENSE.md)
  *    SHTSensor - https://github.com/Sensirion/arduino-sht (https://github.com/Sensirion/arduino-sht/blob/master/LICENSE)
+ *    CCS811 - https://wiki.keyestudio.com/KS0457_keyestudio_CCS811_Carbon_Dioxide_Air_Quality_Sensor
  *
  *  =========================================================================================
  *  
@@ -38,21 +39,30 @@
 #include <LoRa.h>
 #include <SoftwareSerial.h>
 #include <ArduinoJson.h>
-/* #include <base64.h> */
 #include "SHTSensor.h"
+#include <CCS811.h>
 
 // Debug / Polling / Transmitting Config
-#define DEBUG 1
+#define DEBUG 2
 #define MIN_RAND_DELAY 500 // ms
 #define MAX_RAND_DELAY 1250 // ms
 #define MAX_TRANSMISSION_RETRIES 5 // No. Retries before recordings new values then retry
 #define TX_RESERVATION_TIME 1 // How long do we require use of TX for sending packets? (Seconds)
 
+// Poor mans memory-contrained hashmap alternative. Store index of polled count for each sensor in pollEventCountPerSensor
+#define IDX_PPM10 0
+#define IDX_PPM25 1
+#define IDX_PPM100 2
+#define IDX_TEMPERATURE 3
+#define IDX_HUMIDITY 4
+#define IDX_CO2 5
+
 // Variable Values - May be updated by Gateway
 int TX_AFTER_N_READINGS = 4; // No. of samples, after which Transmit Avg Readings
 int POLLING_FREQUENCY = 30000; // ms Take value every POLLING_FREQUENCY ms
 
 int pollEventCount = 0; // Number of times data has been sampled in this recording period
+int pollEventCountPerSensor[6] = {0}; // Number of times each sensor has been polled in this recording period (Used for averaging values)
 
 // Lora Config
 #define ss 16 // Physical Pin 16 = D0 (Default is Physical Pin 5)
@@ -62,34 +72,32 @@ static const int loraSpreadingFactor = 7;
 static const int loraSignalBandwidth = 125E3;
 static const int loraFrequency = 433E6;
 
-// Serial PMS Config
-static const int RXPin = 4, TXPin = 3;
-
-// Temp / Humidity Sensor
-SHTSensor sht;
-
-// Particle Sensor Serial
-SoftwareSerial pmsSerial(2, 3);
-
 // LoRa Message Tracking
 String sensorID = "";
 byte localAddress = 0xBB;
 byte destination = 0xFF;
 uint32_t msgCount = 0;
 
+// Sensor Config
+static const int RXPin = 4, TXPin = 3; // Serial PMS Config
+SHTSensor sht; // Temp / Humidity Sensor
+SoftwareSerial pmsSerial(2, 3); // Particle Sensor Serial
+CCS811 co2Sensor; // CO2 Sensor
+
+// Immediate Sensor Data
 double temperature; // Sensor Values - Temp 
 double humidity;    // Sensor Values - Humidity
 double co2;         // Sensor Values - Co2
-uint32_t ppm10;     // Sensor Values - Particulate
-uint32_t ppm25;     // Sensor Values - Particulate
-uint32_t ppm100;    // Sensor Values - Particulate
+int32_t ppm10;     // Sensor Values - Particulate
+int32_t ppm25;     // Sensor Values - Particulate
+int32_t ppm100;    // Sensor Values - Particulate
 
 // Partial Sensor Data
 struct pms5003data {
   uint16_t framelen;
-  uint16_t pm10_standard, pm25_standard, pm100_standard;
-  uint16_t pm10_env, pm25_env, pm100_env;
-  uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um;
+  int32_t pm10_standard, pm25_standard, pm100_standard;
+  int32_t pm10_env, pm25_env, pm100_env;
+  int32_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um;
   uint16_t unused;
   uint16_t checksum;
 };
@@ -132,10 +140,41 @@ double getHumidity() {
 }
 
 /**
- * TODO getCo2()
+ * Setup CO2 Sensor
+ */
+ bool setupCO2() {
+  if (co2Sensor.begin() != 0) {
+    Serial.println("[-] CO2 Init Failed");
+    return false;
+  }
+
+  /**
+   * @brief Set measurement cycle
+   * @param cycle:in typedef enum{
+   *                  eClosed,      //Idle (Measurements are disabled in this mode)
+   *                  eCycle_1s,    //Constant power mode, IAQ measurement every second
+   *                  eCycle_10s,   //Pulse heating mode IAQ measurement every 10 seconds
+   *                  eCycle_60s,   //Low power pulse heating mode IAQ measurement every 60 seconds
+   *                  eCycle_250ms  //Constant power mode, sensor measurement every 250ms
+   *                  }eCycle_t;
+   */
+  co2Sensor.setMeasCycle(co2Sensor.eCycle_250ms);
+  return true;
+}
+
+/**
+ * Get CO2 reading, returns -1 on error 
  */
 double getCo2() {
-  return 0;
+  double c = -1;
+  
+  co2Sensor.writeBaseLine(0x847B);
+  
+  if(co2Sensor.checkDataReady() == true){
+    c = co2Sensor.getCO2PPM();
+  }
+
+  return c; // -1 on error
 }
 
 
@@ -526,6 +565,28 @@ boolean transmitData(DynamicJsonDocument payload) {
   return true;
 }
 
+/**
+ * Add recorded values to the total, do not store false/failed/invalid values, e.g. -1
+ */
+double addValueToTotal(double avgValue, double newValue, int idx_sensor_count) {
+  if (newValue == -1) {
+    return avgValue;
+  }
+
+  pollEventCountPerSensor[idx_sensor_count] += 1;
+  return avgValue + newValue;
+}
+
+double averageValueByNumberPolls(double value, int idx_sensor_count) {
+  // Ensure we do not divide by 0
+  if (pollEventCountPerSensor[idx_sensor_count] == 0) {
+    return -1;
+  }
+
+  // Otherwise divide total by number of times we sampled this value
+  return value / pollEventCountPerSensor[idx_sensor_count];
+}
+
 void setup() {
   delay(1000);
   
@@ -534,64 +595,59 @@ void setup() {
 
   pmsSerial.begin(9600); // Partical Sensor
 
-  // Setup Hardware
-  if (!setupSHT()) { while(1); } // Temp/Humidity - Die on Error
-  if (!setupLoRa()) { while(1); } // Die on error
+  // Setup Hardware TODO Handle broken sensor!
+  if (!setupSHT()) { /*while(1);*/ } // Temp/Humidity - Die on Error
+  if (!setupCO2()) { /*while(1);*/ }
+  if (!setupLoRa()) { /*while(1);*/ } // Die on error
 }
 
-// Main
-void loop() {
-  // TODO Gather Sensor Data
+void loop() {  
+  delay(POLLING_FREQUENCY);
+  pollEventCount++;
 
-  // Error reading PMS Data, ignore on this round
-  if (!readPMSdata(&pmsSerial)) {
-    data.pm10_standard = 0;
-    data.pm25_standard = 0;
-    data.pm100_standard = 0; 
+  // Get values from sensors
+  double instantTemperature = getTemperature(); // Prevent polling of sensors too frequently when debugging
+  double instantHumidity = getHumidity();
+  double instantCo2 = getCo2();
+
+  int instantPPM10, instantPPM25, instantPPM100;
+  if (!readPMSdata(&pmsSerial)) { // Error reading PMS Data, ignore on this round
+    instantPPM10 = instantPPM25 = instantPPM100 = -1; 
+    
+  } else {
+    instantPPM10 = data.pm10_standard;  // Standard (_standard) or Environmental (_env) readings for Particulate Data
+    instantPPM25 = data.pm25_standard;  
+    instantPPM100 = data.pm100_standard;
   }
   
-  pollEventCount++;
-  
   if (DEBUG) {
-    Serial.println();
-    Serial.print(String(pollEventCount) + ") ");
-    Serial.print("PM 1.0: "); Serial.print(data.pm10_standard);
-    Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_standard);
-    Serial.print("\t\tPM 10: "); Serial.print(data.pm100_standard);
-    Serial.print("\t\tTemp: "); Serial.print(getTemperature());
-    Serial.print("\t\tHumidity: "); Serial.print(getHumidity());
-    Serial.print("\t\tCo2: "); Serial.print(getCo2());
+    Serial.println(); Serial.print(String(pollEventCount) + ") "); Serial.print("PM 1.0: "); Serial.print(instantPPM10); Serial.print("\t\tPM 2.5: "); Serial.print(instantPPM25); Serial.print("\t\tPM 10: "); Serial.print(instantPPM100);
+    Serial.print("\t\tTemp: "); Serial.print(instantTemperature); Serial.print("\t\tHumidity: "); Serial.print(instantHumidity); Serial.print("\t\tCo2: "); Serial.print(instantCo2);
   }
 
-  // Add to Average
-  temperature += getTemperature();
-  humidity += getHumidity();
-  co2 += getCo2();
-  ppm10 = ppm10 + data.pm10_standard; // Standard (_standard) or Environmental (_env) readings for Particulate Data
-  ppm25 = ppm25 + data.pm25_standard;
-  ppm100 = ppm100 + data.pm100_standard;
+  // Add to running total, we will divide by the number of times samples later on to get an average over sample period
+  temperature = addValueToTotal(temperature,  instantTemperature, IDX_TEMPERATURE);
+  humidity    = addValueToTotal(humidity,     instantHumidity,    IDX_HUMIDITY);
+  co2         = addValueToTotal(co2,          instantCo2,         IDX_CO2);
+  ppm10       = addValueToTotal(ppm10,        instantPPM10,       IDX_PPM10);
+  ppm25       = addValueToTotal(ppm25,        instantPPM25,       IDX_PPM25);
+  ppm100      = addValueToTotal(ppm100,       instantPPM100,      IDX_PPM100);
 
   // If we should now transmit
   if (pollEventCount >= TX_AFTER_N_READINGS) {
 
     // Average Values over recording period
-    double avgTemperature = temperature / pollEventCount;
-    double avgHumidity = humidity / pollEventCount;
-    double avgCo2 = co2 / pollEventCount;
-    uint32_t avgPpm10 = ppm10 / pollEventCount;
-    uint32_t avgPpm25 = ppm25 / pollEventCount;
-    uint32_t avgPpm100 = ppm100 / pollEventCount;
+    double  avgTemperature  = averageValueByNumberPolls(temperature,  IDX_TEMPERATURE);
+    double  avgHumidity     = averageValueByNumberPolls(humidity,     IDX_HUMIDITY);
+    double  avgCo2          = averageValueByNumberPolls(co2,          IDX_CO2);
+    int     avgPpm10        = averageValueByNumberPolls(ppm10,        IDX_PPM10);
+    int     avgPpm25        = averageValueByNumberPolls(ppm25,        IDX_PPM25);
+    int     avgPpm100       = averageValueByNumberPolls(ppm100,       IDX_PPM100);
 
     if (DEBUG) {
-      Serial.println("");
-      Serial.print("Avg ppm10: "); Serial.print(avgPpm10);
-      Serial.print("\t\tAvg ppm25: "); Serial.print(avgPpm25);
-      Serial.print("\t\tAvg ppm100: "); Serial.print(avgPpm100);
-      Serial.print("\t\tAvg Temp: "); Serial.print(avgTemperature);
-      Serial.print("\t\tAvg Humidity: "); Serial.print(avgHumidity);
-      Serial.print("\t\tAvg Co2: "); Serial.print(avgCo2);
-      Serial.print("\t\tChip ID: "); Serial.println(getSensorUID());
-      Serial.println("");
+      Serial.println(""); Serial.print("Avg ppm10: "); Serial.print(avgPpm10); Serial.print("\t\tAvg ppm25: "); Serial.print(avgPpm25); Serial.print("\t\tAvg ppm100: "); Serial.print(avgPpm100);
+      Serial.print("\t\tAvg Temp: "); Serial.print(avgTemperature); Serial.print("\t\tAvg Humidity: "); Serial.print(avgHumidity); Serial.print("\t\tAvg Co2: "); Serial.print(avgCo2);
+      Serial.print("\t\tChip ID: "); Serial.println(getSensorUID()); Serial.println("");
     }
 
     // Prepare Data For Send
@@ -621,6 +677,4 @@ void loop() {
     Serial.println("[-] Failed to send packet, max retries reached. Aborting");
     return;
   }
-
-  delay(POLLING_FREQUENCY);
 }