Skip to content

Commit d7fd497

Browse files
authored
Merge pull request #1367 from Joe2824/master
Add DHT20 Humidity/Temperature Sensor
2 parents 5f2d053 + d41b53e commit d7fd497

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This release changes the install directory from ~/Mycodo to /opt/Mycodo. This ne
1616

1717
### Features
1818

19+
- Add Input: DHT20
1920
- Add Output: GPIO On/Off using pinctrl (First Pi 5-compatible Output)
2021
- Add Output: PWM MQTT Publish
2122
- Add Output: GP8403 2-Channel DAC (0-10 VDC) ([#1354](https://github.com/kizniche/Mycodo/issues/1354))

mycodo/inputs/dht20.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# coding=utf-8
2+
import copy
3+
import time
4+
5+
from mycodo.inputs.base_input import AbstractInput
6+
from mycodo.inputs.sensorutils import calculate_dewpoint
7+
from mycodo.inputs.sensorutils import calculate_vapor_pressure_deficit
8+
9+
# Measurements
10+
measurements_dict = {
11+
0: {
12+
'measurement': 'temperature',
13+
'unit': 'C'
14+
},
15+
1: {
16+
'measurement': 'humidity',
17+
'unit': 'percent'
18+
},
19+
2: {
20+
'measurement': 'dewpoint',
21+
'unit': 'C'
22+
},
23+
3: {
24+
'measurement': 'vapor_pressure_deficit',
25+
'unit': 'Pa'
26+
}
27+
}
28+
29+
# Input information
30+
INPUT_INFORMATION = {
31+
'input_name_unique': 'DHT20',
32+
'input_manufacturer': 'AOSONG',
33+
'input_name': 'DHT20',
34+
'input_library': 'smbus2',
35+
'measurements_name': 'Humidity/Temperature',
36+
'measurements_dict': measurements_dict,
37+
'url_datasheet': 'http://www.aosong.com/userfiles/files/media/Data%20Sheet%20DHT20%20%20A1.pdf',
38+
'url_product_purchase': [
39+
'https://www.seeedstudio.com/Grove-Temperature-Humidity-Sensor-V2-0-DHT20-p-4967.html',
40+
'https://www.antratek.de/humidity-and-temperature-sensor-dht20',
41+
'https://www.adafruit.com/product/5183'],
42+
'url_manufacturer': 'https://asairsensors.com/product/dht20-sip-packaged-temperature-and-humidity-sensor/',
43+
44+
'options_enabled': [
45+
'measurements_select',
46+
'period',
47+
'pre_output'
48+
],
49+
50+
'options_disabled': [
51+
'interface',
52+
'i2c_location',
53+
],
54+
55+
'dependencies_module': [
56+
('pip-pypi', 'smbus2', 'smbus2==0.4.1')
57+
],
58+
59+
'interfaces': ['I2C'],
60+
'i2c_location': ['0x38'],
61+
'self.i2c_address_editable': False,
62+
}
63+
64+
65+
class InputModule(AbstractInput):
66+
"""
67+
This module is a modified version of the DHT20 module from the Mycodo
68+
distribution.
69+
70+
A sensor support class that measures the DHT20's humidity and temperature
71+
and calculates the dew point.
72+
73+
"""
74+
def __init__(self, input_dev, testing=False):
75+
"""
76+
Instantiate with the Pi and gpio to which the DHT20 output
77+
pin is connected.
78+
79+
"""
80+
super().__init__(input_dev, testing=testing, name=__name__)
81+
82+
self.i2c_address = None
83+
self.dht20 = None
84+
85+
if not testing:
86+
self.try_initialize()
87+
88+
def initialize(self):
89+
from smbus2 import SMBus
90+
91+
# Wait 100ms after power-on
92+
time.sleep(0.1)
93+
94+
self.i2c_address = int(str(self.input_dev.i2c_location), 16)
95+
self.dht20 = SMBus(self.input_dev.i2c_bus)
96+
97+
# Check if Sensor is ready
98+
status_word = self.dht20.read_byte(self.self.i2c_address)
99+
if status_word != 0x18:
100+
buffer = bytearray(b'\x00\x00')
101+
# Initialize the 0x1B, 0x1C, 0x1E registers
102+
self.dht20.write_i2c_block_data(self.i2c_address, 0x1B, buffer)
103+
self.dht20.write_i2c_block_data(self.i2c_address, 0x1C, buffer)
104+
self.dht20.write_i2c_block_data(self.i2c_address, 0x1E, buffer)
105+
time.sleep(1)
106+
107+
if self.dht20.read_byte(self.i2c_address) != 0x18:
108+
self.logger.error("Could not initialize DHT20.")
109+
else:
110+
self.logger.debug("Initialize DHT20 successfully.")
111+
112+
def calculate_crc(self, data):
113+
data = data[:-1]
114+
crc = 0xFF # Initial value of CRC
115+
polynomial = 0x31 # CRC8 check polynomial: 1 + X^4 + X^5 + X^8
116+
117+
for byte in data:
118+
crc ^= byte # XOR operation with the current byte
119+
for _ in range(8):
120+
if crc & 0x80: # Check if the most significant bit is 1
121+
crc = (crc << 1) ^ polynomial
122+
else:
123+
crc <<= 1
124+
return crc & 0xFF # Return only the 8 least significant bits
125+
126+
def get_measurement(self):
127+
"""Gets the humidity and temperature
128+
129+
'temperature': temperature (°C),
130+
'raw_temperature': the 'raw' temperature as produced by the ADC,
131+
'humidity': relative humidity (%RH),
132+
'raw_humidity': the 'raw' relative humidity as produced by the ADC,
133+
'crc_ok': indicates if the data was received correctly
134+
135+
"""
136+
self.return_dict = copy.deepcopy(measurements_dict)
137+
138+
time.sleep(0.01)
139+
# trigger measurement
140+
self.dht20.write_i2c_block_data(self.i2c_address,0xAC,[0x33,0x00])
141+
time.sleep(0.08)
142+
143+
# read measurement data
144+
data = self.dht20.read_i2c_block_data(self.i2c_address,0x71,7)
145+
146+
received_crc = data[-1]
147+
crc_ok = self.calculate_crc(data) == received_crc
148+
149+
if crc_ok:
150+
raw_temperature = ((data[3] & 0xf) << 16) + (data[4] << 8) + data[5]
151+
temperature = 200*float(raw_temperature)/2**20 - 50
152+
153+
raw_humidity = ((data[3] & 0xf0) >> 4) + (data[1] << 12) + (data[2] << 4)
154+
humidity = 100*float(raw_humidity)/2**20
155+
156+
self.logger.debug(f"Temperature: {temperature}, Humidity: {humidity}, CRC OK: {crc_ok}")
157+
158+
if humidity:
159+
temp_dew_point = calculate_dewpoint(temperature, humidity)
160+
temp_vpd = calculate_vapor_pressure_deficit(temperature, humidity)
161+
else:
162+
self.logger.error("Could not acquire measurement")
163+
164+
if temp_dew_point is not None:
165+
if self.is_enabled(0):
166+
self.value_set(0, temperature)
167+
if self.is_enabled(1):
168+
self.value_set(1, humidity)
169+
if self.is_enabled(2):
170+
self.value_set(2, temp_dew_point)
171+
if self.is_enabled(3):
172+
self.value_set(3, temp_vpd)
173+
174+
else:
175+
self.logger.error("CRC check was not successfully")
176+
177+
return self.return_dict

0 commit comments

Comments
 (0)