From a5a798853283cd36f46b081fd1f2ec7627e17f0b Mon Sep 17 00:00:00 2001 From: peteakalad Date: Sat, 23 May 2020 21:47:04 +0100 Subject: [PATCH] Add Adafruit_SGP30_Sensor-1.2.0 --- .../Adafruit_SGP30.cpp | 306 ++++++++++++++++++ .../Adafruit_SGP30.h | 80 +++++ lib/Adafruit_SGP30_Sensor-1.2.0/README.md | 54 ++++ .../examples/sgp30test/sgp30test.ino | 69 ++++ .../library.properties | 10 + lib/Adafruit_SGP30_Sensor-1.2.0/license.txt | 26 ++ lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml | 27 ++ 7 files changed, 572 insertions(+) create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/README.md create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/library.properties create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/license.txt create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp new file mode 100644 index 000000000..936561ede --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp @@ -0,0 +1,306 @@ +/*! + * @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 + * @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; +} diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h new file mode 100644 index 000000000..d50099e9d --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h @@ -0,0 +1,80 @@ +/*! + * @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 + +// 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 initSensor = true); + boolean softReset(); + 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); +}; diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/README.md b/lib/Adafruit_SGP30_Sensor-1.2.0/README.md new file mode 100644 index 000000000..f4d35eeaa --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/README.md @@ -0,0 +1,54 @@ +# Adafruit SGP30 Gas / Air Quality I2C sensor [[![Build Status](https://github.com/adafruit/Adafruit_SGP30/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_SGP30/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_SGP30/html/index.html) + + + +This is the Adafruit SGP30 Gas / Air Quality I2C sensor library + +Tested and works great with the Aadafruit SGP30 Breakout Board + * http://www.adafruit.com/products/3709 + +This chip uses I2C to communicate, 2 pins are required to interface + +Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! + +# Installation +To install, use the Arduino Library Manager and search for "Adafruit SGP30" and install the library. + +## Dependencies + * [Adafruit ILI9341](https://github.com/adafruit/Adafruit_ILI9341) + * [Adafruit GFX Library](https://github.com/adafruit/Adafruit-GFX-Library) + +# Contributing + +Contributions are welcome! Please read our [Code of Conduct](https://github.com/adafruit/Adafruit_SGP30/blob/master/CODE_OF_CONDUCT.md>) +before contributing to help this project stay welcoming. + +## Documentation and doxygen +Documentation is produced by doxygen. Contributions should include documentation for any new code added. + +Some examples of how to use doxygen can be found in these guide pages: + +https://learn.adafruit.com/the-well-automated-arduino-library/doxygen + +https://learn.adafruit.com/the-well-automated-arduino-library/doxygen-tips + +## Formatting and clang-format +This library uses [`clang-format`](https://releases.llvm.org/download.html) to standardize the formatting of `.cpp` and `.h` files. +Contributions should be formatted using `clang-format`: + +The `-i` flag will make the changes to the file. +```bash +clang-format -i *.cpp *.h +``` +If you prefer to make the changes yourself, running `clang-format` without the `-i` flag will print out a formatted version of the file. You can save this to a file and diff it against the original to see the changes. + +Note that the formatting output by `clang-format` is what the automated formatting checker will expect. Any diffs from this formatting will result in a failed build until they are addressed. Using the `-i` flag is highly recommended. + +### clang-format resources + * [Binary builds and source available on the LLVM downloads page](https://releases.llvm.org/download.html) + * [Documentation and IDE integration](https://clang.llvm.org/docs/ClangFormat.html) + +## About this Driver +Written by Limor Fried for Adafruit Industries. +BSD license, check license.txt for more information +All text above must be included in any redistribution diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino b/lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino new file mode 100644 index 000000000..fbfbf49d5 --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino @@ -0,0 +1,69 @@ +#include +#include "Adafruit_SGP30.h" + +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(1000.0f * absoluteHumidity); // [mg/m^3] + return absoluteHumidityScaled; +} + +void setup() { + Serial.begin(9600); + Serial.println("SGP30 test"); + + if (! sgp.begin()){ + Serial.println("Sensor not found :("); + while (1); + } + Serial.print("Found SGP30 serial #"); + Serial.print(sgp.serialnumber[0], HEX); + Serial.print(sgp.serialnumber[1], HEX); + Serial.println(sgp.serialnumber[2], HEX); + + // If you have a baseline measurement from before you can assign it to start, to 'self-calibrate' + //sgp.setIAQBaseline(0x8E68, 0x8F41); // Will vary for each sensor! +} + +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++; + if (counter == 30) { + counter = 0; + + uint16_t TVOC_base, eCO2_base; + if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) { + Serial.println("Failed to get baseline readings"); + return; + } + Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX); + Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX); + } +} diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/library.properties b/lib/Adafruit_SGP30_Sensor-1.2.0/library.properties new file mode 100644 index 000000000..5242ef25b --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/library.properties @@ -0,0 +1,10 @@ +name=Adafruit SGP30 Sensor +version=1.2.0 +author=Adafruit +maintainer=Adafruit +sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor +paragraph=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor +category=Sensors +url=https://github.com/adafruit/Adafruit_SGP30 +architectures=* +depends=Adafruit ILI9341, Adafruit GFX Library diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt b/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt new file mode 100644 index 000000000..f6a0f22b8 --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt @@ -0,0 +1,26 @@ +Software License Agreement (BSD License) + +Copyright (c) 2012, Adafruit Industries +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holders nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml b/lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml new file mode 100644 index 000000000..428f3434e --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml @@ -0,0 +1,27 @@ +language: c +sudo: false + +# Blacklist +branches: + except: + - gh-pages + +env: + global: + - PRETTYNAME="Adafruit SGP30 Arduino Library" +# Optional, will default to "$TRAVIS_BUILD_DIR/Doxyfile" +# - DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile + +before_install: + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh) + +#install: +# - arduino --install-library "Adafruit ILI9341","Adafruit GFX Library" + +script: + - build_main_platforms + +# Generate and deploy documentation +after_success: + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh) + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh) \ No newline at end of file