Update Adafruit library for SGP30 sensor

This commit is contained in:
Gijs Noorlander
2020-02-22 15:56:26 +01:00
parent c3d2d013d3
commit 5160703d58
14 changed files with 405 additions and 319 deletions
-4
View File
@@ -1,4 +0,0 @@
*~
Doxyfile*
doxygen_sqlite3.db
html
@@ -1,243 +0,0 @@
/*!
* @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.
*
*/
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#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
@returns True if SGP30 found on I2C, False if something went wrong!
*/
/**************************************************************************/
boolean Adafruit_SGP30::begin(TwoWire *theWire) {
_i2caddr = SGP30_I2CADDR_DEFAULT;
if (theWire == NULL) {
_i2c = &Wire;
} else {
_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 != 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!
*/
/**************************************************************************/
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}
@returns 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 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!
*/
/**************************************************************************/
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
@returns 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 I2C low level interfacing
*/
/**************************************************************************/
boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delayms, uint16_t *readdata, uint8_t readlen)
{
//uint8_t data;
_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;
}
@@ -1,68 +0,0 @@
/*!
* @file Adafruit_SGP30.h
*
* 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!
*
* Written by Ladyada for Adafruit Industries.
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Arduino.h"
#include <Wire.h>
// the i2c address
#define SGP30_I2CADDR_DEFAULT 0x58 ///< SGP30 has only one I2C address
// commands and constants
#define SGP30_FEATURESET 0x0020 ///< The required set for this library
#define SGP30_CRC8_POLYNOMIAL 0x31 ///< Seed for SGP30's CRC polynomial
#define SGP30_CRC8_INIT 0xFF ///< Init value for CRC
#define SGP30_WORD_LEN 2 ///< 2 bytes per word
/**************************************************************************/
/*! Class that stores state and functions for interacting with SGP30 Gas Sensor */
/**************************************************************************/
class Adafruit_SGP30 {
public:
Adafruit_SGP30();
boolean begin(TwoWire *theWire = NULL);
boolean IAQinit(void);
boolean IAQmeasure(void);
boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
/**
* The last measurement of the IAQ-calculated Total Volatile Organic Compounds in ppb. This value is set when you call {@link IAQmeasure()}
*/
uint16_t TVOC;
/**
* The last measurement of the IAQ-calculated equivalent CO2 in ppm. This value is set when you call {@link IAQmeasure()}
*/
uint16_t eCO2;
/**
* The 48-bit serial number, this value is set when you call {@link begin()}
*/
uint16_t serialnumber[3];
private:
TwoWire *_i2c;
uint8_t _i2caddr;
void write(uint8_t address, uint8_t *data, uint8_t n);
void read(uint8_t address, uint8_t *data, uint8_t n);
boolean readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delay, uint16_t *readdata = NULL, uint8_t readlen = 0);
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
};
+11
View File
@@ -0,0 +1,11 @@
*~
Doxyfile*
doxygen_sqlite3.db
html# osx
.DS_Store
# doxygen
Doxyfile*
doxygen_sqlite3.db
html
*.tmp
+285
View File
@@ -0,0 +1,285 @@
/*!
* @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 "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
* @return True if SGP30 found on I2C, False if something went wrong!
*/
boolean Adafruit_SGP30::begin(TwoWire *theWire) {
_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 (!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!
*/
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;
}
+79
View File
@@ -0,0 +1,79 @@
/*!
* @file Adafruit_SGP30.h
*
* 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!
*
* Written by Ladyada for Adafruit Industries.
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Arduino.h"
#include <Wire.h>
// the i2c address
#define SGP30_I2CADDR_DEFAULT 0x58 ///< SGP30 has only one I2C address
// commands and constants
#define SGP30_FEATURESET 0x0020 ///< The required set for this library
#define SGP30_CRC8_POLYNOMIAL 0x31 ///< Seed for SGP30's CRC polynomial
#define SGP30_CRC8_INIT 0xFF ///< Init value for CRC
#define SGP30_WORD_LEN 2 ///< 2 bytes per word
/*!
* @brief Class that stores state and functions for interacting with
* SGP30 Gas Sensor
*/
class Adafruit_SGP30 {
public:
Adafruit_SGP30();
boolean begin(TwoWire *theWire = &Wire);
boolean IAQinit();
boolean IAQmeasure();
boolean IAQmeasureRaw();
boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
boolean setHumidity(uint32_t absolute_humidity);
/** The last measurement of the IAQ-calculated Total Volatile Organic
* Compounds in ppb. This value is set when you call {@link IAQmeasure()} **/
uint16_t TVOC;
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
* value is set when you call {@link IAQmeasure()} **/
uint16_t eCO2;
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
* value is set when you call {@link IAQmeasureRaw()} **/
uint16_t rawH2;
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
* value is set when you call {@link IAQmeasureRaw()} **/
uint16_t rawEthanol;
/** The 48-bit serial number, this value is set when you call {@link begin()}
* **/
uint16_t serialnumber[3];
private:
TwoWire *_i2c;
uint8_t _i2caddr;
void write(uint8_t address, uint8_t *data, uint8_t n);
void read(uint8_t address, uint8_t *data, uint8_t n);
boolean readWordFromCommand(uint8_t command[], uint8_t commandLength,
uint16_t delay, uint16_t *readdata = NULL,
uint8_t readlen = 0);
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
};
@@ -1,5 +1,6 @@
Adafruit_SGP30
================
# Adafruit SGP30 Gas / Air Quality I2C sensor [![Build Status](https://travis-ci.com/adafruit/Adafruit_SGP30.svg?branch=master)](https://travis-ci.com/adafruit/Adafruit_SGP30)
<a href="https://www.adafruit.com/product/3709"><img src="assets/board.jpg?raw=true" width="500px"></a>
This is the Adafruit SGP30 Gas / Air Quality I2C sensor library
@@ -16,3 +17,4 @@ Written by Limor Fried for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution
To install, use the Arduino Library Manager and search for "Adafruit SGP30 Sensor" and install the library.
Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

@@ -3,6 +3,17 @@
Adafruit_SGP30 sgp;
/* return absolute humidity [mg/m^3] with approximation formula
* @param temperature [°C]
* @param humidity [%RH]
*/
uint32_t getAbsoluteHumidity(float temperature, float humidity) {
// approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity); // [mg/m^3]
return absoluteHumidityScaled;
}
void setup() {
Serial.begin(9600);
Serial.println("SGP30 test");
@@ -22,12 +33,25 @@ void setup() {
int counter = 0;
void loop() {
// If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals
//float temperature = 22.1; // [°C]
//float humidity = 45.2; // [%RH]
//sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));
if (! sgp.IAQmeasure()) {
Serial.println("Measurement failed");
return;
}
Serial.print("TVOC "); Serial.print(sgp.TVOC); Serial.print(" ppb\t");
Serial.print("eCO2 "); Serial.print(sgp.eCO2); Serial.println(" ppm");
if (! sgp.IAQmeasureRaw()) {
Serial.println("Raw Measurement failed");
return;
}
Serial.print("Raw H2 "); Serial.print(sgp.rawH2); Serial.print(" \t");
Serial.print("Raw Ethanol "); Serial.print(sgp.rawEthanol); Serial.println("");
delay(1000);
counter++;
@@ -42,4 +66,4 @@ void loop() {
Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX);
Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX);
}
}
}
@@ -1,5 +1,5 @@
name=Adafruit SGP30 Sensor
version=1.0.2
version=1.0.5
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor