├── arduino-sht.h ├── library.properties ├── examples ├── sht3xanalog │ └── sht3xanalog.ino ├── sht-autodetect │ └── sht-autodetect.ino └── multiple-sht-sensors │ └── multiple-sht-sensors.ino ├── keywords.txt ├── LICENSE ├── README.md ├── SHTSensor.h └── SHTSensor.cpp /arduino-sht.h: -------------------------------------------------------------------------------- 1 | #ifndef ARDUINO_SHT_H 2 | #define ARDUINO_SHT_H 3 | 4 | #include "SHTSensor.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=arduino-sht 2 | version=1.2.6 3 | author=Johannes Winkelmann, Andreas Brauchli 4 | maintainer=Johannes Winkelmann 5 | sentence=Support for Sensirion's humidity and temperature sensors. 6 | paragraph=Supported sensors: SHTC1, SHTC3, SHTW1, SHTW2, SHT3x-DIS (I2C), SHT2x, SHT85, SHT3x-ARP, SHT4x 7 | category=Sensors 8 | url=https://developer.sensirion.com 9 | architectures=* 10 | -------------------------------------------------------------------------------- /examples/sht3xanalog/sht3xanalog.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "SHTSensor.h" 5 | 6 | SHT3xAnalogSensor sht3xAnalog(A0, A1); 7 | 8 | void setup() { 9 | // put your setup code here, to run once: 10 | 11 | Wire.begin(); 12 | Serial.begin(9600); 13 | 14 | delay(1000); // let serial console settle 15 | } 16 | 17 | void loop() { 18 | Serial.print("SHT3x Analog:\n"); 19 | Serial.print(" RH: "); 20 | Serial.print(sht3xAnalog.readHumidity(), 2); 21 | Serial.print("\n"); 22 | Serial.print(" T: "); 23 | Serial.print(sht3xAnalog.readTemperature(), 2); 24 | Serial.print("\n"); 25 | 26 | delay(1000); 27 | } 28 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For arduino-sht 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | SHTSensorType KEYWORD1 10 | SHTAccuracy KEYWORD1 11 | SHTSensor KEYWORD1 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | 17 | init KEYWORD2 18 | readSample KEYWORD2 19 | getHumidity KEYWORD2 20 | getTemperature KEYWORD2 21 | setAccuracy KEYWORD2 22 | 23 | ####################################### 24 | # Instances (KEYWORD2) 25 | ####################################### 26 | 27 | ####################################### 28 | # Constants (LITERAL1) 29 | ####################################### 30 | -------------------------------------------------------------------------------- /examples/sht-autodetect/sht-autodetect.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "SHTSensor.h" 4 | 5 | SHTSensor sht; 6 | // To use a specific sensor instead of probing the bus use this command: 7 | // SHTSensor sht(SHTSensor::SHT3X); 8 | 9 | void setup() { 10 | // put your setup code here, to run once: 11 | 12 | Wire.begin(); 13 | Serial.begin(9600); 14 | delay(1000); // let serial console settle 15 | 16 | if (sht.init()) { 17 | Serial.print("init(): success\n"); 18 | } else { 19 | Serial.print("init(): failed\n"); 20 | } 21 | sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x 22 | 23 | } 24 | 25 | void loop() { 26 | // put your main code here, to run repeatedly: 27 | 28 | if (sht.readSample()) { 29 | Serial.print("SHT:\n"); 30 | Serial.print(" RH: "); 31 | Serial.print(sht.getHumidity(), 2); 32 | Serial.print("\n"); 33 | Serial.print(" T: "); 34 | Serial.print(sht.getTemperature(), 2); 35 | Serial.print("\n"); 36 | } else { 37 | Serial.print("Error in readSample()\n"); 38 | } 39 | 40 | delay(1000); 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Sensirion AG 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Sensirion AG nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /examples/multiple-sht-sensors/multiple-sht-sensors.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "SHTSensor.h" 3 | 4 | // Note that all i2c devices sharing one bus must have distinct addresses. Thus 5 | // this example only works with sensors that have distinct i2c addresses (e.g. 6 | // SHT3x and SHT3x_alt, or SHT3x and SHTC3). 7 | // Make sure not to use auto-detection as it will only pick up one sensor. 8 | 9 | // Sensor with normal i2c address 10 | // Sensor 1 with address pin pulled to GND 11 | SHTSensor sht1(SHTSensor::SHT3X); 12 | 13 | // Sensor with alternative i2c address 14 | // Sensor 2 with address pin pulled to Vdd 15 | SHTSensor sht2(SHTSensor::SHT3X_ALT); 16 | 17 | void setup() { 18 | // put your setup code here, to run once: 19 | Wire.begin(); 20 | Serial.begin(9600); 21 | delay(1000); // let serial console settle 22 | 23 | // init on a specific sensor type (i.e. without auto detecting), 24 | // does not check if the sensor is responding and will thus always succeed. 25 | 26 | // initialize sensor with normal i2c-address 27 | sht1.init(); 28 | 29 | // initialize sensor with alternative i2c-address 30 | sht2.init(); 31 | } 32 | 33 | void loop() { 34 | // put your main code here, to run repeatedly: 35 | // read from first sensor 36 | if (sht1.readSample()) { 37 | Serial.print("SHT1 :\n"); 38 | Serial.print(" RH: "); 39 | Serial.print(sht1.getHumidity(), 2); 40 | Serial.print("\n"); 41 | Serial.print(" T: "); 42 | Serial.print(sht1.getTemperature(), 2); 43 | Serial.print("\n"); 44 | } else { 45 | Serial.print("Sensor 1: Error in readSample()\n"); 46 | } 47 | 48 | // read from second sensor 49 | if (sht2.readSample()) { 50 | Serial.print("SHT2:\n"); 51 | Serial.print(" RH: "); 52 | Serial.print(sht2.getHumidity(), 2); 53 | Serial.print("\n"); 54 | Serial.print(" T: "); 55 | Serial.print(sht2.getTemperature(), 2); 56 | Serial.print("\n"); 57 | } else { 58 | Serial.print("Sensor 2: Error in readSample()\n"); 59 | } 60 | 61 | delay(1000); 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arduino-sht 2 | Repository for Sensirion humidity and temperature sensor support on Arduino 3 | 4 | ## Supported sensors: 5 | - SHTC1 6 | - SHTC3 7 | - SHTW1 8 | - SHTW2 9 | - SHT2x (SHT20, SHT21, SHT25) 10 | - SHT3x-DIS (I2C) 11 | - SHT3x-ARP (ratiometric analog voltage output) 12 | - SHT85 13 | - SHT4x 14 | 15 | For sht3x and sht4x there are specific drivers available in separate repositories. 16 | 17 | ## Installation 18 | 19 | The recommended way to install ```arduino-sht``` is through the Library 20 | Manager of the Arduino IDE. To access it, go to the ```Tools``` menu and 21 | select ```Manage Libraries...```, and search for the library name there. 22 | 23 | If you prefer to install it manually, you can download either via git or from 24 | the releases page and place it in your Arduino/libraries directory. After 25 | restarting the Arduino IDE, you will see the new SHTSensor menu items under 26 | libraries and examples. 27 | 28 | ## Integrating it into your sketch 29 | 30 | Assuming you installed the library as described above, the following steps are 31 | necessary: 32 | 33 | 1. Import the Wire library like this: From the menu bar, select Sketch > Import 34 | Library > Wire 35 | 1. Import the arduino-sht library: From the menu bar, select Sketch > 36 | Import Library > arduino-sht 37 | 1. Create an instance of the `SHTSensor` class (`SHTSensor sht;`) 38 | 2. In `setup()`, make sure to init the Wire library with `Wire.begin()` 39 | 3. Also in `setup()`, call `sht.init()` 40 | 5. If you want to use the serial console, remember to initialize the Serial 41 | library with `Serial.begin(9600)` 42 | 1. Call `sht.readSample()` in the `loop()` function, which reads a temperature 43 | and humidity sample from the sensor 44 | 2. Use `sht.getHumidity()` and `sht.getTemperature()` to get the values from 45 | the last sample 46 | 47 | *Important:* `getHumidity()` and `getTemperature()` do *not* read a new sample 48 | from the sensor, but return the values read last. To read a new sample, make 49 | sure to call `readSample()` 50 | 51 | ### Using an custom or alternative I2C port/Wire instance 52 | 53 | Some Arduino boards have multiple predefined I2C ports; generally, the second port will be called `Wire1`. 54 | 55 | The `arduino-sht` library allows to use an alternative interface; to do so, pass the port you want to use as an argument to `sht.init()`, like this: 56 | ``` 57 | if (sht.init(Wire1)) { 58 | Serial.print("init(): success\n"); 59 | } else { 60 | Serial.print("init(): failed\n"); 61 | } 62 | ``` 63 | 64 | ## Example projects 65 | 66 | See example project 67 | [sht-autodetect](examples/sht-autodetect/sht-autodetect.ino) 68 | 69 | ### Usage with multiple SHT31 sensors 70 | 71 | See example project 72 | [multiple-sht-sensors](examples/multiple-sht-sensors/multiple-sht-sensors.ino) 73 | -------------------------------------------------------------------------------- /SHTSensor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * Copyright (c) 2015-2016, Johannes Winkelmann 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Sensirion AG nor the names of its 14 | * contributors may be used to endorse or promote products derived 15 | * from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef SHTSENSOR_H 30 | #define SHTSENSOR_H 31 | 32 | #include 33 | #include 34 | 35 | //#define DEBUG_SHT_SENSOR 36 | 37 | #ifdef DEBUG_SHT_SENSOR 38 | #ifdef DEBUG_ESP_PORT 39 | #define DEBUG_SHT(f) do { DEBUG_ESP_PORT.print(PSTR(f)); } while (0) 40 | #else 41 | #define DEBUG_SHT(f) do { Serial.print(PSTR(f)); } while (0) 42 | #endif 43 | #else 44 | #define DEBUG_SHT(x...) do { (void)0; } while (0) 45 | #endif 46 | 47 | // Forward declaration 48 | class SHTSensorDriver; 49 | 50 | /** 51 | * Official interface for Sensirion SHT Sensors 52 | */ 53 | class SHTSensor 54 | { 55 | public: 56 | /** 57 | * Enum of the supported Digital Sensirion SHT Sensors. 58 | * For analog sensors, see SHT3xAnalogSensor. 59 | * Using the special AUTO_DETECT sensor causes all i2c sensors to be 60 | * probed. The first matching sensor will then be used. 61 | */ 62 | enum SHTSensorType { 63 | /** Automatically detect the sensor type (only i2c sensors listed above) */ 64 | AUTO_DETECT, 65 | // i2c Sensors: 66 | /** SHT3x-DIS with ADDR (sensor pin 2) connected to VSS (default) */ 67 | SHT3X, 68 | SHT85, 69 | /** SHT3x-DIS with ADDR (sensor pin 2) connected to VDD */ 70 | SHT3X_ALT, 71 | SHTC1, 72 | SHTC3, 73 | SHTW1, 74 | SHTW2, 75 | SHT4X, 76 | SHT2X 77 | }; 78 | 79 | /** 80 | * Accuracy setting of measurement. 81 | * Not all sensors support changing the sampling accuracy. 82 | */ 83 | enum SHTAccuracy { 84 | /** Highest repeatability at the cost of slower measurement */ 85 | SHT_ACCURACY_HIGH, 86 | /** Balanced repeatability and speed of measurement */ 87 | SHT_ACCURACY_MEDIUM, 88 | /** Fastest measurement but lowest repeatability */ 89 | SHT_ACCURACY_LOW 90 | }; 91 | 92 | /** Value reported by getHumidity() when the sensor is not initialized */ 93 | static const float HUMIDITY_INVALID; 94 | /** Value reported by getTemperature() when the sensor is not initialized */ 95 | static const float TEMPERATURE_INVALID; 96 | /** 97 | * Auto-detectable sensor types. 98 | * Note that the SHTC3, SHTW1 and SHTW2 share exactly the same driver as the SHTC1 99 | * and are thus not listed individually. 100 | */ 101 | static const SHTSensorType AUTO_DETECT_SENSORS[]; 102 | 103 | /** 104 | * Instantiate a new SHTSensor 105 | * By default, the i2c bus is queried for known SHT Sensors. To address 106 | * a specific sensor, set the `sensorType'. 107 | */ 108 | SHTSensor(SHTSensorType sensorType = AUTO_DETECT) 109 | : mSensorType(sensorType), 110 | mSensor(NULL), 111 | mTemperature(SHTSensor::TEMPERATURE_INVALID), 112 | mHumidity(SHTSensor::HUMIDITY_INVALID) 113 | { 114 | } 115 | 116 | virtual ~SHTSensor() { 117 | cleanup(); 118 | } 119 | 120 | /** 121 | * Initialize the sensor driver, and probe for the sensor on the bus 122 | * 123 | * If SHTSensor() was created with an empty constructor or with 'sensorType' 124 | * AUTO_DETECT, init() will also try to automatically detect a sensor. 125 | * Auto detection will stop as soon as the first sensor was found; if you have 126 | * multiple sensor types on the bus, use the 'sensorType' argument of the 127 | * constructor to control which sensor type will be instantiated. 128 | * 129 | * To read out the sensor use readSample(), followed by getTemperature() and 130 | * getHumidity() to retrieve the values from the sample 131 | * 132 | * Returns true if communication with a sensor on the bus was successful, false otherwise 133 | */ 134 | bool init(TwoWire & wire = Wire); 135 | 136 | /** 137 | * Read new values from the sensor 138 | * After the call, use getTemperature() and getHumidity() to retrieve the 139 | * values 140 | * Returns true if the sample was read and the values are cached 141 | */ 142 | bool readSample(); 143 | 144 | /** 145 | * Get the relative humidity in percent read from the last sample 146 | * Use readSample() to trigger a new sensor reading 147 | */ 148 | float getHumidity() const { 149 | return mHumidity; 150 | } 151 | 152 | /** 153 | * Get the temperature in Celsius read from the last sample 154 | * Use readSample() to trigger a new sensor reading 155 | */ 156 | float getTemperature() const { 157 | return mTemperature; 158 | } 159 | 160 | /** 161 | * Change the sensor accurancy, if supported by the sensor 162 | * Returns true if the accuracy was changed 163 | */ 164 | bool setAccuracy(SHTAccuracy newAccuracy); 165 | 166 | SHTSensorType mSensorType; 167 | 168 | private: 169 | void cleanup(); 170 | 171 | 172 | SHTSensorDriver *mSensor; 173 | float mTemperature; 174 | float mHumidity; 175 | }; 176 | 177 | 178 | /** Abstract class for a digital SHT Sensor driver */ 179 | class SHTSensorDriver 180 | { 181 | public: 182 | virtual ~SHTSensorDriver() = 0; 183 | 184 | /** 185 | * Set the sensor accuracy. 186 | * Returns false if the sensor does not support changing the accuracy 187 | */ 188 | virtual bool setAccuracy(SHTSensor::SHTAccuracy /* newAccuracy */) { 189 | return false; 190 | } 191 | 192 | /** Returns true if the next sample was read and the values are cached */ 193 | virtual bool readSample(); 194 | 195 | /** 196 | * Get the relative humidity in percent read from the last sample 197 | * Use readSample() to trigger a new sensor reading 198 | */ 199 | float getHumidity() const { 200 | return mHumidity; 201 | } 202 | 203 | /** 204 | * Get the humidity in percent read from the last sample 205 | * Use readSample() to trigger a new sensor reading 206 | */ 207 | float getTemperature() const { 208 | return mTemperature; 209 | } 210 | 211 | float mTemperature; 212 | float mHumidity; 213 | }; 214 | 215 | /** Base class for i2c SHT Sensor drivers */ 216 | class SHTI2cSensor : public SHTSensorDriver { 217 | public: 218 | /** Size of i2c commands to send */ 219 | 220 | 221 | /** Size of i2c replies to expect */ 222 | static const uint8_t EXPECTED_DATA_SIZE; 223 | 224 | /** 225 | * Constructor for i2c SHT Sensors 226 | * Takes the `i2cAddress' to read, the `i2cCommand' issues when sampling 227 | * the sensor and the values `a', `b', `c' to convert the fixed-point 228 | * temperature value received by the sensor to a floating point value using 229 | * the formula: temperature = a + b * (rawTemperature / c) 230 | * and the values `x' and `y' to convert the fixed-point humidity value 231 | * received by the sensor to a floating point value using the formula: 232 | * humidity = x + y * (rawHumidity / z) 233 | * duration is the duration in milliseconds of one measurement 234 | */ 235 | SHTI2cSensor(uint8_t i2cAddress, uint16_t i2cCommand, uint8_t duration, 236 | float a, float b, float c, 237 | float x, float y, float z, uint8_t cmd_Size, 238 | TwoWire & wire = Wire) 239 | : mI2cAddress(i2cAddress), mI2cCommand(i2cCommand), mDuration(duration), 240 | mA(a), mB(b), mC(c), mX(x), mY(y), mZ(z), mCmd_Size(cmd_Size), 241 | mWire(wire) 242 | { 243 | } 244 | 245 | virtual ~SHTI2cSensor() 246 | { 247 | } 248 | 249 | virtual bool readSample(); 250 | 251 | uint8_t mI2cAddress; 252 | uint16_t mI2cCommand; 253 | uint8_t mDuration; 254 | float mA; 255 | float mB; 256 | float mC; 257 | float mX; 258 | float mY; 259 | float mZ; 260 | uint8_t mCmd_Size; 261 | TwoWire & mWire; 262 | 263 | private: 264 | 265 | protected: 266 | static uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crcInit = 0xff); 267 | static bool readFromI2c(TwoWire & wire, 268 | uint8_t i2cAddress, 269 | const uint8_t *i2cCommand, 270 | uint8_t commandLength, uint8_t *data, 271 | uint8_t dataLength, uint8_t duration); 272 | }; 273 | 274 | class SHT3xAnalogSensor 275 | { 276 | public: 277 | 278 | /** 279 | * Instantiate a new Sensirion SHT3x Analog sensor driver instance. 280 | * The required paramters are `humidityPin` and `temperaturePin` 281 | * An optional `readResolutionBits' can be set since the Arduino/Genuino Zero 282 | * support 12bit precision analog readings. By default, 10 bit precision is 283 | * used. 284 | * 285 | * Example usage: 286 | * SHT3xAnalogSensor sht3xAnalog(HUMIDITY_PIN, TEMPERATURE_PIN); 287 | * float humidity = sht.readHumidity(); 288 | * float temperature = sht.readTemperature(); 289 | */ 290 | SHT3xAnalogSensor(uint8_t humidityPin, uint8_t temperaturePin, 291 | uint8_t readResolutionBits = 10) 292 | : mHumidityAdcPin(humidityPin), mTemperatureAdcPin(temperaturePin), 293 | mReadResolutionBits(readResolutionBits) 294 | { 295 | } 296 | 297 | virtual ~SHT3xAnalogSensor() 298 | { 299 | } 300 | 301 | float readHumidity(); 302 | float readTemperature(); 303 | 304 | uint8_t mHumidityAdcPin; 305 | uint8_t mTemperatureAdcPin; 306 | uint8_t mReadResolutionBits; 307 | }; 308 | 309 | #endif /* SHTSENSOR_H */ 310 | -------------------------------------------------------------------------------- /SHTSensor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Sensirion AG 3 | * Copyright (c) 2015-2016, Johannes Winkelmann 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Sensirion AG nor the names of its 14 | * contributors may be used to endorse or promote products derived 15 | * from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | #include "SHTSensor.h" 33 | 34 | 35 | // 36 | // class SHTSensorDriver 37 | // 38 | 39 | SHTSensorDriver::~SHTSensorDriver() 40 | { 41 | } 42 | 43 | bool SHTSensorDriver::readSample() 44 | { 45 | return false; 46 | } 47 | 48 | 49 | // 50 | // class SHTI2cSensor 51 | // 52 | 53 | const uint8_t SHTI2cSensor::EXPECTED_DATA_SIZE = 6; 54 | 55 | bool SHTI2cSensor::readFromI2c(TwoWire & wire, 56 | uint8_t i2cAddress, 57 | const uint8_t *i2cCommand, 58 | uint8_t commandLength, uint8_t *data, 59 | uint8_t dataLength, 60 | uint8_t duration) 61 | { 62 | wire.beginTransmission(i2cAddress); 63 | for (int i = 0; i < commandLength; ++i) { 64 | if (wire.write(i2cCommand[i]) != 1) { 65 | return false; 66 | } 67 | } 68 | 69 | if (wire.endTransmission() != 0) { 70 | return false; 71 | } 72 | 73 | delay(duration); 74 | 75 | wire.requestFrom(i2cAddress, dataLength); 76 | 77 | // check if the same number of bytes are received that are requested. 78 | if (wire.available() != dataLength) { 79 | return false; 80 | } 81 | 82 | for (int i = 0; i < dataLength; ++i) { 83 | data[i] = wire.read(); 84 | } 85 | return true; 86 | } 87 | 88 | uint8_t SHTI2cSensor::crc8(const uint8_t *data, uint8_t len, uint8_t crcInit) 89 | { 90 | // adapted from SHT21 sample code from 91 | // http://www.sensirion.com/en/products/humidity-temperature/download-center/ 92 | 93 | uint8_t crc = crcInit; 94 | uint8_t byteCtr; 95 | for (byteCtr = 0; byteCtr < len; ++byteCtr) { 96 | crc ^= data[byteCtr]; 97 | for (uint8_t bit = 8; bit > 0; --bit) { 98 | if (crc & 0x80) { 99 | crc = (crc << 1) ^ 0x31; 100 | } else { 101 | crc = (crc << 1); 102 | } 103 | } 104 | } 105 | return crc; 106 | } 107 | 108 | 109 | bool SHTI2cSensor::readSample() 110 | { 111 | uint8_t data[EXPECTED_DATA_SIZE]; 112 | uint8_t cmd[mCmd_Size]; 113 | 114 | cmd[0] = mI2cCommand >> 8; 115 | //is omitted for SHT4x Sensors 116 | cmd[1] = mI2cCommand & 0xff; 117 | 118 | if (!readFromI2c(mWire, mI2cAddress, cmd, mCmd_Size, data, 119 | EXPECTED_DATA_SIZE, mDuration)) { 120 | return false; 121 | } 122 | 123 | // -- Important: assuming each 2 byte of data is followed by 1 byte of CRC 124 | 125 | // check CRC for both RH and T 126 | if (crc8(&data[0], 2) != data[2] || crc8(&data[3], 2) != data[5]) { 127 | return false; 128 | } 129 | 130 | // convert to Temperature/Humidity 131 | uint16_t val; 132 | val = (data[0] << 8) + data[1]; 133 | mTemperature = mA + mB * (val / mC); 134 | 135 | val = (data[3] << 8) + data[4]; 136 | mHumidity = mX + mY * (val / mZ); 137 | 138 | return true; 139 | 140 | } 141 | 142 | // 143 | // class SHTC1Sensor 144 | // 145 | 146 | class SHTC1Sensor : public SHTI2cSensor 147 | { 148 | public: 149 | SHTC1Sensor(TwoWire & wire) 150 | // clock stretching disabled, high precision, T first 151 | : SHTI2cSensor(0x70, 0x7866, 15, -45, 175, 65535, 0, 100, 65535, 2, wire) 152 | { 153 | } 154 | }; 155 | 156 | // 157 | // class SHT2xSensor (SHT20, SHT21, SHT25) 158 | // 159 | 160 | class SHT2xSensor : public SHTI2cSensor 161 | { 162 | public: 163 | SHT2xSensor(TwoWire &wire) 164 | // clock stretching disabled 165 | : SHTI2cSensor(0x40, // i2cAddress 166 | 0xF3F5, // i2cCommand Hi: T, Lo: RH 167 | 85, // duration 168 | -46.85, // a (sht_t_poly1) 169 | 175.72, // b (sht_t_poly2) 170 | 65536.0, // c (sht_t_poly3) 171 | -6.0, // x (sht_h_poly1) 172 | 125.0, // y (sht_h_poly2) 173 | 65536.0, // z (sht_h_poly3) 174 | 1, // cmd_Size 175 | wire) 176 | { 177 | } 178 | 179 | bool readSample() override 180 | { 181 | uint8_t data[EXPECTED_DATA_SIZE]; 182 | uint8_t cmd[mCmd_Size]; 183 | 184 | // SHT2x sends T and RH in two separate commands (different to other sensors) 185 | // so we have to spit the command into two bytes and 186 | // have to read from I2C two times with EXPECTED_DATA_SIZE / 2 187 | 188 | // read T from SHT2x Sensor 189 | // Upper byte of 'mI2cCommand' is T for SHT2x Sensors 190 | cmd[0] = mI2cCommand >> 8; 191 | if (!readFromI2c(mWire, mI2cAddress, cmd, mCmd_Size, data, 192 | EXPECTED_DATA_SIZE / 2, mDuration)) { 193 | DEBUG_SHT("SHT2x readFromI2c(T) false\n"); 194 | return false; 195 | } 196 | 197 | // read RH from SHT2x Sensor 198 | // Lower byte of 'mI2cCommand' is RH for SHT2x Sensors 199 | cmd[0] = mI2cCommand & 0xff; 200 | if (!readFromI2c(mWire, mI2cAddress, cmd, mCmd_Size, &data[3], 201 | EXPECTED_DATA_SIZE / 2, mDuration)) { 202 | DEBUG_SHT("SHT2x readFromI2c(RH) false\n"); 203 | return false; 204 | } 205 | 206 | // -- Important: assuming each 2 byte of data is followed by 1 byte of CRC 207 | 208 | // check CRC for both RH and T with a crc init value of 0 209 | if (crc8(&data[0], 2, 0) != data[2] || crc8(&data[3], 2, 0) != data[5]) { 210 | DEBUG_SHT("SHT2x crc8 false\n"); 211 | return false; 212 | } 213 | 214 | // check status bits [1..0] (see datasheet) 215 | // bit 0: not used, bit 1: measurement type (0: temperature, 1 humidity) 216 | if (((data[1] & 0x02) != 0x00) || ((data[4] & 0x02) != 0x02)) { 217 | DEBUG_SHT("SHT2x status bits false\n"); 218 | return false; 219 | } 220 | 221 | // convert to Temperature/Humidity 222 | uint16_t val; 223 | val = (data[0] << 8) + (data[1] & ~0x03); // get value and clear status bits [1..0] 224 | mTemperature = mA + mB * (val / mC); 225 | 226 | val = (data[3] << 8) + (data[4] & ~0x03); // get value and clear status bits [1..0] 227 | mHumidity = mX + mY * (val / mZ); 228 | 229 | return true; 230 | } 231 | }; 232 | 233 | // 234 | // class SHT3xSensor 235 | // 236 | 237 | class SHT3xSensor : public SHTI2cSensor 238 | { 239 | private: 240 | static const uint16_t SHT3X_ACCURACY_HIGH = 0x2400; 241 | static const uint16_t SHT3X_ACCURACY_MEDIUM = 0x240b; 242 | static const uint16_t SHT3X_ACCURACY_LOW = 0x2416; 243 | 244 | static const uint8_t SHT3X_ACCURACY_HIGH_DURATION = 15; 245 | static const uint8_t SHT3X_ACCURACY_MEDIUM_DURATION = 6; 246 | static const uint8_t SHT3X_ACCURACY_LOW_DURATION = 4; 247 | 248 | public: 249 | static const uint8_t SHT3X_I2C_ADDRESS_44 = 0x44; 250 | static const uint8_t SHT3X_I2C_ADDRESS_45 = 0x45; 251 | 252 | SHT3xSensor(TwoWire & wire, uint8_t i2cAddress = SHT3X_I2C_ADDRESS_44) 253 | : SHTI2cSensor(i2cAddress, SHT3X_ACCURACY_HIGH, 254 | SHT3X_ACCURACY_HIGH_DURATION, 255 | -45, 175, 65535, 0, 100, 65535, 2, wire) 256 | { 257 | } 258 | 259 | virtual bool setAccuracy(SHTSensor::SHTAccuracy newAccuracy) 260 | { 261 | switch (newAccuracy) { 262 | case SHTSensor::SHT_ACCURACY_HIGH: 263 | mI2cCommand = SHT3X_ACCURACY_HIGH; 264 | mDuration = SHT3X_ACCURACY_HIGH_DURATION; 265 | break; 266 | case SHTSensor::SHT_ACCURACY_MEDIUM: 267 | mI2cCommand = SHT3X_ACCURACY_MEDIUM; 268 | mDuration = SHT3X_ACCURACY_MEDIUM_DURATION; 269 | break; 270 | case SHTSensor::SHT_ACCURACY_LOW: 271 | mI2cCommand = SHT3X_ACCURACY_LOW; 272 | mDuration = SHT3X_ACCURACY_LOW_DURATION; 273 | break; 274 | default: 275 | return false; 276 | } 277 | return true; 278 | } 279 | }; 280 | 281 | 282 | // 283 | // class SHT4xSensor 284 | // 285 | 286 | class SHT4xSensor : public SHTI2cSensor 287 | { 288 | private: 289 | static const uint16_t SHT4X_ACCURACY_HIGH = 0xFD00; 290 | static const uint16_t SHT4X_ACCURACY_MEDIUM = 0xF600; 291 | static const uint16_t SHT4X_ACCURACY_LOW = 0xE000; 292 | 293 | static const uint8_t SHT4X_ACCURACY_HIGH_DURATION = 10; 294 | static const uint8_t SHT4X_ACCURACY_MEDIUM_DURATION = 4; 295 | static const uint8_t SHT4X_ACCURACY_LOW_DURATION = 2; 296 | 297 | public: 298 | static const uint8_t SHT4X_I2C_ADDRESS_44 = 0x44; 299 | static const uint8_t SHT4X_I2C_ADDRESS_45 = 0x45; 300 | 301 | SHT4xSensor(TwoWire & wire, uint8_t i2cAddress = SHT4X_I2C_ADDRESS_44) 302 | : SHTI2cSensor(i2cAddress, SHT4X_ACCURACY_HIGH, 303 | SHT4X_ACCURACY_HIGH_DURATION, 304 | -45, 175, 65535, -6, 125, 65535, 1, wire) 305 | { 306 | } 307 | 308 | virtual bool setAccuracy(SHTSensor::SHTAccuracy newAccuracy) 309 | { 310 | switch (newAccuracy) { 311 | case SHTSensor::SHT_ACCURACY_HIGH: 312 | mI2cCommand = SHT4X_ACCURACY_HIGH; 313 | mDuration = SHT4X_ACCURACY_HIGH_DURATION; 314 | break; 315 | case SHTSensor::SHT_ACCURACY_MEDIUM: 316 | mI2cCommand = SHT4X_ACCURACY_MEDIUM; 317 | mDuration = SHT4X_ACCURACY_MEDIUM_DURATION; 318 | break; 319 | case SHTSensor::SHT_ACCURACY_LOW: 320 | mI2cCommand = SHT4X_ACCURACY_LOW; 321 | mDuration = SHT4X_ACCURACY_LOW_DURATION; 322 | break; 323 | default: 324 | return false; 325 | } 326 | return true; 327 | } 328 | }; 329 | 330 | 331 | // 332 | // class SHT3xAnalogSensor 333 | // 334 | 335 | float SHT3xAnalogSensor::readHumidity() 336 | { 337 | float max_adc = (float)((1 << mReadResolutionBits) - 1); 338 | return -12.5f + 125 * (analogRead(mHumidityAdcPin) / max_adc); 339 | } 340 | 341 | float SHT3xAnalogSensor::readTemperature() 342 | { 343 | float max_adc = (float)((1 << mReadResolutionBits) - 1); 344 | return -66.875f + 218.75f * (analogRead(mTemperatureAdcPin) / max_adc); 345 | } 346 | 347 | 348 | // 349 | // class SHTSensor 350 | // 351 | 352 | const SHTSensor::SHTSensorType SHTSensor::AUTO_DETECT_SENSORS[] = { 353 | SHT4X, // IMPORTANT: SHT4x needs to be probed before the SHT3x, since they 354 | // share their I2C address, and probing for an SHT3x can cause the 355 | // first reading of and SHT4x to be off. 356 | // see https://github.com/Sensirion/arduino-sht/issues/27 357 | 358 | SHT2X, 359 | SHT3X, 360 | SHT3X_ALT, 361 | SHTC1 362 | }; 363 | const float SHTSensor::TEMPERATURE_INVALID = NAN; 364 | const float SHTSensor::HUMIDITY_INVALID = NAN; 365 | 366 | bool SHTSensor::init(TwoWire & wire) 367 | { 368 | if (mSensor != NULL) { 369 | cleanup(); 370 | } 371 | 372 | switch(mSensorType) { 373 | case SHT2X: 374 | mSensor = new SHT2xSensor(wire); 375 | break; 376 | 377 | case SHT3X: 378 | case SHT85: 379 | mSensor = new SHT3xSensor(wire); 380 | break; 381 | 382 | case SHT3X_ALT: 383 | mSensor = new SHT3xSensor(wire, SHT3xSensor::SHT3X_I2C_ADDRESS_45); 384 | break; 385 | 386 | case SHTW1: 387 | case SHTW2: 388 | case SHTC1: 389 | case SHTC3: 390 | mSensor = new SHTC1Sensor(wire); 391 | break; 392 | case SHT4X: 393 | mSensor = new SHT4xSensor(wire); 394 | break; 395 | case AUTO_DETECT: 396 | { 397 | bool detected = false; 398 | for (unsigned int i = 0; 399 | i < sizeof(AUTO_DETECT_SENSORS) / sizeof(AUTO_DETECT_SENSORS[0]); 400 | ++i) { 401 | mSensorType = AUTO_DETECT_SENSORS[i]; 402 | delay(40); // TODO: this was necessary to make SHT4x autodetect work; revisit to find root cause 403 | if (init(wire)) { 404 | detected = true; 405 | break; 406 | } 407 | } 408 | if (!detected) { 409 | cleanup(); 410 | } 411 | break; 412 | } 413 | } 414 | 415 | // to finish the initialization, attempt to read to make sure the communication works 416 | // Note: readSample() will check for a NULL mSensor in case auto detect failed 417 | return readSample(); 418 | } 419 | 420 | bool SHTSensor::readSample() 421 | { 422 | if (!mSensor || !mSensor->readSample()) 423 | return false; 424 | mTemperature = mSensor->mTemperature; 425 | mHumidity = mSensor->mHumidity; 426 | return true; 427 | } 428 | 429 | bool SHTSensor::setAccuracy(SHTAccuracy newAccuracy) 430 | { 431 | if (!mSensor) 432 | return false; 433 | return mSensor->setAccuracy(newAccuracy); 434 | } 435 | 436 | void SHTSensor::cleanup() 437 | { 438 | if (mSensor) { 439 | delete mSensor; 440 | mSensor = NULL; 441 | } 442 | } 443 | --------------------------------------------------------------------------------