mirror of https://github.com/arendst/Tasmota.git
281 lines
8.2 KiB
C++
281 lines
8.2 KiB
C++
|
#include "Adafruit_CCS811.h"
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief Setups the I2C interface and hardware and checks for communication.
|
||
|
@param addr Optional I2C address the sensor can be found on. Default is 0x5A
|
||
|
@returns True if device is set up, false on any failure
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
sint8_t Adafruit_CCS811::begin(uint8_t addr)
|
||
|
{
|
||
|
_i2caddr = addr;
|
||
|
|
||
|
_i2c_init();
|
||
|
|
||
|
SWReset();
|
||
|
delay(100);
|
||
|
|
||
|
//check that the HW id is correct
|
||
|
if(this->read8(CCS811_HW_ID) != CCS811_HW_ID_CODE) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
//try to start the app
|
||
|
this->write(CCS811_BOOTLOADER_APP_START, NULL, 0);
|
||
|
delay(100);
|
||
|
|
||
|
//make sure there are no errors and we have entered application mode
|
||
|
if(checkError()) {
|
||
|
return -2;
|
||
|
}
|
||
|
if(!_status.FW_MODE) {
|
||
|
return -3;
|
||
|
}
|
||
|
|
||
|
disableInterrupt();
|
||
|
|
||
|
//default to read every second
|
||
|
setDriveMode(CCS811_DRIVE_MODE_1SEC);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief sample rate of the sensor.
|
||
|
@param mode one of CCS811_DRIVE_MODE_IDLE, CCS811_DRIVE_MODE_1SEC, CCS811_DRIVE_MODE_10SEC, CCS811_DRIVE_MODE_60SEC, CCS811_DRIVE_MODE_250MS.
|
||
|
*/
|
||
|
void Adafruit_CCS811::setDriveMode(uint8_t mode)
|
||
|
{
|
||
|
_meas_mode.DRIVE_MODE = mode;
|
||
|
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief enable the data ready interrupt pin on the device.
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void Adafruit_CCS811::enableInterrupt()
|
||
|
{
|
||
|
_meas_mode.INT_DATARDY = 1;
|
||
|
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief disable the data ready interrupt pin on the device
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void Adafruit_CCS811::disableInterrupt()
|
||
|
{
|
||
|
_meas_mode.INT_DATARDY = 0;
|
||
|
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief checks if data is available to be read.
|
||
|
@returns True if data is ready, false otherwise.
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
bool Adafruit_CCS811::available()
|
||
|
{
|
||
|
_status.set(read8(CCS811_STATUS));
|
||
|
if(!_status.DATA_READY)
|
||
|
return false;
|
||
|
else return true;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief read and store the sensor data. This data can be accessed with getTVOC() and geteCO2()
|
||
|
@returns 0 if no error, error code otherwise.
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
uint8_t Adafruit_CCS811::readData()
|
||
|
{
|
||
|
if(!available())
|
||
|
return false;
|
||
|
else{
|
||
|
uint8_t buf[8];
|
||
|
this->read(CCS811_ALG_RESULT_DATA, buf, 8);
|
||
|
|
||
|
_eCO2 = ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
|
||
|
_TVOC = ((uint16_t)buf[2] << 8) | ((uint16_t)buf[3]);
|
||
|
|
||
|
if(_status.ERROR)
|
||
|
return buf[5];
|
||
|
|
||
|
else return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief set the humidity and temperature compensation for the sensor.
|
||
|
@param humidity the humidity data as a percentage. For 55% humidity, pass in integer 55.
|
||
|
@param temperature the temperature in degrees C as a decimal number. For 25.5 degrees C, pass in 25.5
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void Adafruit_CCS811::setEnvironmentalData(uint8_t humidity, double temperature)
|
||
|
{
|
||
|
/* Humidity is stored as an unsigned 16 bits in 1/512%RH. The
|
||
|
default value is 50% = 0x64, 0x00. As an example 48.5%
|
||
|
humidity would be 0x61, 0x00.*/
|
||
|
|
||
|
/* Temperature is stored as an unsigned 16 bits integer in 1/512
|
||
|
degrees; there is an offset: 0 maps to -25°C. The default value is
|
||
|
25°C = 0x64, 0x00. As an example 23.5% temperature would be
|
||
|
0x61, 0x00.
|
||
|
The internal algorithm uses these values (or default values if
|
||
|
not set by the application) to compensate for changes in
|
||
|
relative humidity and ambient temperature.*/
|
||
|
|
||
|
uint8_t hum_perc = humidity << 1;
|
||
|
|
||
|
float fractional = modf(temperature, &temperature);
|
||
|
uint16_t temp_high = (((uint16_t)temperature + 25) << 9);
|
||
|
uint16_t temp_low = ((uint16_t)(fractional / 0.001953125) & 0x1FF);
|
||
|
|
||
|
uint16_t temp_conv = (temp_high | temp_low);
|
||
|
|
||
|
uint8_t buf[] = {hum_perc, 0x00,
|
||
|
(uint8_t)((temp_conv >> 8) & 0xFF), (uint8_t)(temp_conv & 0xFF)};
|
||
|
|
||
|
this->write(CCS811_ENV_DATA, buf, 4);
|
||
|
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief calculate the temperature using the onboard NTC resistor.
|
||
|
@returns temperature as a double.
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
double Adafruit_CCS811::calculateTemperature()
|
||
|
{
|
||
|
uint8_t buf[4];
|
||
|
this->read(CCS811_NTC, buf, 4);
|
||
|
|
||
|
uint32_t vref = ((uint32_t)buf[0] << 8) | buf[1];
|
||
|
uint32_t vntc = ((uint32_t)buf[2] << 8) | buf[3];
|
||
|
|
||
|
//from ams ccs811 app note
|
||
|
uint32_t rntc = vntc * CCS811_REF_RESISTOR / vref;
|
||
|
|
||
|
double ntc_temp;
|
||
|
ntc_temp = log((double)rntc / CCS811_REF_RESISTOR); // 1
|
||
|
ntc_temp /= 3380; // 2
|
||
|
ntc_temp += 1.0 / (25 + 273.15); // 3
|
||
|
ntc_temp = 1.0 / ntc_temp; // 4
|
||
|
ntc_temp -= 273.15; // 5
|
||
|
return ntc_temp - _tempOffset;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief set interrupt thresholds
|
||
|
@param low_med the level below which an interrupt will be triggered.
|
||
|
@param med_high the level above which the interrupt will ge triggered.
|
||
|
@param hysteresis optional histeresis level. Defaults to 50
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void Adafruit_CCS811::setThresholds(uint16_t low_med, uint16_t med_high, uint8_t hysteresis)
|
||
|
{
|
||
|
uint8_t buf[] = {(uint8_t)((low_med >> 8) & 0xF), (uint8_t)(low_med & 0xF),
|
||
|
(uint8_t)((med_high >> 8) & 0xF), (uint8_t)(med_high & 0xF), hysteresis};
|
||
|
|
||
|
this->write(CCS811_THRESHOLDS, buf, 5);
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief trigger a software reset of the device
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void Adafruit_CCS811::SWReset()
|
||
|
{
|
||
|
//reset sequence from the datasheet
|
||
|
uint8_t seq[] = {0x11, 0xE5, 0x72, 0x8A};
|
||
|
this->write(CCS811_SW_RESET, seq, 4);
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief read the status register and store any errors.
|
||
|
@returns the error bits from the status register of the device.
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
bool Adafruit_CCS811::checkError()
|
||
|
{
|
||
|
_status.set(read8(CCS811_STATUS));
|
||
|
return _status.ERROR;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief write one byte of data to the specified register
|
||
|
@param reg the register to write to
|
||
|
@param value the value to write
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
void Adafruit_CCS811::write8(byte reg, byte value)
|
||
|
{
|
||
|
this->write(reg, &value, 1);
|
||
|
}
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/*!
|
||
|
@brief read one byte of data from the specified register
|
||
|
@param reg the register to read
|
||
|
@returns one byte of register data
|
||
|
*/
|
||
|
/**************************************************************************/
|
||
|
uint8_t Adafruit_CCS811::read8(byte reg)
|
||
|
{
|
||
|
uint8_t ret;
|
||
|
this->read(reg, &ret, 1);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void Adafruit_CCS811::_i2c_init()
|
||
|
{
|
||
|
Wire.begin();
|
||
|
#ifdef ESP8266
|
||
|
Wire.setClockStretchLimit(1000);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void Adafruit_CCS811::read(uint8_t reg, uint8_t *buf, uint8_t num)
|
||
|
{
|
||
|
uint8_t value;
|
||
|
uint8_t pos = 0;
|
||
|
|
||
|
//on arduino we need to read in 32 byte chunks
|
||
|
while(pos < num){
|
||
|
|
||
|
uint8_t read_now = min((uint8_t)32, (uint8_t)(num - pos));
|
||
|
Wire.beginTransmission((uint8_t)_i2caddr);
|
||
|
Wire.write((uint8_t)reg + pos);
|
||
|
Wire.endTransmission();
|
||
|
Wire.requestFrom((uint8_t)_i2caddr, read_now);
|
||
|
|
||
|
for(int i=0; i<read_now; i++){
|
||
|
buf[pos] = Wire.read();
|
||
|
pos++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Adafruit_CCS811::write(uint8_t reg, uint8_t *buf, uint8_t num)
|
||
|
{
|
||
|
Wire.beginTransmission((uint8_t)_i2caddr);
|
||
|
Wire.write((uint8_t)reg);
|
||
|
Wire.write((uint8_t *)buf, num);
|
||
|
Wire.endTransmission();
|
||
|
}
|