hg
Dependents: Example_DS3231_test
Diff: Adafruit_SGP30.cpp
- Revision:
- 0:41a622cdd86d
- Child:
- 1:fedfd309fb3c
diff -r 000000000000 -r 41a622cdd86d Adafruit_SGP30.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adafruit_SGP30.cpp Mon Jul 23 09:51:43 2018 +0000 @@ -0,0 +1,258 @@ +/*! + * @file Adafruit_SGP30.cpp + * + * @mainpage Adafruit SGP30 gas sensor driver + * + * @section intro_sec Introduction + * + * This is the documentation for Adafruit's SGP30 driver for the + * Arduino platform. It is designed specifically to work with the + * Adafruit SGP30 breakout: http://www.adafruit.com/products/3709 + * + * These sensors use I2C to communicate, 2 pins (SCL+SDA) are required + * to interface with the breakout. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * + * @section author Author + * Written by Ladyada for Adafruit Industries. + * + * @section license License + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "mbed.h" +#include "Adafruit_SGP30.h" + +//#define I2C_DEBUG + +/**************************************************************************/ +/*! + @brief Instantiates a new SGP30 class +*/ +/**************************************************************************/ +Adafruit_SGP30::Adafruit_SGP30(PinName sda, PinName scl) : _i2c(sda, scl) { +} + +/**************************************************************************/ +/*! + @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 + @returns True if SGP30 found on I2C, False if something went wrong! +*/ +/**************************************************************************/ +bool Adafruit_SGP30::begin() { + //Mbed use 8bit addr + _i2caddr = SGP30_I2CADDR_DEFAULT << 1; + + uint8_t command[3]; + command[0] = 0x36; + command[1] = 0x82; + command[2] = 0x0; + if (! readWordFromCommand(command, 2, 5, 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 != SGP30_FEATURESET) + return false; + if (! IAQinit()) + return false; + + return true; +} + +/**************************************************************************/ +/*! + @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! +*/ +/**************************************************************************/ +bool 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} + @returns True if command completed successfully, false if something went wrong! +*/ +/**************************************************************************/ +bool 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 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 + @returns True if command completed successfully, false if something went wrong! +*/ +/**************************************************************************/ +bool 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; +} + +bool Adafruit_SGP30::getIAQRaw(uint16_t *H2_raw, uint16_t *Eth_raw) { + uint8_t command[2]; + command[0] = 0x20; + command[1] = 0x50; + uint16_t reply[2]; + if (! readWordFromCommand(command, 2, 40, reply, 2)) + return false; + *H2_raw = reply[0]; + *Eth_raw = 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 + @returns True if command completed successfully, false if something went wrong! +*/ +/**************************************************************************/ +bool 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 I2C low level interfacing +*/ +/**************************************************************************/ + + +bool Adafruit_SGP30::readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delayms, uint16_t *readdata, uint8_t readlen) +{ + uint8_t retval; + + +#ifdef I2C_DEBUG + printf("\t\t-> "); +#endif + + retval = _i2c.write((int)_i2caddr, (const char*)command, (int)commandLength); + //0=OK, 1=Fail https://os.mbed.com/docs/v5.9/reference/i2c.html + if ( retval != 0) + return false; + +#ifdef I2C_DEBUG + for (uint8_t i=0; i<commandLength; i++) { + printf("0x"); + printf("%x", command[i]); + printf(", "); + } +#endif + +#ifdef I2C_DEBUG + printf("\n"); +#endif + + wait_ms(delayms); + + + + if (readlen == 0) + return true; + + uint8_t replylen = readlen * (SGP30_WORD_LEN +1); + uint8_t replybuffer[replylen]; + + retval = _i2c.read((int)_i2caddr, (char*)replybuffer, (int)replylen); + + //0=OK, 1=Fail https://os.mbed.com/docs/v5.9/reference/i2c.html + if ( retval != 0) + return false; + +#ifdef I2C_DEBUG + printf("\t\t<- "); + for (uint8_t i=0; i<replylen; i++) { + printf("0x"); + printf("%x", replybuffer[i]); + printf(", "); + } +#endif + + +#ifdef I2C_DEBUG + printf("\n"); +#endif + + for (uint8_t i=0; i<readlen; i++) { + uint8_t crc = generateCRC(replybuffer+i*3, 2); +#ifdef I2C_DEBUG + printf("\t\tCRC calced: 0x"); + printf("%x", crc); + printf(" vs. 0x"); + printf("%x\n" ,replybuffer[i * 3 + 2]; +#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 + printf("\t\tRead: 0x"); + printf("%x\n", readdata[i]); +#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; +}