/ adafruit_dht.py
adafruit_dht.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2017 Mike McWethy for Adafruit Industries 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 :mod:`adafruit_dhtlib` 24 ====================== 25 26 CircuitPython support for the DHT11 and DHT22 temperature and humidity devices. 27 28 * Author(s): Mike McWethy 29 """ 30 31 import array 32 import time 33 try: 34 import pulseio 35 except ImportError as excpt: 36 print("adafruit_dht requires the pulseio library, but it failed to load."+ 37 " Note that CircuitPython does not support pulseio on all boards.") 38 raise excpt 39 40 class DHTBase: 41 """ base support for DHT11 and DHT22 devices 42 """ 43 44 __hiLevel = 51 45 46 def __init__(self, dht11, pin, trig_wait): 47 """ 48 :param boolean dht11: True if device is DHT11, otherwise DHT22. 49 :param ~board.Pin pin: digital pin used for communication 50 :param int trig_wait: length of time to hold trigger in LOW state (microseconds) 51 """ 52 self._dht11 = dht11 53 self._pin = pin 54 self._trig_wait = trig_wait 55 self._last_called = 0 56 self._humidity = None 57 self._temperature = None 58 59 60 def _pulses_to_binary(self, pulses, start, stop): 61 """Takes pulses, a list of transition times, and converts 62 them to a 1's or 0's. The pulses array contains the transition times. 63 pulses starts with a low transition time followed by a high transistion time. 64 then a low followed by a high and so on. The low transition times are 65 ignored. Only the high transition times are used. If the high 66 transition time is greater than __hiLevel, that counts as a bit=1, if the 67 high transition time is less that __hiLevel, that counts as a bit=0. 68 69 start is the starting index in pulses to start converting 70 71 stop is the index to convert upto but not including 72 73 Returns an integer containing the converted 1 and 0 bits 74 """ 75 76 binary = 0 77 hi_sig = False 78 for bit_inx in range(start, stop): 79 if hi_sig: 80 bit = 0 81 if pulses[bit_inx] > self.__hiLevel: 82 bit = 1 83 binary = binary<<1 | bit 84 85 hi_sig = not hi_sig 86 87 return binary 88 89 def _get_pulses(self): 90 """ _get_pulses implements the communication protcol for 91 DHT11 and DHT22 type devices. It sends a start signal 92 of a specific length and listens and measures the 93 return signal lengths. 94 95 return pulses (array.array uint16) contains alternating high and low 96 transition times starting with a low transition time. Normally 97 pulses will have 81 elements for the DHT11/22 type devices. 98 """ 99 pulses = array.array('H') 100 tmono = time.monotonic() 101 102 # create the PulseIn object using context manager 103 with pulseio.PulseIn(self._pin, 81, True) as pulse_in: 104 105 # The DHT type device use a specialize 1-wire protocol 106 # The microprocessor first sends a LOW signal for a 107 # specific length of time. Then the device sends back a 108 # series HIGH and LOW signals. The length the HIGH signals 109 # represents the device values. 110 pulse_in.pause() 111 pulse_in.clear() 112 pulse_in.resume(self._trig_wait) 113 114 # loop until we get the return pulse we need or 115 # time out after 1/2 seconds 116 while True: 117 if len(pulse_in) >= 80: 118 break 119 if time.monotonic()-tmono > 0.5: # time out after 1/2 seconds 120 break 121 122 pulse_in.pause() 123 while len(pulse_in): 124 pulses.append(pulse_in.popleft()) 125 pulse_in.resume() 126 127 return pulses 128 129 def measure(self): 130 """ measure runs the communications to the DHT11/22 type device. 131 if successful, the class properties temperature and humidity will 132 return the reading returned from the device. 133 134 Raises RuntimeError exception for checksum failure and for insuffcient 135 data returned from the device (try again) 136 """ 137 if time.monotonic()-self._last_called > 0.5: 138 self._last_called = time.monotonic() 139 140 pulses = self._get_pulses() 141 ##print(pulses) 142 143 if len(pulses) >= 80: 144 buf = array.array('B') 145 for byte_start in range(0, 80, 16): 146 buf.append(self._pulses_to_binary(pulses, byte_start, byte_start+16)) 147 #print(buf) 148 149 # humidity is 2 bytes 150 if self._dht11: 151 self._humidity = buf[0] 152 else: 153 self._humidity = ((buf[0]<<8) | buf[1]) / 10 154 155 # tempature is 2 bytes 156 if self._dht11: 157 self._temperature = buf[2] 158 else: 159 self._temperature = ((buf[2]<<8) | buf[3]) / 10 160 161 # calc checksum 162 chk_sum = 0 163 for b in buf[0:4]: 164 chk_sum += b 165 166 # checksum is the last byte 167 if chk_sum & 0xff != buf[4]: 168 # check sum failed to validate 169 raise RuntimeError("Checksum did not validate. Try again.") 170 #print("checksum did not match. Temp: {} Humidity: {} Checksum:{}".format(self._temperature,self._humidity,bites[4])) 171 172 # checksum matches 173 #print("Temp: {} C Humidity: {}% ".format(self._temperature, self._humidity)) 174 175 else: 176 raise RuntimeError("A full buffer was not returned. Try again.") 177 #print("did not get a full return. number returned was: {}".format(len(r))) 178 179 @property 180 def temperature(self): 181 """ temperature current reading. It makes sure a reading is available 182 183 Raises RuntimeError exception for checksum failure and for insuffcient 184 data returned from the device (try again) 185 """ 186 self.measure() 187 return self._temperature 188 189 @property 190 def humidity(self): 191 """ humidity current reading. It makes sure a reading is available 192 193 Raises RuntimeError exception for checksum failure and for insuffcient 194 data returned from the device (try again) 195 """ 196 self.measure() 197 return self._humidity 198 199 class DHT11(DHTBase): 200 """ Support for DHT11 device. 201 202 :param ~board.Pin pin: digital pin used for communication 203 """ 204 def __init__(self, pin): 205 super().__init__(True, pin, 18000) 206 207 208 class DHT22(DHTBase): 209 """ Support for DHT22 device. 210 211 :param ~board.Pin pin: digital pin used for communication 212 """ 213 def __init__(self, pin): 214 super().__init__(False, pin, 1000)