SHT15 Humidity and Temperature Sensor - SHT15 Breakout

No problem. Glad you got it up and running now :smile:

Where did you get your SHT-15 sensor from?

What is the advantage of the an SHT15 over a DHT22?

Its much more accurate and sensitive and a higher quality type sensor. The data sheets should have the specifics.

The DHT22 would probably be fine for most basic applications though.

To add to RWB’s comments, the big difference is that the SHT15 is an I2C device whereas the DHT22 is a 1-Wire device. In my experience with the DHT22, I find that if conditions (voltage, wire length) are not just right, the DHT22 will not read. In some cases if the 1-Wire timing is off, the DHT22 will stall until it is powered off then on again! With the SHT15, all those issues are removed because it is an I2C device. The downside of the SHT15 is that it is much more expensive than a DHT22.

So I have the spark core, and want to build an indoor temp and humidity sensor for my stepdad. He just had new hardwood floors put in their home and the installer was very strict about what the humidity levels should be in the home to maintain the floors. So what I’m wondering is, what would be the best way for him to monitor from his iphone or ipad?

Any thoughts?

james211, the sht15 will do just fine taking the readings. The Spark can make data “available” to external apps using Spark.variable() and can “push” the data using Spark.publish(). You can also use Spark.function() to send commands to your Core. The development of an iOS app is out of my realm but there are great tutorials on how to read a core using a web page and script. These include:

1 Like

Hello,
anyone have an example with spark publish?

thanks

Hello @peekay123,
could you make an example with spark.publish()?
thanks

@hmmatos, if you use the library and demo code from my repo, here is a modified loop() for the demo that will publish a string containing the readings. Don’t forget the data pin is D0 and the clock pin is D1.

void loop()
{
  float temp_c;
  float temp_f;
  float humidity;
  char msg[60];

  if (millis() > wait) {
    wait = millis() + waittime;

    // Read values from the sensor
    temp_c = sht1x.readTemperatureC();
    temp_f = sht1x.readTemperatureF();
    humidity = sht1x.readHumidity();

    //Create the string for publishing
    sprintf(msg,"%2.1fC  %2.1fF  %3.1f%%", temp_c, temp_f, humidity);

    //Publish with event name "SHTreadings", TTL of 60 seconds and set to PRIVATE publishing (can only be read by your account)
    Spark.publish("SHTreadings", msg, 60, PRIVATE);	//Remove private to publish publicly
	
    // Print the values to the serial port
    Serial.println(msg);
    }
 }

I test compiled the code on the web IDE without any problems. :smile:

Thanks @peekay123 it work perfectly with the SHT71

1 Like

I have the SHT15 Temp + Humidity sensor hooked up to the Spark Core and uploading data to Ubidots.com now.

I also have a event setup so when the temp is 87F or higher it will send me a email to alert me of this which actually works and works fast.

@peekay123 @BDub @bko @wgbartley @zachary @Dave @zach Guys this Ubidots makes getting started with the Spark Core and getting email and SMS responses easier than anything else out there that I have come across. I would highly recommend it to people getting started out since its going to provide the least frustration than most other options based on my experience. Just my 2 cents.

@aguspg is with Ubidots and he posted a how to guide showing how you integrate Ubitods with the Spark Core and that is what I used to get this up and running. Its pretty simple.

4 Likes

Looks neat, I’ll have to check it out! :slight_smile:

@RWB (Or anyone?) Would you mind sharing how you got the Spark to send two, unique variables to ubidots? I’m almost there… and feel like I need to define two variable ID’s: one for temp and one for humidity… below is what I have thus far - which obviously is not working properly. It’s sending humidity info to my temp VariableID in ubidots, and the humidity in ubidots is empty.
thanks for all the great info already provided - I was was going to ask for the a github share of the final, but wanted to put this together / learn along the way, and it has been quite fun! I’ll be locating the unit up in the attic space to monitor humidity levels over the winter and comparing to house and outdoor levels. Might even add a small fan to move air?

#define VARIABLE_ID "__ubidots_variableID_temp__"
#define VARIABLE_IDH "__ubidots_variableID_humidity__"
...
request.path = "/api/v1.6/variables/"VARIABLE_ID"/values";
requesth.path = "/api/v1.6/variables/"VARIABLE_IDH"/values";

  sprintf(resultstr, "{\"value\":%.1f}", temp_f);
request.body = resultstr;
http.post(request, response, headers); 

   sprintf(resultstr, "{\"value\":%.1f}", humidity);
requesth.body = resultstr;
http.post(requesth, responseh, headers);

@h2ogood Here is the code I’m using to send temp and humidity data to Ubidots.

Your going to need to replace the Variable ID and API key with your own unique numbers from your Ubidots account:

Here is the main code:

// This #include statement was automatically added by the Spark IDE.
#include "SHT1x.h"

// This #include statement was automatically added by the Spark IDE.
#include "HttpClient/HttpClient.h"

/**
 * ReadSHT1xValues
 *
 * Read temperature and humidity values from an SHT1x-series (SHT10,
 * SHT11, SHT15) sensor.
 *
 * Copyright 2009 Jonathan Oxer <jon@oxer.com.au>
 * www.practicalarduino.com
 */
 


// Specify data and clock connections and instantiate SHT1x object
#define dataPin  D0
#define clockPin D1
SHT1x sht1x(dataPin, clockPin);


/**
* Declaring the variables.
*/
HttpClient http;
#define VARIABLE_ID "Put Variable ID here"
#define VARIABLE_ID2 "Put Variable ID here"
#define TOKEN "put your Ubidots Token here"


// Headers currently need to be set at init, useful for API keys etc.
http_header_t headers[] = {
      { "Content-Type", "application/json" },
      { "X-Auth-Token" , TOKEN },
    { NULL, NULL } // NOTE: Always terminate headers will NULL
};

http_request_t request;
http_response_t response;

void setup() {
   
    request.hostname = "things.ubidots.com";
    request.port = 80;
    //Serial.begin(9600);
}

void loop() {


float temp_f = 0;
float humidity = 0;

 
    // Read values from the sensor
    temp_f = sht1x.readTemperatureF();
    humidity = sht1x.readHumidity();
    
    delay(10001);

    // Send to Ubidots

    request.path = "/api/v1.6/variables/"VARIABLE_ID"/values";
    request.body = "{\"value\":" + String(temp_f) + "}";
    
    http.post(request, response, headers);
    
    request.path = "/api/v1.6/variables/"VARIABLE_ID2"/values";
    request.body = "{\"value\":" + String(humidity) + "}";

    http.post(request, response, headers);
    //Serial.println(response.status);

    //Serial.println(response.body);

   
}  

Here is the SHT1x.cpp file:

/**
 * SHT1x Library
 *
 * Copyright 2009 Jonathan Oxer <jon@oxer.com.au> / <www.practicalarduino.com>
 * Based on previous work by:
 *    Maurice Ribble: <www.glacialwanderer.com/hobbyrobotics/?p=5>
 *    Wayne ?: <ragingreality.blogspot.com/2008/01/ardunio-and-sht15.html>
 *
 * Manages communication with SHT1x series (SHT10, SHT11, SHT15)
 * temperature / humidity sensors from Sensirion (www.sensirion.com).
 */

#include "SHT1x.h"

    SHT1x::SHT1x(int dataPin, int clockPin)
    {
      _dataPin = dataPin;
      _clockPin = clockPin;
    }
    
    
    /* ================  Public methods ================ */
    
    /**
     * Reads the current temperature in degrees Celsius
    */
    float SHT1x::readTemperatureC()
    {
      int _val;                // Raw value returned from sensor
      float _temperature;      // Temperature derived from raw value
    
      // Conversion coefficients from SHT15 datasheet
      const float CC1 = -40.0;  // for 14 Bit @ 5V
      const float CC2 =   0.01; // for 14 Bit DEGC
    
      // Fetch raw value
      _val = readTemperatureRaw();
    
      // Convert raw value to degrees Celsius
      _temperature = (_val * CC2) + CC1;
    
      return (_temperature);
    }
    
    /**
     * Reads the current temperature in degrees Fahrenheit
     */
    float SHT1x::readTemperatureF()
    {
      int _val;                 // Raw value returned from sensor
      float _temperature;       // Temperature derived from raw value
    
      // Conversion coefficients from SHT15 datasheet
      const float CC1 = -40.0;   // for 14 Bit @ 5V
      const float CC2 =   0.018; // for 14 Bit DEGF
    
      // Fetch raw value
      _val = readTemperatureRaw();
    
      // Convert raw value to degrees Fahrenheit
      _temperature = (_val * CC2) + CC1;
    
      return (_temperature);
    }
    
    /**
     * Reads current temperature-corrected relative humidity
     */
    float SHT1x::readHumidity()
    {
      int _val;                    // Raw humidity value returned from sensor
      float _linearHumidity;       // Humidity with linear correction applied
      float _correctedHumidity;    // Temperature-corrected humidity
      float _temperature;          // Raw temperature value
    
      // Conversion coefficients from SHT15 datasheet
      const float CC1 = -4.0;       // for 12 Bit
      const float CC2 =  0.0405;    // for 12 Bit
      const float CC3 = -0.0000028; // for 12 Bit
      const float CT1 =  0.01;      // for 14 Bit @ 5V
      const float CT2 =  0.00008;   // for 14 Bit @ 5V
    
      // Command to send to the SHT1x to request humidity
      int _gHumidCmd = 0b00000101;
    
      // Fetch the value from the sensor
      sendCommandSHT(_gHumidCmd, _dataPin, _clockPin);
      waitForResultSHT(_dataPin);
      _val = getData16SHT(_dataPin, _clockPin);
      skipCrcSHT(_dataPin, _clockPin);
    
      // Apply linear conversion to raw value
      _linearHumidity = CC1 + CC2 * _val + CC3 * _val * _val;
    
      // Get current temperature for humidity correction
      _temperature = readTemperatureC();
    
      // Correct humidity value for current temperature
      _correctedHumidity = (_temperature - 25.0 ) * (CT1 + CT2 * _val) + _linearHumidity;
    
      return (_correctedHumidity);
    }
    
    
    /* ================  Private methods ================ */
    
    /**
     * Reads the current raw temperature value
     */
    float SHT1x::readTemperatureRaw()
    {
      int _val;
    
      // Command to send to the SHT1x to request Temperature
      int _gTempCmd  = 0b00000011;
    
      sendCommandSHT(_gTempCmd, _dataPin, _clockPin);
      waitForResultSHT(_dataPin);
      _val = getData16SHT(_dataPin, _clockPin);
      skipCrcSHT(_dataPin, _clockPin);
    
      return (_val);
    }
    
    /**
     */
    int SHT1x::shiftIn(int _dataPin, int _clockPin, int _numBits)
    {
      int ret = 0;
      int i;
    
      for (i=0; i<_numBits; ++i)
      {
         digitalWrite(_clockPin, HIGH);
         delay(10);  // I don't know why I need this, but without it I don't get my 8 lsb of temp
         ret = ret*2 + digitalRead(_dataPin);
         digitalWrite(_clockPin, LOW);
      }
    
      return(ret);
    }
    
    /**
     */
    void SHT1x::sendCommandSHT(int _command, int _dataPin, int _clockPin)
    {
      int ack;
    
      // Transmission Start
      pinMode(_dataPin, OUTPUT);
      pinMode(_clockPin, OUTPUT);
      digitalWrite(_dataPin, HIGH);
      digitalWrite(_clockPin, HIGH);
      digitalWrite(_dataPin, LOW);
      digitalWrite(_clockPin, LOW);
      digitalWrite(_clockPin, HIGH);
      digitalWrite(_dataPin, HIGH);
      digitalWrite(_clockPin, LOW);
    
      // The command (3 msb are address and must be 000, and last 5 bits are command)
      shiftOut(_dataPin, _clockPin, MSBFIRST, _command);
    
      // Verify we get the correct ack
      digitalWrite(_clockPin, HIGH);
      pinMode(_dataPin, INPUT);
      ack = digitalRead(_dataPin);
      if (ack != LOW) {
        //Serial.println("Ack Error 0");
      }
      digitalWrite(_clockPin, LOW);
      ack = digitalRead(_dataPin);
      if (ack != HIGH) {
        //Serial.println("Ack Error 1");
      }
    }
    
    /**
     */
    void SHT1x::waitForResultSHT(int _dataPin)
    {
      int i;
      int ack;
    
      pinMode(_dataPin, INPUT);
    
      for(i= 0; i < 100; ++i)
      {
        delay(10);
        ack = digitalRead(_dataPin);
    
        if (ack == LOW) {
          break;
        }
      }
    
      if (ack == HIGH) {
        //Serial.println("Ack Error 2"); // Can't do serial stuff here, need another way of reporting errors
      }
    }
    
    /**
     */
    int SHT1x::getData16SHT(int _dataPin, int _clockPin)
    {
      int val;
    
      // Get the most significant bits
      pinMode(_dataPin, INPUT);
      pinMode(_clockPin, OUTPUT);
      val = shiftIn(_dataPin, _clockPin, 8);
      val *= 256;
    
      // Send the required ack
      pinMode(_dataPin, OUTPUT);
      digitalWrite(_dataPin, HIGH);
      digitalWrite(_dataPin, LOW);
      digitalWrite(_clockPin, HIGH);
      digitalWrite(_clockPin, LOW);
    
      // Get the least significant bits
      pinMode(_dataPin, INPUT);
      val |= shiftIn(_dataPin, _clockPin, 8);
    
      return val;
    }
    
    /**
     */
    void SHT1x::skipCrcSHT(int _dataPin, int _clockPin)
    {
      // Skip acknowledge to end trans (no CRC)
      pinMode(_dataPin, OUTPUT);
      pinMode(_clockPin, OUTPUT);
    
      digitalWrite(_dataPin, HIGH);
      digitalWrite(_clockPin, HIGH);
      digitalWrite(_clockPin, LOW);
    }

And last here is the SHT1x.h file:

#include "application.h"
#ifndef SHT1x_h
#define SHT1x_h


class SHT1x
{
  public:
    SHT1x(int dataPin, int clockPin);
    float readHumidity();
    float readTemperatureC();
    float readTemperatureF();
  private:
    int _dataPin;
    int _clockPin;
    int _numBits;
    float readTemperatureRaw();
    int shiftIn(int _dataPin, int _clockPin, int _numBits);
    void sendCommandSHT(int _command, int _dataPin, int _clockPin);
    void waitForResultSHT(int _dataPin);
    int getData16SHT(int _dataPin, int _clockPin);
    void skipCrcSHT(int _dataPin, int _clockPin);
};

#endif
1 Like

sweet action - that did it, thanks @RWB ! Ah, you simply put your request.path within the loop, then did an http.post after defining the request.path. That works much better. I was trying to setup variables for the request.path and requesth.path (for humidity), then making unique calls. This is why I’m NOT a programmer. Thansk again - all is working great now.

1 Like

@h2ogood Sweet! It’s nice to help somebody else out. Usually its me asking for help :smile:

@aguspg Ubidots is the nicest and easiest to use online service I have ever used. They make it super simple to setup trigger events that send emails or text messages which is nice.

1 Like

I’m glad it worked guys! Btw we’re working on a more seamless integration with Spark that will make it simpler to setup this type of code (even if you’re not a programmer :slight_smile: )

2 Likes

@RWB - One more quick question, as I recall mention of adding some form of watchdog code to maintain a consistent web connection. Do you recommend, or have you found the Spark to maintain connectivity over time without that? I’m about to drop this bad boy in the attic, and I don’t really want to be crawling up there to reset it. Just curiuos on thoughts/findings.

My personal experience with the first Spark Cores has been mixed when it comes to needing to reset it by pushing the reset button on the actual board.

I think if your using your homes Wifi connection you may have minimal issues as far as resets will go but only time will tell.

My cellphone provides my WiFi hotspot and its always coming and going as I leave the house and come back. Normally it will reconnect without issues but sometimes it will flash green and will not automatically reconnect unless I do a physical reset. I think this would be less of a issue if I was connecting to a stable home Wifi router.

I say throw it up there and see how it goes over time. Also let me know what your experience is if you do have to actually push the reset button to get it going again.

@h2ogood, @RWB is right and with the (really soon to be released) firmware, the Core will be way better at reconnecting itself. :smiley: