123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- //@file Adafruit_SGP30.cpp
- #include "Arduino.h"
- #include "Adafruit_SGP30.h"
- //#define I2C_DEBUG
- /*!
- * @brief Instantiates a new SGP30 class
- */
- Adafruit_SGP30::Adafruit_SGP30() {}
- /*!
- * @brief Setups the hardware and detects a valid SGP30. Initializes I2C
- * then reads the serialnumber and checks that we are talking to an
- * SGP30
- * @param theWire
- * Optional pointer to I2C interface, otherwise use Wire
- * @param initSensor
- * Optional pointer to prevent IAQinit to be called. Used for Deep
- * Sleep.
- * @return True if SGP30 found on I2C, False if something went wrong!
- */
- boolean Adafruit_SGP30::begin(TwoWire *theWire, boolean initSensor) {
- _i2caddr = SGP30_I2CADDR_DEFAULT;
- _i2c = theWire;
- _i2c->begin();
- uint8_t command[2];
- command[0] = 0x36;
- command[1] = 0x82;
- if (!readWordFromCommand(command, 2, 10, serialnumber, 3))
- return false;
- uint16_t featureset;
- command[0] = 0x20;
- command[1] = 0x2F;
- if (!readWordFromCommand(command, 2, 10, &featureset, 1))
- return false;
- // Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
- if ((featureset & 0xF0) != SGP30_FEATURESET)
- return false;
- if (initSensor) {
- if (!IAQinit())
- return false;
- }
- return true;
- }
- /*!
- * @brief Commands the sensor to perform a soft reset using the "General
- * Call" mode. Take note that this is not sensor specific and all devices that
- * support the General Call mode on the on the same I2C bus will perform this.
- *
- * @return True if command completed successfully, false if something went
- * wrong!
- */
- boolean Adafruit_SGP30::softReset(void) {
- uint8_t command[2];
- command[0] = 0x00;
- command[1] = 0x06;
- return readWordFromCommand(command, 2, 10);
- }
- /*!
- * @brief Commands the sensor to begin the IAQ algorithm. Must be called
- * after startup.
- * @returns True if command completed successfully, false if something went
- * wrong!
- */
- boolean Adafruit_SGP30::IAQinit(void) {
- uint8_t command[2];
- command[0] = 0x20;
- command[1] = 0x03;
- return readWordFromCommand(command, 2, 10);
- }
- /*!
- * @brief Commands the sensor to take a single eCO2/VOC measurement. Places
- * results in {@link TVOC} and {@link eCO2}
- * @return True if command completed successfully, false if something went
- * wrong!
- */
- boolean Adafruit_SGP30::IAQmeasure(void) {
- uint8_t command[2];
- command[0] = 0x20;
- command[1] = 0x08;
- uint16_t reply[2];
- if (!readWordFromCommand(command, 2, 12, reply, 2))
- return false;
- TVOC = reply[1];
- eCO2 = reply[0];
- return true;
- }
- /*!
- * @brief Commands the sensor to take a single H2/ethanol raw measurement.
- * Places results in {@link rawH2} and {@link rawEthanol}
- * @returns True if command completed successfully, false if something went
- * wrong!
- */
- boolean Adafruit_SGP30::IAQmeasureRaw(void) {
- uint8_t command[2];
- command[0] = 0x20;
- command[1] = 0x50;
- uint16_t reply[2];
- if (!readWordFromCommand(command, 2, 25, reply, 2))
- return false;
- rawEthanol = reply[1];
- rawH2 = reply[0];
- return true;
- }
- /*!
- * @brief Request baseline calibration values for both CO2 and TVOC IAQ
- * calculations. Places results in parameter memory locaitons.
- * @param eco2_base
- * A pointer to a uint16_t which we will save the calibration
- * value to
- * @param tvoc_base
- * A pointer to a uint16_t which we will save the calibration value to
- * @return True if command completed successfully, false if something went
- * wrong!
- */
- boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base,
- uint16_t *tvoc_base) {
- uint8_t command[2];
- command[0] = 0x20;
- command[1] = 0x15;
- uint16_t reply[2];
- if (!readWordFromCommand(command, 2, 10, reply, 2))
- return false;
- *eco2_base = reply[0];
- *tvoc_base = reply[1];
- return true;
- }
- /*!
- * @brief Assign baseline calibration values for both CO2 and TVOC IAQ
- * calculations.
- * @param eco2_base
- * A uint16_t which we will save the calibration value from
- * @param tvoc_base
- * A uint16_t which we will save the calibration value from
- * @return True if command completed successfully, false if something went
- * wrong!
- */
- boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
- uint8_t command[8];
- command[0] = 0x20;
- command[1] = 0x1e;
- command[2] = tvoc_base >> 8;
- command[3] = tvoc_base & 0xFF;
- command[4] = generateCRC(command + 2, 2);
- command[5] = eco2_base >> 8;
- command[6] = eco2_base & 0xFF;
- command[7] = generateCRC(command + 5, 2);
- return readWordFromCommand(command, 8, 10);
- }
- /*!
- * @brief Set the absolute humidity value [mg/m^3] for compensation to
- * increase precision of TVOC and eCO2.
- * @param absolute_humidity
- * A uint32_t [mg/m^3] which we will be used for compensation.
- * If the absolute humidity is set to zero, humidity compensation
- * will be disabled.
- * @return True if command completed successfully, false if something went
- * wrong!
- */
- boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
- if (absolute_humidity > 256000) {
- return false;
- }
- uint16_t ah_scaled =
- (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
- uint8_t command[5];
- command[0] = 0x20;
- command[1] = 0x61;
- command[2] = ah_scaled >> 8;
- command[3] = ah_scaled & 0xFF;
- command[4] = generateCRC(command + 2, 2);
- return readWordFromCommand(command, 5, 10);
- }
- /*!
- * @brief I2C low level interfacing
- */
- boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[],
- uint8_t commandLength,
- uint16_t delayms,
- uint16_t *readdata,
- uint8_t readlen) {
- _i2c->beginTransmission(_i2caddr);
- #ifdef I2C_DEBUG
- Serial.print("\t\t-> ");
- #endif
- for (uint8_t i = 0; i < commandLength; i++) {
- _i2c->write(command[i]);
- #ifdef I2C_DEBUG
- Serial.print("0x");
- Serial.print(command[i], HEX);
- Serial.print(", ");
- #endif
- }
- #ifdef I2C_DEBUG
- Serial.println();
- #endif
- _i2c->endTransmission();
- delay(delayms);
- if (readlen == 0)
- return true;
- uint8_t replylen = readlen * (SGP30_WORD_LEN + 1);
- if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
- return false;
- uint8_t replybuffer[replylen];
- #ifdef I2C_DEBUG
- Serial.print("\t\t<- ");
- #endif
- for (uint8_t i = 0; i < replylen; i++) {
- replybuffer[i] = _i2c->read();
- #ifdef I2C_DEBUG
- Serial.print("0x");
- Serial.print(replybuffer[i], HEX);
- Serial.print(", ");
- #endif
- }
- #ifdef I2C_DEBUG
- Serial.println();
- #endif
- for (uint8_t i = 0; i < readlen; i++) {
- uint8_t crc = generateCRC(replybuffer + i * 3, 2);
- #ifdef I2C_DEBUG
- Serial.print("\t\tCRC calced: 0x");
- Serial.print(crc, HEX);
- Serial.print(" vs. 0x");
- Serial.println(replybuffer[i * 3 + 2], HEX);
- #endif
- if (crc != replybuffer[i * 3 + 2])
- return false;
- // success! store it
- readdata[i] = replybuffer[i * 3];
- readdata[i] <<= 8;
- readdata[i] |= replybuffer[i * 3 + 1];
- #ifdef I2C_DEBUG
- Serial.print("\t\tRead: 0x");
- Serial.println(readdata[i], HEX);
- #endif
- }
- return true;
- }
- uint8_t Adafruit_SGP30::generateCRC(uint8_t *data, uint8_t datalen) {
- // calculates 8-Bit checksum with given polynomial
- uint8_t crc = SGP30_CRC8_INIT;
- for (uint8_t i = 0; i < datalen; i++) {
- crc ^= data[i];
- for (uint8_t b = 0; b < 8; b++) {
- if (crc & 0x80)
- crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
- else
- crc <<= 1;
- }
- }
- return crc;
- }
|