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.

Revision:
0:f1a93e55feb5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sht7x.h	Sun Aug 11 18:11:05 2013 +0000
@@ -0,0 +1,478 @@
+// 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