diff --git a/M5Atom_airqa.ino.m5stick_c.bin b/41.bin similarity index 100% rename from M5Atom_airqa.ino.m5stick_c.bin rename to 41.bin diff --git a/42.bin b/42.bin new file mode 100644 index 0000000..be66f93 Binary files /dev/null and b/42.bin differ diff --git a/43.bin b/43.bin new file mode 100644 index 0000000..43da923 Binary files /dev/null and b/43.bin differ diff --git a/M5Atom_airqa.ino b/M5Atom_airqa.ino index 01804db..257a82b 100644 --- a/M5Atom_airqa.ino +++ b/M5Atom_airqa.ino @@ -55,7 +55,7 @@ #define CCS811_ADDR 0x5B //Default I2C Address //#define CCS811_ADDR 0x5A //Alternate I2C Address -unsigned int VersionSW = 42; //65536 Версия прошивки +unsigned int VersionSW = 43; //65536 Версия прошивки //15 - добавлено то, се, забыл вообще дописать что добавленоSerial //19 - вывод в консоль всех действий, ошибки с обновлнеием - починил, прияногое мигание светодиодом, тест для поиска metrics. @@ -63,7 +63,7 @@ unsigned int VersionSW = 42; //65536 Версия прошивки //22 - поправлено поиск сервера по metrics local. //23 - добавленн BSID //24 - полный передел всего -//25 - новые топики, спящий режим +//25 - новые топики, спящий режим и куча иземенний связанных со спящим режимом. //26 - тест обновления сука - работает //27 - таймаут станции без wifi 40 сек //28 - калибровка вернулась @@ -77,9 +77,10 @@ unsigned int VersionSW = 42; //65536 Версия прошивки //36 - настройка mqtt по ip и режимы датчика - неудачная загрузка 27.03 //37-38 - сохранение ip metrics, и чтение из памяти, убрана базовая линия. Логин пароль. // Фиксим получение временя 29.03 -//39 новая функция преобразования Ip в строку. +//39 новая функция преобразования строку в ip. //41 уменьшил код на 7%. Перезалив. Отвалились 3 из 5, но не отвалились, ходят за обновлениями, все успешно -//42 +//42 Новая функция получения данных с CO2. Что-то не вяжется с либой. +//43 Больше задержек для чтения i2c WiFiManager wm; // обьект менеджера WiFiManagerParameter custom_field; @@ -479,7 +480,7 @@ void reqSensorData() hdc1080Temp = hdc1080.readTemperature(); hdc1080Hum = hdc1080.readHumidity(); - + delay(50); char outstr[6]; dtostrf(hdc1080Temp, 5, 2, outstr); TempAv = atof(outstr); @@ -487,45 +488,55 @@ void reqSensorData() HumAv = atof(outstr); int counAvail = 0; + byte countRead = 0; ledset(4, false); - while (!ccs.available()) + + bool ccsflag = true; + + while (ccsflag) { - counAvail++; - if (counAvail >= 1000) + if (ccs.available()) { - break; + ccs.setEnvironmentalData(hdc1080Hum - 55, hdc1080Temp - 25); + + if (ccs.readData() == 0) + { + countRead++; + if (countRead >= 7) + { + ccsflag = false; + Serial.println(counAvail); + } + delay(1050); + } + else + { + Serial.println("=======ECO TVOC NONE READ"); + } + } + + counAvail++; + if (counAvail >= 6000) + { + ccsflag = false; } } Serial.print("counAvail: "); Serial.println(counAvail); - for (int t = 0; t <= 7; t++) - { - Serial.println(ccs.readData()); - { - ledset(2, false); - ccs.setEnvironmentalData(hdc1080Hum - 55, hdc1080Temp - 25); - - delay(1200); - } - else - { - Serial.println("=======ECO TVOC NONE READ"); - } - } + ledset(2, false); - eco2 = ccs.geteCO2(); - tvoc = ccs.getTVOC(); - BaselineTvoc = ccs.getBaseline(); - Serial.print(eco2); - Serial.print(" "); - Serial.print(tvoc); - Serial.print(" "); - Serial.println(BaselineTvoc); + Eco2Av = ccs.geteCO2(); + TvocAv = ccs.getTVOC(); + BaselineTvoc = ccs.getBaseline(); - Eco2Av = eco2; - TvocAv = tvoc; + Serial.print(eco2); + Serial.print(" "); + Serial.print(tvoc); + Serial.print(" "); + Serial.println(BaselineTvoc); + rssi = WiFi.RSSI(); } diff --git a/ccs811.cpp b/ccs811.cpp new file mode 100644 index 0000000..9c12ed8 --- /dev/null +++ b/ccs811.cpp @@ -0,0 +1,574 @@ +/* + ccs811.cpp - Library for the CCS811 digital gas sensor for monitoring indoor air quality from ams. + 2019 jan 22 v10 Maarten Pennings Added F() on all strings, added get/set_baseline() + 2019 jan 15 v9 Maarten Pennings Function set_envdata did not write T; flash now uses PROGMEM array; flash now works without app, better PRINT macros, removed warnings + 2018 dec 06 v8 Maarten Pennings Added firmware flash routine + 2018 Dec 04 v7 Maarten Pennings Added support for older CCS811's (fw 1100) + 2018 Nov 11 v6 Maarten Pennings uint16 -> uint16_t, added cast + 2018 Nov 02 v5 Maarten Pennings Added clearing of ERROR_ID + 2018 Oct 23 v4 Maarten Pennings Added envdata/i2cdelay + 2018 Oct 21 v3 Maarten Pennings Fixed bug in begin(), added hw-version + 2018 Oct 21 v2 Maarten Pennings Simplified I2C, added version mngt + 2017 Dec 11 v1 Maarten Pennings Created +*/ + + +#include +#include +#include "ccs811.h" + + +// begin() and flash() prints errors to help diagnose startup problems. +// Change these macro's to empty to suppress those prints. +#define PRINTLN(s) Serial.println(s) +#define PRINT(s) Serial.print(s) +#define PRINTLN2(s,m) Serial.println(s,m) +#define PRINT2(s,m) Serial.print(s,m) +//#define PRINTLN(s) +//#define PRINT(s) +//#define PRINTLN2(s,m) +//#define PRINT2(s,m) + + +// Timings +#define CCS811_WAIT_AFTER_RESET_US 2000 // The CCS811 needs a wait after reset +#define CCS811_WAIT_AFTER_APPSTART_US 1000 // The CCS811 needs a wait after app start +#define CCS811_WAIT_AFTER_WAKE_US 50 // The CCS811 needs a wait after WAKE signal +#define CCS811_WAIT_AFTER_APPERASE_MS 500 // The CCS811 needs a wait after app erase (300ms from spec not enough) +#define CCS811_WAIT_AFTER_APPVERIFY_MS 70 // The CCS811 needs a wait after app verify +#define CCS811_WAIT_AFTER_APPDATA_MS 50 // The CCS811 needs a wait after writing app data + + +// Main interface ===================================================================================================== + + +// CCS811 registers/mailboxes, all 1 byte except when stated otherwise +#define CCS811_STATUS 0x00 +#define CCS811_MEAS_MODE 0x01 +#define CCS811_ALG_RESULT_DATA 0x02 // up to 8 bytes +#define CCS811_RAW_DATA 0x03 // 2 bytes +#define CCS811_ENV_DATA 0x05 // 4 bytes +#define CCS811_THRESHOLDS 0x10 // 5 bytes +#define CCS811_BASELINE 0x11 // 2 bytes +#define CCS811_HW_ID 0x20 +#define CCS811_HW_VERSION 0x21 +#define CCS811_FW_BOOT_VERSION 0x23 // 2 bytes +#define CCS811_FW_APP_VERSION 0x24 // 2 bytes +#define CCS811_ERROR_ID 0xE0 +#define CCS811_APP_ERASE 0xF1 // 4 bytes +#define CCS811_APP_DATA 0xF2 // 9 bytes +#define CCS811_APP_VERIFY 0xF3 // 0 bytes +#define CCS811_APP_START 0xF4 // 0 bytes +#define CCS811_SW_RESET 0xFF // 4 bytes + + +// Pin number connected to nWAKE (nWAKE can also be bound to GND, then pass -1), slave address (5A or 5B) +CCS811::CCS811(int nwake, int slaveaddr) { + _nwake= nwake; + _slaveaddr= slaveaddr; + _i2cdelay_us= 0; + wake_init(); +} + + +// Reset the CCS811, switch to app mode and check HW_ID. Returns false on problems. +bool CCS811::begin( void ) { + uint8_t sw_reset[]= {0x11,0xE5,0x72,0x8A}; + uint8_t app_start[]= {}; + uint8_t hw_id; + uint8_t hw_version; + uint8_t app_version[2]; + uint8_t status; + bool ok; + + // Wakeup CCS811 + wake_up(); + + // Try to ping CCS811 (can we reach CCS811 via I2C?) + ok= i2cwrite(0,0,0); + if( !ok ) ok= i2cwrite(0,0,0); // retry + if( !ok ) { + // Try the other slave address + _slaveaddr= CCS811_SLAVEADDR_0 + CCS811_SLAVEADDR_1 - _slaveaddr; // swap address + ok= i2cwrite(0,0,0); + _slaveaddr= CCS811_SLAVEADDR_0 + CCS811_SLAVEADDR_1 - _slaveaddr; // swap back + if( ok ) { + PRINTLN(F("ccs811: wrong slave address, ping successful on other address")); + } else { + PRINTLN(F("ccs811: ping failed (VDD/GND connected? SDA/SCL connected?)")); + } + goto abort_begin; + } + + // Invoke a SW reset (bring CCS811 in a know state) + ok= i2cwrite(CCS811_SW_RESET,4,sw_reset); + if( !ok ) { + PRINTLN(F("ccs811: reset failed")); + goto abort_begin; + } + delayMicroseconds(CCS811_WAIT_AFTER_RESET_US); + + // Check that HW_ID is 0x81 + ok= i2cread(CCS811_HW_ID,1,&hw_id); + if( !ok ) { + PRINTLN(F("ccs811: HW_ID read failed")); + goto abort_begin; + } + if( hw_id!=0x81 ) { + PRINT(F("ccs811: Wrong HW_ID: ")); + PRINTLN2(hw_id,HEX); + goto abort_begin; + } + + // Check that HW_VERSION is 0x1X + ok= i2cread(CCS811_HW_VERSION,1,&hw_version); + if( !ok ) { + PRINTLN(F("ccs811: HW_VERSION read failed")); + goto abort_begin; + } + if( (hw_version&0xF0)!=0x10 ) { + PRINT(F("ccs811: Wrong HW_VERSION: ")); + PRINTLN2(hw_version,HEX); + goto abort_begin; + } + + // Check status (after reset, CCS811 should be in boot mode with valid app) + ok= i2cread(CCS811_STATUS,1,&status); + if( !ok ) { + PRINTLN(F("ccs811: STATUS read (boot mode) failed")); + goto abort_begin; + } + if( status!=0x10 ) { + PRINT(F("ccs811: Not in boot mode, or no valid app: ")); + PRINTLN2(status,HEX); + goto abort_begin; + } + + // Read the application version + ok= i2cread(CCS811_FW_APP_VERSION,2,app_version); + if( !ok ) { + PRINTLN(F("ccs811: APP_VERSION read failed")); + goto abort_begin; + } + _appversion= app_version[0]*256+app_version[1]; + + // Switch CCS811 from boot mode into app mode + ok= i2cwrite(CCS811_APP_START,0,app_start); + if( !ok ) { + PRINTLN(F("ccs811: Goto app mode failed")); + goto abort_begin; + } + delayMicroseconds(CCS811_WAIT_AFTER_APPSTART_US); + + // Check if the switch was successful + ok= i2cread(CCS811_STATUS,1,&status); + if( !ok ) { + PRINTLN(F("ccs811: STATUS read (app mode) failed")); + goto abort_begin; + } + if( status!=0x90 ) { + PRINT(F("ccs811: Not in app mode, or no valid app: ")); + PRINTLN2(status,HEX); + goto abort_begin; + } + + // CCS811 back to sleep + wake_down(); + // Return success + return true; + +abort_begin: + // CCS811 back to sleep + wake_down(); + // Return failure + return false; +} + + +// Switch CCS811 to `mode`, use constants CCS811_MODE_XXX. Returns false on problems. +bool CCS811::start( int mode ) { + uint8_t meas_mode[]= {(uint8_t)(mode<<4)}; + wake_up(); + bool ok = i2cwrite(CCS811_MEAS_MODE,1,meas_mode); + wake_down(); + return ok; +} + + +// Get measurement results from the CCS811, check status via errstat, e.g. ccs811_errstat(errstat) +void CCS811::read( uint16_t*eco2, uint16_t*etvoc, uint16_t*errstat,uint16_t*raw) { + bool ok; + uint8_t buf[8]; + uint8_t stat; + wake_up(); + if( _appversion<0x2000 ) { + ok= i2cread(CCS811_STATUS,1,&stat); // CCS811 with pre 2.0.0 firmware has wrong STATUS in CCS811_ALG_RESULT_DATA + if( ok && stat==CCS811_ERRSTAT_OK ) ok= i2cread(CCS811_ALG_RESULT_DATA,8,buf); else buf[5]=0; + buf[4]= stat; // Update STATUS field with correct STATUS + } else { + ok = i2cread(CCS811_ALG_RESULT_DATA,8,buf); + } + wake_down(); + // Status and error management + uint16_t combined = buf[5]*256+buf[4]; + if( combined & ~(CCS811_ERRSTAT_HWERRORS|CCS811_ERRSTAT_OK) ) ok= false; // Unused bits are 1: I2C transfer error + combined &= CCS811_ERRSTAT_HWERRORS|CCS811_ERRSTAT_OK; // Clear all unused bits + if( !ok ) combined |= CCS811_ERRSTAT_I2CFAIL; + // Clear ERROR_ID if flags are set + if( combined & CCS811_ERRSTAT_HWERRORS ) { + int err = get_errorid(); + if( err==-1 ) combined |= CCS811_ERRSTAT_I2CFAIL; // Propagate I2C error + } + // 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];; +} + + +// Returns a string version of an errstat. Note, each call, this string is updated. +const char * CCS811::errstat_str(uint16_t errstat) { + static char s[17]; // 16 bits plus terminating zero + // First the ERROR_ID flags + s[ 0]='-'; + s[ 1]='-'; + if( errstat & CCS811_ERRSTAT_HEATER_SUPPLY ) s[ 2]='V'; else s[2]='v'; + if( errstat & CCS811_ERRSTAT_HEATER_FAULT ) s[ 3]='H'; else s[3]='h'; + if( errstat & CCS811_ERRSTAT_MAX_RESISTANCE ) s[ 4]='X'; else s[4]='x'; + if( errstat & CCS811_ERRSTAT_MEASMODE_INVALID ) s[ 5]='M'; else s[5]='m'; + if( errstat & CCS811_ERRSTAT_READ_REG_INVALID ) s[ 6]='R'; else s[6]='r'; + if( errstat & CCS811_ERRSTAT_WRITE_REG_INVALID) s[ 7]='W'; else s[7]='w'; + // Then the STATUS flags + if( errstat & CCS811_ERRSTAT_FW_MODE ) s[ 8]='F'; else s[8]='f'; + s[ 9]='-'; + s[10]='-'; + if( errstat & CCS811_ERRSTAT_APP_VALID ) s[11]='A'; else s[11]='a'; + if( errstat & CCS811_ERRSTAT_DATA_READY ) s[12]='D'; else s[12]='d'; + s[13]='-'; + // Next bit is used by SW to signal I2C transfer error + if( errstat & CCS811_ERRSTAT_I2CFAIL ) s[14]='I'; else s[14]='i'; + if( errstat & CCS811_ERRSTAT_ERROR ) s[15]='E'; else s[15]='e'; + s[16]='\0'; + return s; +} + + +// Extra interface ======================================================================================== + + +// Gets version of the CCS811 hardware (returns 0 on I2C failure) +int CCS811::hardware_version(void) { + uint8_t buf[1]; + wake_up(); + bool ok = i2cread(CCS811_HW_VERSION,1,buf); + wake_down(); + int version= -1; + if( ok ) version= buf[0]; + return version; +} + + +// Gets version of the CCS811 boot loader (returns 0 on I2C failure) +int CCS811::bootloader_version(void) { + uint8_t buf[2]; + wake_up(); + bool ok = i2cread(CCS811_FW_BOOT_VERSION,2,buf); + wake_down(); + int version= -1; + if( ok ) version= buf[0]*256+buf[1]; + return version; +} + + +// Gets version of the CCS811 application (returns 0 on I2C failure) +int CCS811::application_version(void) { + uint8_t buf[2]; + wake_up(); + bool ok = i2cread(CCS811_FW_APP_VERSION,2,buf); + wake_down(); + int version= -1; + if( ok ) version= buf[0]*256+buf[1]; + return version; +} + + +// Gets the ERROR_ID [same as 'err' part of 'errstat' in 'read'] (returns -1 on I2C failure) +// Note, this actually clears CCS811_ERROR_ID (hardware feature) +int CCS811::get_errorid(void) { + uint8_t buf[1]; + wake_up(); + bool ok = i2cread(CCS811_ERROR_ID,1,buf); + wake_down(); + int version= -1; + if( ok ) version= buf[0]; + return version; +} + + +#define HI(u16) ( (uint8_t)( ((u16)>>8)&0xFF ) ) +#define LO(u16) ( (uint8_t)( ((u16)>>0)&0xFF ) ) + + +// Writes t and h to ENV_DATA (see datasheet for format). Returns false on I2C problems. +bool CCS811::set_envdata(uint16_t t, uint16_t h) { + uint8_t envdata[]= { HI(h), LO(h), HI(t), LO(t) }; + wake_up(); + // Serial.print(" [T="); Serial.print(t); Serial.print(" H="); Serial.print(h); Serial.println("] "); + bool ok = i2cwrite(CCS811_ENV_DATA,4,envdata); + wake_down(); + return ok; +} + + +// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems. +bool CCS811::set_envdata210(uint16_t t, uint16_t h) { + // Humidity formats of ENS210 and CCS811 are equal, we only need to map temperature. + // The lowest and highest (raw) ENS210 temperature values the CCS811 can handle + uint16_t lo= 15882; // (273.15-25)*64 = 15881.6 (float to int error is 0.4) + uint16_t hi= 24073; // 65535/8+lo = 24073.875 (24074 would map to 65539, so overflow) + // Check if ENS210 temperature is within CCS811 range, if not clip, if so map + bool ok; + if( thi ) ok= set_envdata(65535,h); + else ok= set_envdata( (t-lo)*8+3 , h); // error in 'lo' is 0.4; times 8 is 3.2; so we correct 3 + // Returns I2C transaction status + return ok; +} + + +// 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; +} + + +// Writes (encoded) baseline to BASELINE (see datasheet). Returns false on I2C problems. +bool CCS811::set_baseline(uint16_t baseline) { + uint8_t buf[]= { HI(baseline), LO(baseline) }; + wake_up(); + bool ok = i2cwrite(CCS811_BASELINE,2,buf); + wake_down(); + return ok; +} + + +// Flashes the firmware of the CCS811 with size bytes from image - image _must_ be in PROGMEM +bool CCS811::flash(const uint8_t * image, int size) { + uint8_t sw_reset[]= {0x11,0xE5,0x72,0x8A}; + uint8_t app_erase[]= {0xE7,0xA7,0xE6,0x09}; + uint8_t app_verify[]= {}; + uint8_t status; + int count; + bool ok; + wake_up(); + + // Try to ping CCS811 (can we reach CCS811 via I2C?) + PRINT(F("ccs811: ping ")); + ok= i2cwrite(0,0,0); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + PRINTLN(F("ok")); + + // Invoke a SW reset (bring CCS811 in a know state) + PRINT(F("ccs811: reset ")); + ok= i2cwrite(CCS811_SW_RESET,4,sw_reset); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + delayMicroseconds(CCS811_WAIT_AFTER_RESET_US); + PRINTLN(F("ok")); + + // Check status (after reset, CCS811 should be in boot mode with or without valid app) + PRINT(F("ccs811: status (reset1) ")); + ok= i2cread(CCS811_STATUS,1,&status); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + PRINT2(status,HEX); + PRINT(F(" ")); + if( status!=0x00 && status!=0x10 ) { + PRINTLN(F("ERROR - ignoring")); // Seems to happens when there is no valid app + } else { + PRINTLN(F("ok")); + } + + // Invoke app erase + PRINT(F("ccs811: app-erase ")); + ok= i2cwrite(CCS811_APP_ERASE,4,app_erase); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + delay(CCS811_WAIT_AFTER_APPERASE_MS); + PRINTLN(F("ok")); + + // Check status (CCS811 should be in boot mode without valid app, with erase completed) + PRINT(F("ccs811: status (app-erase) ")); + ok= i2cread(CCS811_STATUS,1,&status); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + PRINT2(status,HEX); + PRINT(F(" ")); + if( status!=0x40 ) { + PRINTLN(F("ERROR")); + goto abort_begin; + } + PRINTLN(F("ok")); + + // Write all blocks + count= 0; + while( size>0 ) { + if( count%64==0 ) { PRINT(F("ccs811: writing ")); PRINT(size); PRINT(F(" ")); } + int len= size<8 ? size : 8; + // Copy PROGMEM to RAM + uint8_t ram[8]; + memcpy_P(ram, image, len); + // Send 8 bytes from RAM to CCS811 + ok= i2cwrite(CCS811_APP_DATA,len, ram); + if( !ok ) { + PRINTLN(F("ccs811: app data failed")); + goto abort_begin; + } + PRINT(F(".")); + delay(CCS811_WAIT_AFTER_APPDATA_MS); + image+= len; + size-= len; + count++; + if( count%64==0 ) { PRINT(F(" ")); PRINTLN(size); } + } + if( count%64!=0 ) { PRINT(F(" ")); PRINTLN(size); } + + // Invoke app verify + PRINT(F("ccs811: app-verify ")); + ok= i2cwrite(CCS811_APP_VERIFY,0,app_verify); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + delay(CCS811_WAIT_AFTER_APPVERIFY_MS); + PRINTLN(F("ok")); + + // Check status (CCS811 should be in boot mode with valid app, and erased and verified) + PRINT(F("ccs811: status (app-verify) ")); + ok= i2cread(CCS811_STATUS,1,&status); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + PRINT2(status,HEX); + PRINT(F(" ")); + if( status!=0x30 ) { + PRINTLN(F("ERROR")); + goto abort_begin; + } + PRINTLN(F("ok")); + + // Invoke a second SW reset (clear flashing flags) + PRINT(F("ccs811: reset2 ")); + ok= i2cwrite(CCS811_SW_RESET,4,sw_reset); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + delayMicroseconds(CCS811_WAIT_AFTER_RESET_US); + PRINTLN(F("ok")); + + // Check status (after reset, CCS811 should be in boot mode with valid app) + PRINT(F("ccs811: status (reset2) ")); + ok= i2cread(CCS811_STATUS,1,&status); + if( !ok ) { + PRINTLN(F("FAILED")); + goto abort_begin; + } + PRINT2(status,HEX); + PRINT(F(" ")); + if( status!=0x10 ) { + PRINTLN(F("ERROR")); + goto abort_begin; + } + PRINTLN(F("ok")); + + // CCS811 back to sleep + wake_down(); + // Return success + return true; + +abort_begin: + // CCS811 back to sleep + wake_down(); + // Return failure + return false; +} + + +// Advanced interface: i2cdelay ======================================================================================== + + +// Delay before a repeated start - needed for e.g. ESP8266 because it doesn't handle I2C clock stretch correctly +void CCS811::set_i2cdelay(int us) { + if( us<0 ) us= 0; + _i2cdelay_us= us; +} + + +// Get current delay +int CCS811::get_i2cdelay(void) { + return _i2cdelay_us; +} + + +// Helper interface: nwake pin ======================================================================================== + + +// _nwake<0 means nWAKE is not connected to a pin of the host, so no action needed +void CCS811::wake_init( void ) { + if( _nwake>=0 ) pinMode(_nwake, OUTPUT); +} + + +void CCS811::wake_up( void) { + if( _nwake>=0 ) { digitalWrite(_nwake, LOW); delayMicroseconds(CCS811_WAIT_AFTER_WAKE_US); } +} + + +void CCS811::wake_down( void) { + if( _nwake>=0 ) digitalWrite(_nwake, HIGH); +} + + +// Helper interface: i2c wrapper ====================================================================================== + + +// Writes `count` from `buf` to register at address `regaddr` in the CCS811. Returns false on I2C problems. +bool CCS811::i2cwrite(int regaddr, int count, const uint8_t * buf) { + Wire.beginTransmission(_slaveaddr); // START, SLAVEADDR + Wire.write(regaddr); // Register address + for( int i=0; i uint16_t + 2018 Nov 02 v5 Maarten Pennings Added clearing of ERROR_ID + 2018 Oct 23 v4 Maarten Pennings Added envdata/i2cdelay + 2018 Oct 21 v3 Maarten Pennings Added hw-version + 2018 Oct 21 v2 Maarten Pennings Simplified I2C, added version mngt + 2017 Dec 11 v1 Maarten Pennings Created +*/ +#ifndef _CCS811_H_ +#define _CCS811_H_ + + +// To help diagnose problems, the begin() and flash() functions print diagnostic messages to Serial. +// If you do not want that, make the PRINT macros in ccs811.cpp empty. + + +#include + + +// Version of this CCS811 driver +#define CCS811_VERSION 10 + + +// I2C slave address for ADDR 0 respectively 1 +#define CCS811_SLAVEADDR_0 0x5A +#define CCS811_SLAVEADDR_1 0x5B + + +// The values for mode in ccs811_start() +#define CCS811_MODE_IDLE 0 +#define CCS811_MODE_1SEC 1 +#define CCS811_MODE_10SEC 2 +#define CCS811_MODE_60SEC 3 + + +// The flags for errstat in ccs811_read() +// ERRSTAT is a merge of two hardware registers: ERROR_ID (bits 15-8) and STATUS (bits 7-0) +// Also bit 1 (which is always 0 in hardware) is set to 1 when an I2C read error occurs +#define CCS811_ERRSTAT_ERROR 0x0001 // There is an error, the ERROR_ID register (0xE0) contains the error source +#define CCS811_ERRSTAT_I2CFAIL 0x0002 // Bit flag added by software: I2C transaction error +#define CCS811_ERRSTAT_DATA_READY 0x0008 // A new data sample is ready in ALG_RESULT_DATA +#define CCS811_ERRSTAT_APP_VALID 0x0010 // Valid application firmware loaded +#define CCS811_ERRSTAT_FW_MODE 0x0080 // Firmware is in application mode (not boot mode) +#define CCS811_ERRSTAT_WRITE_REG_INVALID 0x0100 // The CCS811 received an I²C write request addressed to this station but with invalid register address ID +#define CCS811_ERRSTAT_READ_REG_INVALID 0x0200 // The CCS811 received an I²C read request to a mailbox ID that is invalid +#define CCS811_ERRSTAT_MEASMODE_INVALID 0x0400 // The CCS811 received an I²C request to write an unsupported mode to MEAS_MODE +#define CCS811_ERRSTAT_MAX_RESISTANCE 0x0800 // The sensor resistance measurement has reached or exceeded the maximum range +#define CCS811_ERRSTAT_HEATER_FAULT 0x1000 // The heater current in the CCS811 is not in range +#define CCS811_ERRSTAT_HEATER_SUPPLY 0x2000 // The heater voltage is not being applied correctly + +// These flags should not be set. They flag errors. +#define CCS811_ERRSTAT_HWERRORS ( CCS811_ERRSTAT_ERROR | CCS811_ERRSTAT_WRITE_REG_INVALID | CCS811_ERRSTAT_READ_REG_INVALID | CCS811_ERRSTAT_MEASMODE_INVALID | CCS811_ERRSTAT_MAX_RESISTANCE | CCS811_ERRSTAT_HEATER_FAULT | CCS811_ERRSTAT_HEATER_SUPPLY ) +#define CCS811_ERRSTAT_ERRORS ( CCS811_ERRSTAT_I2CFAIL | CCS811_ERRSTAT_HWERRORS ) +// These flags should normally be set - after a measurement. They flag data available (and valid app running). +#define CCS811_ERRSTAT_OK ( CCS811_ERRSTAT_DATA_READY | CCS811_ERRSTAT_APP_VALID | CCS811_ERRSTAT_FW_MODE ) +// These flags could be set after a measurement. They flag data is not yet available (and valid app running). +#define CCS811_ERRSTAT_OK_NODATA ( CCS811_ERRSTAT_APP_VALID | CCS811_ERRSTAT_FW_MODE ) + + +class CCS811 { + public: // Main interface + CCS811(int nwake=-1, int slaveaddr=CCS811_SLAVEADDR_0); // Pin number connected to nWAKE (nWAKE can also be bound to GND, then pass -1), slave address (5A or 5B) + bool begin( void ); // Reset the CCS811, switch to app mode and check HW_ID. Returns false on problems. + bool start( int mode ); // Switched CCS811 to `mode`, use constants CCS811_MODE_XXX. Returns false on I2C problems. + void read( uint16_t*eco2, uint16_t*etvoc, uint16_t*errstat,uint16_t*raw); // Get measurement results from the CCS811 (all args may be NULL), check status via errstat, e.g. ccs811_errstat(errstat) + const char * errstat_str(uint16_t errstat); // Returns a string version of an errstat. Note, each call, this string is updated. + public: // Extra interface + int hardware_version(void); // Gets version of the CCS811 hardware (returns -1 on I2C failure) + int bootloader_version(void); // Gets version of the CCS811 bootloader (returns -1 on I2C failure) + int application_version(void); // Gets version of the CCS811 application (returns -1 on I2C failure) + int get_errorid(void); // Gets the ERROR_ID [same as 'err' part of 'errstat' in 'read'] (returns -1 on I2C failure) + bool set_envdata(uint16_t t, uint16_t h); // Writes t and h to ENV_DATA (see datasheet for format). Returns false on I2C problems. + bool set_envdata210(uint16_t t, uint16_t h); // Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems. + bool get_baseline(uint16_t *baseline); // Reads (encoded) baseline from BASELINE. Returns false on I2C problems. Get it, just before power down (but only when sensor was on at least 20min) - see CCS811_AN000370 + bool set_baseline(uint16_t baseline); // Writes (encoded) baseline to BASELINE. Returns false on I2C problems. Set it, after power up (and after 20min) + bool flash(const uint8_t * image, int size); // Flashes the firmware of the CCS811 with size bytes from image - image _must_ be in PROGMEM + public: // Advanced interface: i2cdelay + void set_i2cdelay(int us); // Delay before a repeated start - needed for e.g. ESP8266 because it doesn't handle I2C clock stretch correctly + int get_i2cdelay(void); // Get current delay + protected: // Helper interface: nwake pin + void wake_init(void); // Initialize nwake pin + void wake_up(void); // Wake up CCS811, i.e. pull nwake pin low + void wake_down(void); // CCS811 back to sleep, i.e. pull nwake pin high + protected: // Helper interface: i2c wrapper + bool i2cwrite(int regaddr, int count, const uint8_t * buf); // Writes `count` from `buf` to register at address `regaddr` in the CCS811. Returns false on I2C problems. + bool i2cread (int regaddr, int count, uint8_t * buf); // Reads 'count` bytes from register at address `regaddr`, and stores them in `buf`. Returns false on I2C problems. + private: + int _nwake; // Pin number for nWAKE pin (or -1) + int _slaveaddr; // Slave address of the CCS811 + int _i2cdelay_us; // Delay in us just before an I2C repeated start condition + int _appversion; // Version of the app firmware inside the CCS811 (for workarounds) +}; + + +#endif +