Library for Sensirion Environmental Sensor Shield for SGP30 & SHTC1
Dependents: example-sensirion-ublox-c030
sensirion_ess.cpp
00001 /* 00002 * Copyright (c) 2017-2018, Sensirion AG 00003 * All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright notice, this 00009 * list of conditions and the following disclaimer. 00010 * 00011 * * Redistributions in binary form must reproduce the above copyright notice, 00012 * this list of conditions and the following disclaimer in the documentation 00013 * and/or other materials provided with the distribution. 00014 * 00015 * * Neither the name of Sensirion AG nor the names of its 00016 * contributors may be used to endorse or promote products derived from 00017 * this software without specific prior written permission. 00018 * 00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 00023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00024 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00026 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00027 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00028 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00029 */ 00030 00031 /* 00032 * TODO: 00033 * - Baseline store/restore 00034 * - proper init 00035 * - IAQ levels, getIAQLevel() 00036 */ 00037 00038 00039 00040 #include <string.h> 00041 00042 #include "sensirion_ess.h" 00043 DigitalOut led_red(LED_RED); 00044 DigitalOut led_yellow(LED_YEL); 00045 DigitalOut led_green(LED_GRN); 00046 00047 SensirionESS::SensirionESS(void *interface) 00048 { 00049 i2c_connection = (I2C*)(interface); 00050 t = new Timer(); 00051 } 00052 00053 int SensirionESS::initSensors() 00054 { 00055 00056 if (measureRHTInt() != 0) { 00057 setError("Error communicating with SHTC1"); 00058 return -1; 00059 } 00060 00061 if (initSGP() != 0) { 00062 setError("Error communicating with SGPC3"); 00063 return -2; 00064 } 00065 00066 t->start(); 00067 00068 mInitialized = true; 00069 return 0; 00070 } 00071 00072 ////////////////////////////////////////////////////////////////////////////// 00073 // SHT 00074 00075 int SensirionESS::measureRHT() 00076 { 00077 if (!mInitialized) { 00078 setError("ESS not initialized"); 00079 return -1; 00080 } 00081 00082 measureRHTInt(); 00083 return 0; 00084 } 00085 00086 int SensirionESS::measureRHTInt() 00087 { 00088 uint8_t cmd[CMD_LENGTH] = { 0x7C, 0xA2 }; 00089 00090 if (i2c_write(SHT_I2C_ADDR, cmd, CMD_LENGTH)) { 00091 setError("I2C write error"); 00092 return -1; 00093 } 00094 00095 wait_ms(SHT_MEASURE_DELAY); 00096 00097 int ret = i2c_read(SHT_I2C_ADDR, mDataBuf, SHT_DATA_LENGTH); 00098 if (ret == -1) { 00099 setError("I2C read error"); 00100 return -2; 00101 } 00102 00103 // check CRC for both RH and T 00104 if (crc8(mDataBuf+0, 2) != mDataBuf[2] || 00105 crc8(mDataBuf+3, 2) != mDataBuf[5]) { 00106 setError("CRC mismatch"); 00107 return -3; 00108 } 00109 00110 uint16_t val; 00111 val = (mDataBuf[0] << 8) + mDataBuf[1]; 00112 mTemperature = -45 + 175 * (val / 65535.0); 00113 val = (mDataBuf[3] << 8) + mDataBuf[4]; 00114 mHumidity = 100 * (val / 65535.0); 00115 00116 return 0; 00117 } 00118 00119 ////////////////////////////////////////////////////////////////////////////// 00120 // SGP 00121 00122 int SensirionESS::getProductType() const 00123 { 00124 return mProductType; 00125 } 00126 00127 int SensirionESS::getFeatureSetVersion() const 00128 { 00129 return mFeatureSetVersion; 00130 } 00131 00132 int SensirionESS::measureIAQ() 00133 { 00134 if (!mInitialized) { 00135 setError("ESS not initialized"); 00136 return -1; 00137 } 00138 00139 // keep track of timing 00140 mSGPMeasurementTimestamp = t->read_ms(); 00141 00142 uint8_t cmd[CMD_LENGTH] = { 0x20, 0x08 }; 00143 00144 if (i2c_write(SGP_I2C_ADDR, cmd, CMD_LENGTH)) { 00145 setError("error in i2c_write"); 00146 return -1; 00147 } 00148 00149 wait_ms(SGP_MEASURE_DELAY); 00150 00151 int ret = i2c_read(SGP_I2C_ADDR, mDataBuf, SGP_DATA_LENGTH); 00152 if (ret == -1) { 00153 setError("error in i2c_read"); 00154 return -2; 00155 } 00156 00157 if (crc8(mDataBuf, 2) != mDataBuf[2]) { 00158 setError("CRC mismatch"); 00159 return -3; 00160 } 00161 00162 // SGPC3 only has TVOC; SGP30 sends [eco2, tvoc] 00163 if (mProductType == PRODUCT_TYPE_SGP30) { 00164 mECO2 = (mDataBuf[0] << 8) | mDataBuf[1]; 00165 if (crc8(mDataBuf+3, 2) != mDataBuf[5]) { 00166 setError("CRC mismatch"); 00167 return -4; 00168 } 00169 mTVOC = (mDataBuf[3] << 8) | mDataBuf[4]; 00170 } else { 00171 mTVOC = (mDataBuf[0] << 8) | mDataBuf[1];; 00172 } 00173 00174 return 0; 00175 } 00176 00177 int SensirionESS::readFeatureSetInt() 00178 { 00179 uint8_t cmd[CMD_LENGTH] = { 0x20, 0x2f }; 00180 00181 if (i2c_write(SGP_I2C_ADDR, cmd, CMD_LENGTH)) { 00182 setError("error in i2c_write"); 00183 return -1; 00184 } 00185 00186 wait_ms(2); 00187 00188 const uint8_t DATA_LEN = 3; 00189 uint8_t data[DATA_LEN] = { 0 }; 00190 int ret = i2c_read(SGP_I2C_ADDR, data, DATA_LEN); 00191 if (ret == -1) { 00192 setError("I2C read error"); 00193 return -3; 00194 } 00195 00196 // check CRC 00197 if (crc8(data, 2) != data[2]) { 00198 setError("CRC mismatch"); 00199 return -4; 00200 } 00201 00202 // 0 = SGP30, 1 = SGPC3 00203 mProductType = (data[0] & 0xF0) >> 4; 00204 mFeatureSetVersion = data[1] & 0xFF; 00205 00206 return 0; 00207 } 00208 00209 int SensirionESS::initSGP() 00210 { 00211 int ret = readFeatureSetInt(); 00212 // default: SGP30 00213 SGP_INTERMEASURE_DELAY = SGP30_INTERMEASURE_DELAY; 00214 SGP_DATA_LENGTH = SGP30_DATA_LENGTH; 00215 uint8_t cmd[CMD_LENGTH] = { 0x20, 0x03 }; 00216 00217 if (mProductType == PRODUCT_TYPE_SGPC3) { 00218 SGP_INTERMEASURE_DELAY = SGPC3_INTERMEASURE_DELAY; 00219 cmd[1] = 0xae; 00220 } 00221 00222 // run init air quality 00223 if (i2c_write(SGP_I2C_ADDR, cmd, CMD_LENGTH)) { 00224 setError("error in i2c_write"); 00225 return -1; 00226 } 00227 wait_ms(10); 00228 00229 return ret; 00230 } 00231 00232 ////////////////////////////////////////////////////////////////////////////// 00233 // getter for values read earlier 00234 bool SensirionESS::isInitialized() 00235 { 00236 return mInitialized; 00237 } 00238 00239 float SensirionESS::getTemperature() const 00240 { 00241 return mTemperature; 00242 } 00243 00244 float SensirionESS::getHumidity() const 00245 { 00246 return mHumidity; 00247 } 00248 00249 float SensirionESS::getTVOC() const 00250 { 00251 return mTVOC; 00252 } 00253 00254 float SensirionESS::getECO2() const 00255 { 00256 return mECO2; 00257 } 00258 00259 void SensirionESS::setLedRYG(int r, int y, int g) 00260 { 00261 led_red = r; 00262 led_yellow = y; 00263 led_green = g; 00264 } 00265 00266 ////////////////////////////////////////////////////////////////////////////// 00267 // error handling 00268 00269 inline void SensirionESS::setError(const char* error) 00270 { 00271 strlcpy(mErrorBuf, error, ERROR_BUF_LENGTH); 00272 } 00273 00274 const char* SensirionESS::getError() const 00275 { 00276 return mErrorBuf; 00277 } 00278 00279 ////////////////////////////////////////////////////////////////////////////// 00280 // helper 00281 00282 int SensirionESS::remainingWaitTimeMS() 00283 { 00284 unsigned long deltaT = t->read_ms() - mSGPMeasurementTimestamp; 00285 if (deltaT > SGP_INTERMEASURE_DELAY) { 00286 // we're already late, don't wait any longer 00287 return 0; 00288 } 00289 return (SGP_INTERMEASURE_DELAY - deltaT); 00290 } 00291 00292 int8_t SensirionESS::i2c_read(uint8_t address, uint8_t* data, uint16_t count) 00293 { 00294 if (i2c_connection->read(address << 1, (char *) data, count) != 0) 00295 return -1; 00296 return 0; 00297 } 00298 00299 int8_t SensirionESS::i2c_write(uint8_t address, const uint8_t* data, uint16_t count) 00300 { 00301 if (i2c_connection->write(address << 1, (char *) data, count) != 0) 00302 return -1; 00303 return 0; 00304 } 00305 00306 uint8_t SensirionESS::crc8(const uint8_t* data, uint8_t len) 00307 { 00308 // adapted from SHT21 sample code from http://www.sensirion.com/en/products/humidity-temperature/download-center/ 00309 00310 uint8_t crc = 0xff; 00311 uint8_t byteCtr; 00312 for (byteCtr = 0; byteCtr < len; ++byteCtr) { 00313 crc ^= (data[byteCtr]); 00314 for (uint8_t bit = 8; bit > 0; --bit) { 00315 if (crc & 0x80) { 00316 crc = (crc << 1) ^ 0x31; 00317 } else { 00318 crc = (crc << 1); 00319 } 00320 } 00321 } 00322 return crc; 00323 }
Generated on Tue Jul 26 2022 22:33:06 by
1.7.2