Introduction.
Note that the original work in interfacing the Sensirion SHT71 with a BasicX BX24 and with a Basic Atom was originally developed by undergraduate student Brittany Fleming as a part of her capstone design project at Morgan State University.
This discussion focuses on interfacing a PICAXE-18X with a SHT RH and Temperature sensor. Sensirion offers the SHT module in a small SOIC like package (SHT11/15) and a 4-pin SIP (0.05 inch centers) (SHT71/75). The only difference between the SHTx1 and SHTx5 devices is the accuracy of the relative humidity, 3.5 vs 2.0 percent.
I was concerned with my ability to solder the very small SHT1x device to an adaptor without damaging the device and I opted for the SHT71 and for the moment I have a stock of these for $25.00. With either, you will probably need an 8-pin SOIC to DIP adaptor which I stock for $5.00. (As of this writing, the US $ is falling like a stone. As the SHT devices are manufactured in Europe, the price may well rise with time).
Note that the terminal assignments for the SHT1x and the SHT7x are not the same.
Interface Protocol
The interface with the SHT is similar to the Philips I2C protocol in having a unidirectional clock (SCK) and a bidirectional data lead (DATA), but the actual protocol is quite different and I seriously doubt that one could accommodate other I2C devices on the same pair used by the SHT device nor operate multiple SHT sensors on the same pair.
Thus, in developing the following code, I attempted to carefully follow the timing diagrams in the data sheet and the result may well be an overkill. For example, in initiating each communication session, I opted to reset the device by bringing DATA high (high impedance) and providing nine clock pulses. This probably is not necessary, but uses little code and consumes little time relative to the time required for A/D conversions.
Although the developers of the PICAXE provide specific commands such as WriteI2C and Read I2C, these will not work with the SHT71;
An additional complication is that the PICAXE-18X does not have a bidirectional IO pin. Thus, it is not obvious that one can bring the DATA output to a high impedance state (logic one) nor read from the slave.
However I found that I could use a series 4.7K resistor between the Data output and the slave's data lead and also loop this to a PICAXE input;
PICAXE-18X SHT-71
DataOut, Output1 (term 7) ------ 4.7K --------------- Data
|
DataIn, Input1 (term 18) ---------------
CLK, Output0 (term 6) ------------------------------- CLK
When sending a logic zero, the DataOut terminal is brought low and CLK is brought high and low. When sending a logic one, DataOut is brought high and CLK is brought high and low.
When reading a bit, DataOut is first brought high. CLK is brought high and the bit is read on DataIn.
Environment
The Sensirion data sheet indicates a maximum of distance of 11 cm from the processor to the SHT sensor. This probably is good conservative advice. Clearly, it probably could be located much further, and I have verified reliable operation to 100 feet through an inexpensive telephone extension cable. However, I would hesitate to use it over any such distances where property or health may be at risk if the communications is not reliable. A preferred approach might be a slave processor collocated with the SHT device in a grain silo and interfacing with the master processor using either RS232 or better yet, RS485.
I am not capable of offering advice to the ruggedness of the SHT device, its ability to withstand very cold temperatures or its recovery from moisture or recovery from frozen moisture.
Calculations
The SHT defaults to a 12-bit conversion for the relative humidity measurement. This amounts to the RH range of 0.0 to 100 percent being broken into 4096 quantizing levels or about 40 levels per percent humidity. The unit defaults to a whopping 14-bit resolution for temperature which I believe amounts to the range of -40 to 100 degrees C being quantized into some 16384 levels. Both strike me as an overkill and perhaps this comes at a penalty. Eight, 12 and 14 bit conversions, require nominally 11, 55 and 210 ms, respectively. In the following routine, I used the default values of the status register and thus used a 250 ms delay for the 14-bit temperature measurement and a 60 ms delay for the 12-bit RH measurement. However, these are times which may well make the averaging of 100 measurements requiring 30 seconds impractical.
An alternative might be to write a logic one to the least significant bit of the status register and live with 8-bit and 12-bit resolutions for RH and temperature measurements, respectively. The wait times are then reduced to nominally 11 and 55 ms.
In the following routine, I read the 12-bit value for relative humidity (SOrh) from the SHT-71. However, in performing the calculation I opted to use only the eight most significant bits.
The value of relative humidity is then calculated;
RH_linear = -4 + 0.648 * SOrh -7.2e-4 * SOrh RH_linear = -4 + 0.648 * SOrh * (1 - 7.2e-4/0.648 * SOrh)Multiplying by 100;
RH_linear_100 = -400 + 0.648 * SOrh * (100 - 0.111 * SOrh)This is implemented as follows;
X = SOrh / 10 X = SOrh / 100 + X X = SOrh / 1000 + X ' 0.111 * SOrh X = 100 - X ' 100 - 0.111 * SOrh X = X * SOrh X = X * 6 / 10 ' times 0.648 X = X * 4 /100 + X X = X * 8 / 1000 + X RH_100 = X - 400
I used the 14-bit value of the temperature (SOt).
TC = -40.00 + 0.01 * SHtMultiplying by 100;
TC_100 = -4000 + SHtThe relative humidity is then compensated for temperature;
RH_true = (Tc - 25) * (0.01 + 0.00128 * SOrh) + RH_linearMultiplying by 100;
RH_true_100 = (Tc - 25) * (1.0 + 0.128 * SOrh) + RH_100Note that for values of temperature less than 25 degrees C, the value added to RH_100 is negative.
This temperature compensation is implemented;
X = SOrh / 10 X = SOrh * 2 / 100 + X X = SOrh * 8 / 1000 + X X = X + 1 ' 1 + 0.128 * SOrh T_diff = TC_100 / 100 - 25 SignBit = T_diff / 128 If SignBit = 1 Then Negative Positive: X = T_diff * X RH_100 = RH_100 + X GoTo Display Negative: T_diff = T_diff ^ $ff + 1 ' two's compliment X = T_diff * X RH_100 = RH_100 - X GoTo Display
General Observations.
In performing the arithmetic calculations, I have made every attempt to avoid overflows. However, recognize that I did not develop it for any product that I plan to offer. Rather, I developed this code for PICAXE users to go forth and use this and use their creativity. But, before you bet the farm, it is best you be certain there are no overflows, or for that matter, other careless errors. I did test the code at my desk and compared the RH and temperature with other devices I have and then placed my finger near the device to observe increasing humidity, but this is not a conclusive test by any means.
In developing this routine, I had to pause as I allocated each of the 14 bytes of user RAM. One can sure use these 14 bytes quite quickly
Recognize however, that one has the ability to peek and poke far more RAM. I did not do so, but plan to develop code that illustrates how one might use the peek poke capability without the program becoming unreadable.
' SHT71.Bas
'
' copyright, Peter H Anderson, Baltimore, MD, Feb, '04
' pin definitions
Symbol SCL = OutPut0
Symbol SDAOut = Output1
Symbol SDAin = Input1
Symbol shtMT = %00000011 ' Meas Temp
Symbol shtMH = %00000101 ' Meas Rel Hum
Symbol shtSRW = %00000110 ' Status Reg Write
Symbol shtSRR = %00000111 ' Status Reg Read
Symbol shtRSR = %00011110 ' Soft Reset
Symbol OByte = B0
Symbol IByte = B0
Symbol N = B1
Symbol H = B0
Symbol L = B1
Symbol SOrh = W1
Symbol SOt = W2
Symbol X = W3
Symbol TC_100 = W4
Symbol RH_100 = W5
Symbol T_Diff = B0
Symbol MSBit = B12
Symbol SignBit = B12
Symbol Digit2 = B12
Main:
Top:
' soft reset
GoSub Idle
GoSub ResetComm
GoSub TransStart
OByte = shtRSR
GoSub WriteByte
Pause 50
' measure the temperature
GoSub Idle
GoSub ResetComm
GoSub TransStart
OByte = shtMT
GoSub WriteByte
Pause 250
Pause 250
GoSub ReadByte ' read high byte
SOt = IByte * 256
GoSub ReadByte ' read low byte
SOt = SOt + IByte
' H = SOt / 100
' L = SOt % 100
' SerTxD ("T = ", H, " ", L, 13)
' measure the humidity
GoSub Idle
GoSub ResetComm
GoSub TransStart
OByte = shtMH ' measure humidity
GoSub WriteByte
Pause 250 ' allow for conversion to complete
Pause 250
GoSub ReadByte
SOrh = IByte * 256
GoSub ReadByte
SOrh = SOrh + IByte
' Do the calculations
SOrh = SOrh / 16 ' use 8 bit
' SerTxD ("Point 1 ", #SOrh, 13, 10)
X = SOrh / 10
X = SOrh / 100 + X
X = SOrh / 1000 + X ' 0.112 * SOrh
X = 100 - X ' 100 - 0.112 * SOrh
X = X * SOrh
X = X / 10 * 6 ' times 0.648
X = X / 100 * 4 + X
X = X / 1000 * 8 + X
RH_100 = X - 400
TC_100 = SOt - 4000
X = SOrh / 10
X = SOrh * 2 / 100 + X
X = SOrh * 8 / 1000 + X
X = X + 1 ' 1 + 0.128 * SOrh
T_diff = TC_100 / 100 - 25
SignBit = T_diff / 256 / 128
If SignBit = 1 Then Negative
Positive:
X = T_diff * X
RH_100 = RH_100 + X
GoTo Display
Negative:
T_diff = T_diff ^ $ff + 1
X = T_diff * X
SerTxD ("Point 3 ", #X, 13, 10)
RH_100 = RH_100 - X
GoTo Display
Display:
SignBit = TC_100 / 256 / 128
X = TC_100
GoSub DisplayFloat
SerTxD (" ")
SignBit = RH_100 / 256 / 128
If SignBit = 0 Then Display_1 ' it is positive
SignBit = 0 ' Oops, it's negative
X = 0
GoSub DisplayFloat
GoTo Display_3
Display_1:
If RH_100 < 9999 Then Display_2
X = 9999 ' limit to 99.99 percent
GoSub DisplayFloat
GoTo Display_3
Display_2: ' in range of 0 to 9999
X = RH_100
GoSub DisplayFloat
Display_3:
SerTxD (10, 13)
Sleep 1
GoTo Top
Idle:
Low SCL ' low clock
High SDAOut ' high Z on Data
Return
ResetComm:
Low SCL ' low on CLK
High SDAOut ' DTA in high Z state
For N = 1 to 9
High SCL
Low SCL
Next
Return
TransStart:
High SDAOut
Low SCL
High SCL
Low SDAOut
Low SCL
High SCL
High SDAOut
Low SCL
Return
WriteByte:
For N = 1 to 8
MSBit = OByte / 128
If MSBit = 1 Then WriteByte_1
Low SDAOut
Goto WriteByte_2
WriteByte_1:
High SDAOut
WriteByte_2:
High SCL
Low SCL
OByte = OByte * 2
Next
High SDAOut
High SCL
Low SCL
Return
ReadByte:
High SDAOut
IByte = $00
For N = 1 To 8
High SCL
IByte = IByte * 2 + SDAIn
Low SCL
Next
Low SDAOut
High SCL
Low SCL
High SDAOut
Return
DisplayFloat:
If SignBit = 0 Then DisplayFloat_1
SerTxD ("-")
DisplayFloat_1:
Digit2 = X / 100 ' display the hundreds
SerTxD (#Digit2, ".")
Digit2 = X % 100
If Digit2 > 9 Then DisplayFloat_2
SerTxD ("0", #Digit2) ' be sure to put in leading zero
GoTo DisplayFloat_3
DisplayFloat_2:
SerTxD (#Digit2)
DisplayFloat_3:
Return