//-*-c++-*- // sht15.pde // j. eric townsend (jet@flatline.net) // v0.1, 11 April 2008 // // get data from an SHT15 // loosely based on the sample code from sensirion // // things of note: // // it takes a minute or so for the heater to bring the unit up to // temperature and at least a minute for it to cool back down to // ambient. // // while sensirion claims a high degree of accuracy for this sensor, // the unit I am working with consistantly reads 4F higher than my // "Eastman Kodak Tank and Tray" thermometer. This is the same // thermometer that got me through several years of darkroom // chemistry, but I've never calibrated it against anything. // // I have no real way to check the humidity reading other than to // compare it to the weather forcast. I was able to make the value // change by breating on it repeatedly, so it's measuring something. int sckPin = 10; int dataPin = 11; int heaterCount = 1; //adr command r/w const byte sht_status_w = 0x06; //000 0011 0 const byte sht_status_r = 0x07; //000 0011 1 const byte sht_read_temp = 0x03; //000 0001 1 const byte sht_read_hum = 0x05; //000 0010 1 const byte sht_reset = 0x1e; //000 1111 0 const boolean NoAck = false; const boolean Ack = true; const int readDelay = 2000; // docs suggest waiting up to 2 seconds // for the sensor to return a value void setup() { pinMode(dataPin,OUTPUT); pinMode(sckPin,OUTPUT); Serial.begin(9600); // connect to the serial port Serial.println("resetting SHT 15"); // 'After powerup the device needs 11ms to reach it's "sleep" state". // No commands should be sent before that time' -- sht15 spec. // yes, it's probably been > 11ms since powerup, but let's play it safe delay(11); resetSht15(); Serial.println("turn off heater"); setShtHeater(false); } void loop () { int temp = 0; // read the temp/humidity once a minute, turning the heater on for a // two minutes out of every 12. It takes a minute or so to warm up, // so we leave it on for two minutes. There's really no reason to // turn it on other than to test it or to burn off moisture if it gets // wet. // // so, minutes 1-9 it's off, minutes 10 and 11 it's on, and 12 it's // off again. out of that cycle, we would only trust the values in // 1-9, as we're waiting for it to cool off during 12. heaterCount++; if (heaterCount == 10) { if (setShtHeater(true)) { Serial.println("heater is on"); } else { Serial.println("ERROR: did not turn heater on"); } } else if (heaterCount == 12) { heaterCount = 1; if (setShtHeater(false)) { Serial.println("heater is off"); } else { Serial.println("ERROR: did not turn heater off"); } } temp = readShtTemp(); Serial.print("temp(F): "); Serial.println(temp); delay(500); // brief pause before reading humid. why? temp = readShtHumid(); Serial.print("relHumid: "); Serial.println(temp); delay(60000UL); // only read once a minute } void resetSht15Comm() { } void resetSht15() { pinMode(dataPin,OUTPUT); pinMode(sckPin,OUTPUT); shiftOut(dataPin, sckPin, LSBFIRST, 255); shiftOut(dataPin, sckPin, LSBFIRST, 255); digitalWrite(dataPin,HIGH); for(int i = 0; i < 15; i++){ digitalWrite(sckPin, LOW); digitalWrite(sckPin, HIGH); } } void startShtTrans() { pinMode(sckPin,OUTPUT); pinMode(dataPin,OUTPUT); digitalWrite(dataPin,HIGH); digitalWrite(sckPin,HIGH); digitalWrite(dataPin,LOW); digitalWrite(sckPin,LOW); digitalWrite(sckPin,HIGH); digitalWrite(dataPin,HIGH); digitalWrite(sckPin,LOW); } boolean readShtStatus(byte *val, byte *chk) { boolean retVal = true; startShtTrans(); writeShtByte(sht_status_r); *val = readShtByte(Ack); *chk = readShtByte(NoAck); return retVal; } boolean writeShtStatus(byte val) { boolean retVal = true; startShtTrans(); writeShtByte(sht_status_w); writeShtByte(val); return retVal; } boolean setShtHeater(boolean onOff) { const byte heaterBit = 0x04; byte status; byte chk; if (! readShtStatus(&status, &chk)) { Serial.println("ERROR: readShtStatus failed."); } if (onOff) { // on if (status & heaterBit) { return true; } else { status = status | heaterBit; writeShtStatus(status); delay(10); // seems reasonable if (! readShtStatus(&status, &chk)) { Serial.println("ERROR: readShtStatus failed."); } if (status & heaterBit) { return true; } else { return false; } } } else { // off if (! (status & heaterBit) ) { return true; } else { status = status & ~heaterBit; writeShtStatus(status); delay(10); // seems reasonable if (! readShtStatus(&status, &chk)) { Serial.println("ERROR: readShtStatus failed."); } if ( ! (status & heaterBit)) { return true; } else { return false; } } } // check the heater to see if we turned it on? return(true); } boolean writeShtByte(byte b) { boolean retVal = false; pinMode(sckPin,OUTPUT); pinMode(dataPin,OUTPUT); shiftOut(dataPin,sckPin,MSBFIRST,b); digitalWrite(dataPin, HIGH); digitalWrite(sckPin,HIGH); // check the ack bit to set the retVal pinMode(dataPin, INPUT); if (digitalRead(dataPin) == LOW) { retVal = true; } digitalWrite(sckPin,LOW); } int readSht16b() { int val = 0; unsigned int mask = 32768; int temp = 0; ; pinMode(dataPin,INPUT); pinMode(sckPin,OUTPUT); digitalWrite(sckPin,LOW); for(int i = 0; i <= 16; i++) { if(i != 8) { digitalWrite(sckPin,HIGH); temp = digitalRead(dataPin); digitalWrite(sckPin,LOW); val += (mask * temp); mask = mask / 2; } else { pinMode(dataPin,OUTPUT); digitalWrite(dataPin,LOW); digitalWrite(sckPin,HIGH); digitalWrite(sckPin,LOW); pinMode(dataPin,INPUT); } } digitalWrite(sckPin,HIGH); return val; } byte readShtByte(boolean ack) { byte val = 0; int bit = 0; // release the data line pinMode(dataPin,OUTPUT); digitalWrite(dataPin,HIGH); pinMode(dataPin,INPUT); pinMode(sckPin,OUTPUT); digitalWrite(sckPin,LOW); for(int i = 0; i <= 7; i++) { digitalWrite(sckPin,HIGH); bit = digitalRead(dataPin); digitalWrite(sckPin,LOW); val = val << 1; val = val | bit; } if (ack) { digitalWrite(dataPin, LOW); delay(5); } digitalWrite(sckPin,LOW); digitalWrite(dataPin,HIGH); return val; } int readShtTemp() { int retVal = 0; int chk = 0; int i = 0; startShtTrans(); writeShtByte(sht_read_temp); // wait for sensor to finish pinMode(dataPin, INPUT); while ((digitalRead(dataPin) == HIGH) && (i < readDelay) ) { delay(1); i++; } if (digitalRead(dataPin) == HIGH) { return 0; // TODO need to returna valid error message } retVal = readShtByte(Ack); retVal = retVal << 8; retVal += readShtByte(Ack); // we should actually test this and report an error: chk = readShtByte(NoAck); // C: -40 + .01 * SOt // F: -40 + .018 * SOt return ( (retVal * .018) - 40) ; } int readShtHumid() { int retVal = 0; int chk = 0; int i = 0; startShtTrans(); writeShtByte(sht_read_hum); // wait for sensor to finish pinMode(dataPin, INPUT); while ((digitalRead(dataPin) == HIGH) && (i < readDelay) ) { delay(1); i++; } if (digitalRead(dataPin) == HIGH) { return 0; // TODO need to report valid error message } retVal = readShtByte(Ack); retVal = retVal << 8; retVal += readShtByte(Ack); // we should actually test this and return an error: chk = readShtByte(NoAck); // rh = c1 + c2 * SOrh + c3 * (SOrh)^2 return ( (.0405 * retVal) + (-.0000028 * (retVal*retVal))); }