├── __init__.py ├── dht11_example.py ├── dht22_example.py ├── README.md ├── LICENSE.md └── dth.py /__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dht11_example.py: -------------------------------------------------------------------------------- 1 | import pycom 2 | import time 3 | from machine import Pin 4 | from dth import DTH 5 | 6 | pycom.heartbeat(False) 7 | pycom.rgbled(0x000008) # blue 8 | th = DTH(Pin('P3', mode=Pin.OPEN_DRAIN),0) 9 | time.sleep(2) 10 | result = th.read() 11 | if result.is_valid(): 12 | pycom.rgbled(0x001000) # green 13 | print("Temperature: %d C" % result.temperature) 14 | print("Humidity: %d %%" % result.humidity) -------------------------------------------------------------------------------- /dht22_example.py: -------------------------------------------------------------------------------- 1 | import pycom 2 | import time 3 | from machine import Pin 4 | from dth import DTH 5 | 6 | pycom.heartbeat(False) 7 | pycom.rgbled(0x000008) # blue 8 | th = DTH(Pin('P3', mode=Pin.OPEN_DRAIN),1) 9 | time.sleep(2) 10 | result = th.read() 11 | if result.is_valid(): 12 | pycom.rgbled(0x001000) # green 13 | print('Temperature: {:3.2f}'.format(result.temperature/1.0)) 14 | print('Humidity: {:3.2f}'.format(result.humidity/1.0)) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DHT Pure Python library for Pycom board 2 | 3 | This simple class can be used for reading temperature and humidity values from DHT11 and DTH22 sensors on Pycom Board. Thanks to szazo for the original source code. 4 | 5 | # Usage 6 | 7 | 1. Instantiate the `DHT` class with the pin number and type of sensor (0=DTH11, 1=DTH22) as constructor parameters. 8 | 2. Call `read()` method, which will return `DHTResult` object with actual values and error code. 9 | 10 | For example: 11 | 12 | ```python 13 | import pycom 14 | import time 15 | from machine import Pin 16 | from dth import DTH 17 | 18 | pycom.heartbeat(False) 19 | pycom.rgbled(0x000008) # blue 20 | th = DTH(Pin('P3', mode=Pin.OPEN_DRAIN),0) 21 | time.sleep(2) 22 | result = th.read() 23 | if result.is_valid(): 24 | pycom.rgbled(0x001000) # green 25 | print("Temperature: %d C" % result.temperature) 26 | print("Humidity: %d %%" % result.humidity) 27 | 28 | For working example, see `dht11_example.py` (you probably need to adjust pin for your configuration) 29 | 30 | 31 | # License 32 | 33 | This project is licensed under the terms of the MIT license. 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Zoltan Szarvas 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dth.py: -------------------------------------------------------------------------------- 1 | import time 2 | from machine import enable_irq, disable_irq, Pin 3 | 4 | 5 | class DTHResult: 6 | 'DHT sensor result returned by DHT.read() method' 7 | 8 | ERR_NO_ERROR = 0 9 | ERR_MISSING_DATA = 1 10 | ERR_CRC = 2 11 | 12 | error_code = ERR_NO_ERROR 13 | temperature = -1 14 | humidity = -1 15 | 16 | def __init__(self, error_code, temperature, humidity): 17 | self.error_code = error_code 18 | self.temperature = temperature 19 | self.humidity = humidity 20 | 21 | def is_valid(self): 22 | return self.error_code == DTHResult.ERR_NO_ERROR 23 | 24 | 25 | class DTH: 26 | 'DHT sensor (dht11, dht21,dht22) reader class for Pycom' 27 | 28 | #__pin = Pin('P3', mode=Pin.OPEN_DRAIN) 29 | __dhttype = 0 30 | 31 | def __init__(self, pin, sensor=0): 32 | self.__pin = pin 33 | self.__dhttype = sensor 34 | self.__pin(1) 35 | time.sleep(1.0) 36 | 37 | 38 | def read(self): 39 | #time.sleep(1) 40 | 41 | # send initial high 42 | #self.__send_and_sleep(1, 0.025) 43 | 44 | # pull down to low 45 | self.__send_and_sleep(0, 0.019) 46 | 47 | # collect data into an array 48 | data = self.__collect_input() 49 | #print(data) 50 | # parse lengths of all data pull up periods 51 | pull_up_lengths = self.__parse_data_pull_up_lengths(data) 52 | # if bit count mismatch, return error (4 byte data + 1 byte checksum) 53 | #print(pull_up_lengths) 54 | #print(len(pull_up_lengths)) 55 | if len(pull_up_lengths) != 40: 56 | return DTHResult(DTHResult.ERR_MISSING_DATA, 0, 0) 57 | 58 | # calculate bits from lengths of the pull up periods 59 | bits = self.__calculate_bits(pull_up_lengths) 60 | 61 | # we have the bits, calculate bytes 62 | the_bytes = self.__bits_to_bytes(bits) 63 | #print(the_bytes) 64 | # calculate checksum and check 65 | checksum = self.__calculate_checksum(the_bytes) 66 | if the_bytes[4] != checksum: 67 | return DTHResult(DTHResult.ERR_CRC, 0, 0) 68 | 69 | # ok, we have valid data, return it 70 | [int_rh, dec_rh, int_t, dec_t, csum] = the_bytes 71 | if self.__dhttype==0: #dht11 72 | rh = int_rh #dht11 20% ~ 90% 73 | t = int_t #dht11 0..50°C 74 | else: #dht21,dht22 75 | rh = ((int_rh * 256) + dec_rh)/10 76 | t = (((int_t & 0x7F) * 256) + dec_t)/10 77 | if (int_t & 0x80) > 0: 78 | t *= -1 79 | return DTHResult(DTHResult.ERR_NO_ERROR, t, rh) 80 | 81 | def __send_and_sleep(self, output, mysleep): 82 | self.__pin(output) 83 | time.sleep(mysleep) 84 | 85 | def __collect_input(self): 86 | # collect the data while unchanged found 87 | unchanged_count = 0 88 | # this is used to determine where is the end of the data 89 | max_unchanged_count = 100 90 | last = -1 91 | data = [] 92 | m = bytearray(800) # needs long sample size to grab all the bits from the DHT 93 | irqf = disable_irq() 94 | self.__pin(1) 95 | for i in range(len(m)): 96 | m[i] = self.__pin() ## sample input and store value 97 | enable_irq(irqf) 98 | for i in range(len(m)): 99 | current = m[i] 100 | data.append(current) 101 | if last != current: 102 | unchanged_count = 0 103 | last = current 104 | else: 105 | unchanged_count += 1 106 | if unchanged_count > max_unchanged_count: 107 | break 108 | #print(data) 109 | return data 110 | 111 | def __parse_data_pull_up_lengths(self, data): 112 | STATE_INIT_PULL_DOWN = 1 113 | STATE_INIT_PULL_UP = 2 114 | STATE_DATA_FIRST_PULL_DOWN = 3 115 | STATE_DATA_PULL_UP = 4 116 | STATE_DATA_PULL_DOWN = 5 117 | 118 | state = STATE_INIT_PULL_UP 119 | 120 | lengths = [] # will contain the lengths of data pull up periods 121 | current_length = 0 # will contain the length of the previous period 122 | 123 | for i in range(len(data)): 124 | 125 | current = data[i] 126 | current_length += 1 127 | 128 | if state == STATE_INIT_PULL_DOWN: 129 | if current == 0: 130 | # ok, we got the initial pull down 131 | state = STATE_INIT_PULL_UP 132 | continue 133 | else: 134 | continue 135 | if state == STATE_INIT_PULL_UP: 136 | if current == 1: 137 | # ok, we got the initial pull up 138 | state = STATE_DATA_FIRST_PULL_DOWN 139 | continue 140 | else: 141 | continue 142 | if state == STATE_DATA_FIRST_PULL_DOWN: 143 | if current == 0: 144 | # we have the initial pull down, the next will be the data pull up 145 | state = STATE_DATA_PULL_UP 146 | continue 147 | else: 148 | continue 149 | if state == STATE_DATA_PULL_UP: 150 | if current == 1: 151 | # data pulled up, the length of this pull up will determine whether it is 0 or 1 152 | current_length = 0 153 | state = STATE_DATA_PULL_DOWN 154 | continue 155 | else: 156 | continue 157 | if state == STATE_DATA_PULL_DOWN: 158 | if current == 0: 159 | # pulled down, we store the length of the previous pull up period 160 | lengths.append(current_length) 161 | state = STATE_DATA_PULL_UP 162 | continue 163 | else: 164 | continue 165 | 166 | return lengths 167 | 168 | def __calculate_bits(self, pull_up_lengths): 169 | # find shortest and longest period 170 | shortest_pull_up = 1000 171 | longest_pull_up = 0 172 | 173 | for i in range(0, len(pull_up_lengths)): 174 | length = pull_up_lengths[i] 175 | if length < shortest_pull_up: 176 | shortest_pull_up = length 177 | if length > longest_pull_up: 178 | longest_pull_up = length 179 | 180 | # use the halfway to determine whether the period it is long or short 181 | halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2 182 | bits = [] 183 | 184 | for i in range(0, len(pull_up_lengths)): 185 | bit = False 186 | if pull_up_lengths[i] > halfway: 187 | bit = True 188 | bits.append(bit) 189 | 190 | return bits 191 | 192 | def __bits_to_bytes(self, bits): 193 | the_bytes = [] 194 | byte = 0 195 | 196 | for i in range(0, len(bits)): 197 | byte = byte << 1 198 | if (bits[i]): 199 | byte = byte | 1 200 | else: 201 | byte = byte | 0 202 | if ((i + 1) % 8 == 0): 203 | the_bytes.append(byte) 204 | byte = 0 205 | #print(the_bytes) 206 | return the_bytes 207 | 208 | def __calculate_checksum(self, the_bytes): 209 | return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255 210 | --------------------------------------------------------------------------------