Interface library for the Sensirion SHT7x series humidity and temperature sensors.
This was developed and tested on the LPC1768 board.
Currently, it doesn't work for LPC11U24. I'm investigating why.
sht7x.h
- Committer:
- JacobBramley
- Date:
- 2013-08-11
- Revision:
- 0:f1a93e55feb5
File content as of revision 0:f1a93e55feb5:
// Copyright (c) 2012-2013 Jacob Bramley // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include <stdint.h> #include "mbed.h" #ifndef SHT7X_SHT7X_H #define SHT7X_SHT7X_H namespace sht7x { //! The Reading class holds a generic reading. //! //! This class is used by the Temp and Hum classes, and cannot be instantiated //! by itself. class Reading { public: //! Return the raw reading value. //! //! Only the low-order bits are relevant. Use get_bits() to determine how many //! bits are part of the reading. inline uint32_t get_raw() const { return (is_valid()) ? reading : 0; } //! Return the number of bits in the reading. inline uint32_t get_bits() const { return bits; } //! Check the validity of the reading. //! //! Only the default constructor produces an invalid reading, so this can be //! used to determine whether or not a reading was taken. inline bool is_valid() const { return (bits > 0); } protected: Reading(uint32_t bits, uint32_t reading) : bits(bits), reading(reading) {} // The default constructor creates an invalid reading. Reading() : bits(0) {} private: uint32_t bits; uint32_t reading; }; //! The Temp class holds a temperature reading. class Temp : public Reading { public: //! Initialize a Temp object with a raw reading. //! //! @param bits //! The number of significant bits in the reading. //! //! @param reading //! The raw reading taken from the SHT7x. //! //! @param mvdd //! The supply voltage. //! //! The coefficients used for the temperature calculation depend upon the //! supply voltage to the sensor. Specifying this voltage allows for the //! reading to be adjusted. The adjustment is applied to all of the get_*() //! methods except get_raw(). Temp(uint32_t bits, uint32_t reading, uint32_t mvdd = mvdd_default) : Reading(bits, reading), mvdd(mvdd) {} //! Initialize an empty object. //! //! The resulting object's is_valid() function will return false. It should //! be passed into SHT7x::measure() to get a valid reading. Temp() : Reading() {} //! Return the reading (float). float get_f() const; //! Return the reading as a 64-bit signed value with 32 fractional bits. //! //! The rounding mode is round-to-nearest, with ties away from zero. int64_t get_q32() const; // Fixed-point (q32) //! Return the reading as a 32-bit signed value. //! //! @param fbits //! The number of fractional bits desired in the result. //! //! The rounding mode is round-to-nearest, with ties away from zero. int32_t get_fixed(int fbits) const; // Fixed-point (q0-q32) //! Return the reading as an integer. //! //! The rounding mode is round-to-nearest, with ties away from zero. int32_t get_i() const { // Integer return get_fixed(0); } //! Default supply voltage setting. //! //! This setting is the recommended supply voltage according to the //! datasheet. static uint32_t const mvdd_default = 3300; private: uint32_t mvdd; // The 'd1' coefficient used for the temperature calculation depend upon // the supply voltage to the sensor. These methods calculate and return the // proper coefficient, based on mvdd. float get_d1_f() const; int64_t get_d1_q32() const; }; //! The Hum class holds a humidity reading. class Hum : public Reading { public: //! Initialize a Hum object with a raw reading. //! //! @param bits //! The number of significant bits in the reading. //! //! @param reading //! The raw reading taken from the SHT7x. //! //! @param temp //! A recent temperature reading. //! //! The humidity reading is dependant on temperature. By providing a Temp, it //! will be used to adjust humidity readings accordingly. //! //! Note that the Temp object is not copied, so it must remain in scope at //! least until the last Hum::get_*() call (other than get_raw()). Hum(uint32_t bits, uint32_t reading, Temp const * temp = NULL) : Reading(bits, reading), temp(temp) {} //! Initialize an empty object. //! //! The resulting object's is_valid() function will return false. It should be //! passed into SHT7x::measure() to get a valid reading. Hum() : Reading() {} //! Return the reading (float). float get_f() const; //! Return the reading as a 64-bit signed value with 32 fractional bits. //! //! The rounding mode is round-to-nearest, with ties away from zero. int64_t get_q32() const; // Fixed-point (q32) //! Return the reading as a 32-bit signed value. //! //! @param fbits //! The number of fractional bits desired in the result. //! //! The rounding mode is round-to-nearest, with ties away from zero. int32_t get_fixed(int fbits) const; // Fixed-point (q0-q32) //! Return the reading as an integer. //! //! The rounding mode is round-to-nearest, with ties away from zero. int32_t get_i() const { // Integer return get_fixed(0); } private: Temp const * temp; }; enum Precision { PRECISION_HIGH = 0x0, PRECISION_LOW = 0x1, PRECISION_KEEP = -1 }; enum OTP { OTP_ON = 0x0, OTP_OFF = 0x2, OTP_KEEP = -1 }; enum Heater { HEATER_OFF = 0x0, HEATER_ON = 0x4, HEATER_KEEP = -1 }; enum Battery { BATTERY_GT_2_47 = 0x00, BATTERY_LT_2_47 = 0x40 }; struct Status { Precision precision; OTP otp; Heater heater; Battery battery; }; enum State { STATE_UNKNOWN, STATE_SLEEP, STATE_RESETTING, STATE_INITIALIZING, STATE_SETTING_CONFIGURATION, STATE_GETTING_CONFIGURATION, STATE_MEASURING_TEMPERATURE, STATE_MEASURING_HUMIDITY, STATE_COMMS_ERROR }; //! Sensirion SHT7x Interface Class //! //! This class interfaces with a Sensirion SHT7x series humidity and temperature //! sensor. The SHT7x series implement a protocol similar to I²C, but //! unfortunately not similar enough that the LPC I²C peripheral can communicate //! directly. This implementation uses GPIO. //! //! Other Sensirion sensors may also be compatible, but this code has only been //! tested with SHT71 and SHT75. //! //! The SHT7x interface functions in this class will return 'false' to indicate //! a fault. A fault can be caused by one of the following events: //! //! - The class is in the wrong state. After a reset, it is necessary to call //! initialize() before any communication with the sensor. //! - A communications error is detected, most likely in the CRC check. //! - The CRC byte sent from the sensor takes the status byte into account, //! so any CRC check will fail if the low-voltage detection bit has //! changed since it the status byte was last read. //! //! There is no way to determine the cause of a fault. The suggested //! error-handling routine is as follows: //! //! - Optionally, call status() to correct problems where the low-voltage //! detection bit in the status byte has changed. This should not be //! necessary if the power supply is known to be stable. //! - If that fails, call reset() and then initialize() to force a reset. //! - This operation is relatively slow since initialize() takes 11ms. //! //! The class is not thread-safe and interface functions are not re-entrant. //! //! The datasheet can (as of early 2012) be found here: //! http://www.sensirion.com/en/pdf/product_information/Datasheet-humidity-sensor-SHT7x.pdf //! //! Example: //! @code //! #include "mbed.h" //! #include "sht7x.h" //! //! bool do_measure(sht7x::SHT7x & sensor) { //! sht7x::Temp temp; //! sht7x::Hum hum; //! //! if (!sensor.measure(temp, 5000)) { //! printf("Couldn't measure temperature.\n"); //! return false; //! } //! //! if (!sensor.measure(hum, &temp)) { //! printf("Couldn't measure humidity.\n"); //! return false; //! } //! //! printf("Temperature: %.3f degrees C.\n", temp.get_f()); //! printf("Humidity: %.3f%% relative humidity.\n", hum.get_f()); //! //! return true; //! } //! //! int main() { //! Serial serial(USBTX, USBRX); //! serial.baud(115200); //! serial.format(8, Serial::None, 1); //! //! sht7x::SHT7x sensor(p27, p28); //! // Set up communications with the sensor. //! sensor.initialize(); //! //! printf("\n======== SHT7x Example ========\n\n"); //! printf("==== Configuration: PRECISION_HIGH, OTP_ON ====\n"); //! sensor.configure(sht7x::PRECISION_HIGH, sht7x::OTP_ON); //! //! while (1) { //! if (!do_measure(sensor)) { //! printf("==== Resetting sensor... ====\n"); //! sensor.reset(); //! sensor.initialize(); //! continue; //! } //! wait(3); //! } //! } //! @endcode //! //! TODO: It might be possible to use I²C for the majority of communications, //! but switch the pin mapping to GPIO to wait for a reading. (This seems to be //! the only part that is not sufficiently I²C-compliant.) Is it worth //! investigating this? class SHT7x { public: //! Construct a new SHT7x interface object. //! //! The constructor does not communicate with the sensor in any way, but it //! assigns and configures the specified input and output pins. SHT7x(PinName sck, PinName sda); //! Reset the sensor and restore its configuration to default values. //! //! Note that it should not normally be necessary to call this method manually //! because the sensor resets automatically when turned on. //! //! After calling reset, the sensor will be in the power-on state, so it is //! necessary to call initialize before issuing any other commands. bool reset(); //! Initialize the sensor and communications channel. //! //! This takes 11ms, and must be done manually before using the sensor for the //! first time after a hard or soft reset. For this reason, it is advisable to //! call initialize() in advance, rather than on-demand. bool initialize(); //! Configure the sensor's status byte. //! //! @param precision //! High-precision readings take longer to measure than low-precision //! readings. As a result, they will probably require more energy. //! //! @param otp //! By default, the sensor will reload OTP calibration data before each //! measurement. It is possible to disable this mechanism. However, the //! datasheet is not clear about the consequences of this, except that //! disabling it can take about 10ms off the measurement time. //! //! @param heater //! The heater can be used to verify the operation of the sensor. When the //! heater is activated, the temperature reading should increase and the //! humidity reading should decrease. Note that the heater should not be //! left on permanently because it could cause damage. The datasheet is not //! clear about how long it can be left on for, however. //! //! Setting any of the values to *_KEEP will cause the existing values to //! remain unchanged. bool configure(Precision precision, OTP otp = OTP_KEEP, Heater heater = HEATER_KEEP); //! Retrieve the sensor's status byte. bool status(Status * & status); //! Take a temperature measurement. //! //! @param temp //! The sht7x::Temp object to populate. //! //! @param mvdd //! The supply voltage to the sensor. This is used to adjust the sensor //! reading as specified by the datasheet. The datasheet recommends using a //! 3.3V supply voltage for maximum accuracy. //! //! The temperature reading can vary by approximately 0.8 degrees across the //! specified voltage range (2.4-5.5V). If this accuracty is sufficient, or //! if you only want to read the raw data, you may ignore mvdd. bool measure(Temp & temp, uint32_t mvdd = Temp::mvdd_default); //! Take a humidity measurement. //! //! @param hum //! The sht7x::Hum object to populate. //! //! @param temp //! A temperature reading. This is used to adjust the humidity reading as //! specified by the datasheet. //! //! The humidity reading can vary by approximately 2% relative humidity //! across the operating temperature and humidity ranges. If this accuracy is //! sufficient, or if you only want to read the raw data, you may ignore temp. bool measure(Hum & hum, Temp const * temp = NULL); private: // Private types and fields. enum Command { CMD_READ_TEMP = 0x03, CMD_READ_HUM = 0x05, CMD_READ_STATUS = 0x07, CMD_WRITE_STATUS = 0x06, CMD_RESET = 0x1e }; // Pin states, as driven by the microprocessor. Because sda is open-drain, // its real value may differ in the PIN_1 and PIN_FREE cases. enum pin_State { PIN_0 = 0x0, PIN_1 = 0x1, PIN_FREE = 0x2, PIN_INVALID = 0x3 }; // Communications pins. DigitalOut sck_; DigitalInOut sda_; pin_State sck_state_; pin_State sda_state_; // Sensor (local) state. State state_; // Cached status register bits. Status status_; // Utilities uint32_t encode_status_byte() const; void decode_status_byte(uint32_t value); uint32_t bits_required_temperature() const; uint32_t bits_required_humidity() const; uint32_t time_atom() const; uint32_t time_reset() const; uint32_t time_reading_temperature() const; uint32_t time_reading_humidity() const; // Communications void start(); void put_bit(int b); int get_bit(); void put_ack(int b); void get_ack(); void put_byte(uint32_t value); uint32_t get_byte(); int get_sda(); void set_sda(int pin); void set_sda(pin_State pin); void set_sck(int pin); void set_sck(pin_State pin); // Utility methods. void reset_comms(); void command(Command command); bool check_state(State s0); bool check_state(State s0, State s1); bool check_state(State s0, State s1, State s2); static int payload_size(Command command); bool status(); // Error handling. bool check_crc(Command command, uint32_t data, uint32_t crc); }; } // namespace sht7x #endif // SHT7X_SHT7X_H