Library for Sensirion Environmental Sensor Shield for SGP30 & SHTC1
Dependents: example-sensirion-ublox-c030
sensirion_ess.cpp
- Committer:
- Haseeb Khalid
- Date:
- 2018-12-21
- Revision:
- 0:3e97001a43f8
File content as of revision 0:3e97001a43f8:
/* * Copyright (c) 2017-2018, Sensirion AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * 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. * * * Neither the name of Sensirion AG 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 AND CONTRIBUTORS "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 OR CONTRIBUTORS 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. */ /* * TODO: * - Baseline store/restore * - proper init * - IAQ levels, getIAQLevel() */ #include <string.h> #include "sensirion_ess.h" DigitalOut led_red(LED_RED); DigitalOut led_yellow(LED_YEL); DigitalOut led_green(LED_GRN); SensirionESS::SensirionESS(void *interface) { i2c_connection = (I2C*)(interface); t = new Timer(); } int SensirionESS::initSensors() { if (measureRHTInt() != 0) { setError("Error communicating with SHTC1"); return -1; } if (initSGP() != 0) { setError("Error communicating with SGPC3"); return -2; } t->start(); mInitialized = true; return 0; } ////////////////////////////////////////////////////////////////////////////// // SHT int SensirionESS::measureRHT() { if (!mInitialized) { setError("ESS not initialized"); return -1; } measureRHTInt(); return 0; } int SensirionESS::measureRHTInt() { uint8_t cmd[CMD_LENGTH] = { 0x7C, 0xA2 }; if (i2c_write(SHT_I2C_ADDR, cmd, CMD_LENGTH)) { setError("I2C write error"); return -1; } wait_ms(SHT_MEASURE_DELAY); int ret = i2c_read(SHT_I2C_ADDR, mDataBuf, SHT_DATA_LENGTH); if (ret == -1) { setError("I2C read error"); return -2; } // check CRC for both RH and T if (crc8(mDataBuf+0, 2) != mDataBuf[2] || crc8(mDataBuf+3, 2) != mDataBuf[5]) { setError("CRC mismatch"); return -3; } uint16_t val; val = (mDataBuf[0] << 8) + mDataBuf[1]; mTemperature = -45 + 175 * (val / 65535.0); val = (mDataBuf[3] << 8) + mDataBuf[4]; mHumidity = 100 * (val / 65535.0); return 0; } ////////////////////////////////////////////////////////////////////////////// // SGP int SensirionESS::getProductType() const { return mProductType; } int SensirionESS::getFeatureSetVersion() const { return mFeatureSetVersion; } int SensirionESS::measureIAQ() { if (!mInitialized) { setError("ESS not initialized"); return -1; } // keep track of timing mSGPMeasurementTimestamp = t->read_ms(); uint8_t cmd[CMD_LENGTH] = { 0x20, 0x08 }; if (i2c_write(SGP_I2C_ADDR, cmd, CMD_LENGTH)) { setError("error in i2c_write"); return -1; } wait_ms(SGP_MEASURE_DELAY); int ret = i2c_read(SGP_I2C_ADDR, mDataBuf, SGP_DATA_LENGTH); if (ret == -1) { setError("error in i2c_read"); return -2; } if (crc8(mDataBuf, 2) != mDataBuf[2]) { setError("CRC mismatch"); return -3; } // SGPC3 only has TVOC; SGP30 sends [eco2, tvoc] if (mProductType == PRODUCT_TYPE_SGP30) { mECO2 = (mDataBuf[0] << 8) | mDataBuf[1]; if (crc8(mDataBuf+3, 2) != mDataBuf[5]) { setError("CRC mismatch"); return -4; } mTVOC = (mDataBuf[3] << 8) | mDataBuf[4]; } else { mTVOC = (mDataBuf[0] << 8) | mDataBuf[1];; } return 0; } int SensirionESS::readFeatureSetInt() { uint8_t cmd[CMD_LENGTH] = { 0x20, 0x2f }; if (i2c_write(SGP_I2C_ADDR, cmd, CMD_LENGTH)) { setError("error in i2c_write"); return -1; } wait_ms(2); const uint8_t DATA_LEN = 3; uint8_t data[DATA_LEN] = { 0 }; int ret = i2c_read(SGP_I2C_ADDR, data, DATA_LEN); if (ret == -1) { setError("I2C read error"); return -3; } // check CRC if (crc8(data, 2) != data[2]) { setError("CRC mismatch"); return -4; } // 0 = SGP30, 1 = SGPC3 mProductType = (data[0] & 0xF0) >> 4; mFeatureSetVersion = data[1] & 0xFF; return 0; } int SensirionESS::initSGP() { int ret = readFeatureSetInt(); // default: SGP30 SGP_INTERMEASURE_DELAY = SGP30_INTERMEASURE_DELAY; SGP_DATA_LENGTH = SGP30_DATA_LENGTH; uint8_t cmd[CMD_LENGTH] = { 0x20, 0x03 }; if (mProductType == PRODUCT_TYPE_SGPC3) { SGP_INTERMEASURE_DELAY = SGPC3_INTERMEASURE_DELAY; cmd[1] = 0xae; } // run init air quality if (i2c_write(SGP_I2C_ADDR, cmd, CMD_LENGTH)) { setError("error in i2c_write"); return -1; } wait_ms(10); return ret; } ////////////////////////////////////////////////////////////////////////////// // getter for values read earlier bool SensirionESS::isInitialized() { return mInitialized; } float SensirionESS::getTemperature() const { return mTemperature; } float SensirionESS::getHumidity() const { return mHumidity; } float SensirionESS::getTVOC() const { return mTVOC; } float SensirionESS::getECO2() const { return mECO2; } void SensirionESS::setLedRYG(int r, int y, int g) { led_red = r; led_yellow = y; led_green = g; } ////////////////////////////////////////////////////////////////////////////// // error handling inline void SensirionESS::setError(const char* error) { strlcpy(mErrorBuf, error, ERROR_BUF_LENGTH); } const char* SensirionESS::getError() const { return mErrorBuf; } ////////////////////////////////////////////////////////////////////////////// // helper int SensirionESS::remainingWaitTimeMS() { unsigned long deltaT = t->read_ms() - mSGPMeasurementTimestamp; if (deltaT > SGP_INTERMEASURE_DELAY) { // we're already late, don't wait any longer return 0; } return (SGP_INTERMEASURE_DELAY - deltaT); } int8_t SensirionESS::i2c_read(uint8_t address, uint8_t* data, uint16_t count) { if (i2c_connection->read(address << 1, (char *) data, count) != 0) return -1; return 0; } int8_t SensirionESS::i2c_write(uint8_t address, const uint8_t* data, uint16_t count) { if (i2c_connection->write(address << 1, (char *) data, count) != 0) return -1; return 0; } uint8_t SensirionESS::crc8(const uint8_t* data, uint8_t len) { // adapted from SHT21 sample code from http://www.sensirion.com/en/products/humidity-temperature/download-center/ uint8_t crc = 0xff; uint8_t byteCtr; for (byteCtr = 0; byteCtr < len; ++byteCtr) { crc ^= (data[byteCtr]); for (uint8_t bit = 8; bit > 0; --bit) { if (crc & 0x80) { crc = (crc << 1) ^ 0x31; } else { crc = (crc << 1); } } } return crc; }