Загрузка перед обнорвлением библиотеки на https://github.com/maarten-pennings
parent
5f19f1ba2f
commit
08fa5d9b1b
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#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( t<lo ) ok= set_envdata(0,h);
|
||||
else if( t>hi ) 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<count; i++) Wire.write(buf[i]); // Write bytes
|
||||
int r= Wire.endTransmission(true); // STOP
|
||||
return r==0;
|
||||
}
|
||||
|
||||
// Reads 'count` bytes from register at address `regaddr`, and stores them in `buf`. Returns false on I2C problems.
|
||||
bool CCS811::i2cread(int regaddr, int count, uint8_t * buf) {
|
||||
Wire.beginTransmission(_slaveaddr); // START, SLAVEADDR
|
||||
Wire.write(regaddr); // Register address
|
||||
int wres= Wire.endTransmission(false); // Repeated START
|
||||
delayMicroseconds(_i2cdelay_us); // Wait
|
||||
int rres=Wire.requestFrom(_slaveaddr,count); // From CCS811, read bytes, STOP
|
||||
for( int i=0; i<count; i++ ) buf[i]=Wire.read();
|
||||
return (wres==0) && (rres==count);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
ccs811.h - 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 Flash&i2cwrite now use const array
|
||||
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
|
||||
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 <stdint.h>
|
||||
|
||||
|
||||
// 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
|
||||
|
Loading…
Reference in New Issue