Skip to content

Commit 1d1ed4a

Browse files
committed
[FEAT] addition of the library for the humidity temperature sensor and its functions in the elio library
1 parent de17a37 commit 1d1ed4a

File tree

9 files changed

+404
-4
lines changed

9 files changed

+404
-4
lines changed

.idea/.gitignore

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/Eliobot-Python-Library.iml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/profiles_settings.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

adafruit_dht.py

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
# SPDX-FileCopyrightText: 2017 Mike McWethy for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""
6+
:mod:`adafruit_dhtlib`
7+
======================
8+
9+
CircuitPython support for the DHT11 and DHT22 temperature and humidity devices.
10+
11+
* Author(s): Mike McWethy
12+
13+
**Hardware:**
14+
15+
* Adafruit `DHT22 temperature-humidity sensor + extras
16+
<https://www.adafruit.com/product/385>`_ (Product ID: 385)
17+
18+
* Adafruit `DHT11 basic temperature-humidity sensor + extras
19+
<https://www.adafruit.com/product/386>`_ (Product ID: 386)
20+
21+
22+
**Software and Dependencies:**
23+
24+
* Adafruit CircuitPython firmware for the supported boards:
25+
https://circuitpython.org/downloads
26+
27+
"""
28+
29+
import array
30+
import time
31+
from os import uname
32+
from digitalio import DigitalInOut, Pull, Direction
33+
34+
_USE_PULSEIO = False
35+
try:
36+
from pulseio import PulseIn
37+
38+
_USE_PULSEIO = True
39+
except (ImportError, NotImplementedError):
40+
pass # This is OK, we'll try to bitbang it!
41+
42+
try:
43+
# Used only for typing
44+
from typing import Union
45+
from microcontroller import Pin
46+
except ImportError:
47+
pass
48+
49+
__version__ = "0.0.0+auto.0"
50+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DHT.git"
51+
52+
53+
class DHTBase:
54+
"""base support for DHT11 and DHT22 devices
55+
56+
:param bool dht11: True if device is DHT11, otherwise DHT22.
57+
:param ~board.Pin pin: digital pin used for communication
58+
:param int trig_wait: length of time to hold trigger in LOW state (microseconds)
59+
:param bool use_pulseio: False to force bitbang when pulseio available (only with Blinka)
60+
"""
61+
62+
__hiLevel = 51
63+
64+
def __init__(
65+
self,
66+
dht11: bool,
67+
pin: Pin,
68+
trig_wait: int,
69+
use_pulseio: bool,
70+
*,
71+
max_pulses: int = 81
72+
):
73+
self._dht11 = dht11
74+
self._pin = pin
75+
self._trig_wait = trig_wait
76+
self._max_pulses = max_pulses
77+
self._last_called = 0
78+
self._humidity = None
79+
self._temperature = None
80+
self._use_pulseio = use_pulseio
81+
if "Linux" not in uname() and not self._use_pulseio:
82+
raise ValueError("Bitbanging is not supported when using CircuitPython.")
83+
# We don't use a context because linux-based systems are sluggish
84+
# and we're better off having a running process
85+
if self._use_pulseio:
86+
self.pulse_in = PulseIn(self._pin, maxlen=self._max_pulses, idle_state=True)
87+
self.pulse_in.pause()
88+
89+
def exit(self) -> None:
90+
"""Cleans up the PulseIn process. Must be called explicitly"""
91+
if self._use_pulseio:
92+
self.pulse_in.deinit()
93+
94+
def _pulses_to_binary(self, pulses: array.array, start: int, stop: int) -> int:
95+
"""Takes pulses, a list of transition times, and converts
96+
them to a 1's or 0's. The pulses array contains the transition times.
97+
pulses starts with a low transition time followed by a high transistion time.
98+
then a low followed by a high and so on. The low transition times are
99+
ignored. Only the high transition times are used. If the high
100+
transition time is greater than __hiLevel, that counts as a bit=1, if the
101+
high transition time is less that __hiLevel, that counts as a bit=0.
102+
103+
start is the starting index in pulses to start converting
104+
105+
stop is the index to convert upto but not including
106+
107+
Returns an integer containing the converted 1 and 0 bits
108+
"""
109+
110+
binary = 0
111+
hi_sig = False
112+
for bit_inx in range(start, stop):
113+
if hi_sig:
114+
bit = 0
115+
if pulses[bit_inx] > self.__hiLevel:
116+
bit = 1
117+
binary = binary << 1 | bit
118+
119+
hi_sig = not hi_sig
120+
121+
return binary
122+
123+
def _get_pulses_pulseio(self) -> array.array:
124+
"""_get_pulses implements the communication protocol for
125+
DHT11 and DHT22 type devices. It sends a start signal
126+
of a specific length and listens and measures the
127+
return signal lengths.
128+
129+
return pulses (array.array uint16) contains alternating high and low
130+
transition times starting with a low transition time. Normally
131+
pulses will have 81 elements for the DHT11/22 type devices.
132+
"""
133+
pulses = array.array("H")
134+
if self._use_pulseio:
135+
# The DHT type device use a specialize 1-wire protocol
136+
# The microprocessor first sends a LOW signal for a
137+
# specific length of time. Then the device sends back a
138+
# series HIGH and LOW signals. The length the HIGH signals
139+
# represents the device values.
140+
self.pulse_in.clear()
141+
self.pulse_in.resume(self._trig_wait)
142+
143+
# loop until we get the return pulse we need or
144+
# time out after 1/4 second
145+
time.sleep(0.25)
146+
self.pulse_in.pause()
147+
while self.pulse_in:
148+
pulses.append(self.pulse_in.popleft())
149+
return pulses
150+
151+
def _get_pulses_bitbang(self) -> array.array:
152+
"""_get_pulses implements the communication protcol for
153+
DHT11 and DHT22 type devices. It sends a start signal
154+
of a specific length and listens and measures the
155+
return signal lengths.
156+
157+
return pulses (array.array uint16) contains alternating high and low
158+
transition times starting with a low transition time. Normally
159+
pulses will have 81 elements for the DHT11/22 type devices.
160+
"""
161+
pulses = array.array("H")
162+
with DigitalInOut(self._pin) as dhtpin:
163+
# we will bitbang if no pulsein capability
164+
transitions = []
165+
# Signal by setting pin high, then low, and releasing
166+
dhtpin.direction = Direction.OUTPUT
167+
dhtpin.value = True
168+
time.sleep(0.1)
169+
dhtpin.value = False
170+
# Using the time to pull-down the line according to DHT Model
171+
time.sleep(self._trig_wait / 1000000)
172+
timestamp = time.monotonic() # take timestamp
173+
dhtval = True # start with dht pin true because its pulled up
174+
dhtpin.direction = Direction.INPUT
175+
176+
try:
177+
dhtpin.pull = Pull.UP
178+
# Catch the NotImplementedError raised because
179+
# blinka.microcontroller.generic_linux.libgpiod_pin does not support
180+
# internal pull resistors.
181+
except NotImplementedError:
182+
dhtpin.pull = None
183+
184+
while time.monotonic() - timestamp < 0.25:
185+
if dhtval != dhtpin.value:
186+
dhtval = not dhtval # we toggled
187+
transitions.append(time.monotonic()) # save the timestamp
188+
# convert transtions to microsecond delta pulses:
189+
# use last 81 pulses
190+
transition_start = max(1, len(transitions) - self._max_pulses)
191+
for i in range(transition_start, len(transitions)):
192+
pulses_micro_sec = int(1000000 * (transitions[i] - transitions[i - 1]))
193+
pulses.append(min(pulses_micro_sec, 65535))
194+
return pulses
195+
196+
def measure(self) -> None:
197+
"""measure runs the communications to the DHT11/22 type device.
198+
if successful, the class properties temperature and humidity will
199+
return the reading returned from the device.
200+
201+
Raises RuntimeError exception for checksum failure and for insufficient
202+
data returned from the device (try again)
203+
"""
204+
delay_between_readings = 2 # 2 seconds per read according to datasheet
205+
# Initiate new reading if this is the first call or if sufficient delay
206+
# If delay not sufficient - return previous reading.
207+
# This allows back to back access for temperature and humidity for same reading
208+
if (
209+
self._last_called == 0
210+
or (time.monotonic() - self._last_called) > delay_between_readings
211+
):
212+
self._last_called = time.monotonic()
213+
214+
new_temperature = 0
215+
new_humidity = 0
216+
217+
if self._use_pulseio:
218+
pulses = self._get_pulses_pulseio()
219+
else:
220+
pulses = self._get_pulses_bitbang()
221+
# print(len(pulses), "pulses:", [x for x in pulses])
222+
223+
if len(pulses) < 10:
224+
# Probably a connection issue!
225+
raise RuntimeError("DHT sensor not found, check wiring")
226+
227+
if len(pulses) < 80:
228+
# We got *some* data just not 81 bits
229+
raise RuntimeError("A full buffer was not returned. Try again.")
230+
231+
buf = array.array("B")
232+
for byte_start in range(0, 80, 16):
233+
buf.append(self._pulses_to_binary(pulses, byte_start, byte_start + 16))
234+
235+
if self._dht11:
236+
# humidity is 1 byte
237+
new_humidity = buf[0]
238+
# temperature is 1 byte
239+
new_temperature = buf[2]
240+
else:
241+
# humidity is 2 bytes
242+
new_humidity = ((buf[0] << 8) | buf[1]) / 10
243+
# temperature is 2 bytes
244+
# MSB is sign, bits 0-14 are magnitude)
245+
new_temperature = (((buf[2] & 0x7F) << 8) | buf[3]) / 10
246+
# set sign
247+
if buf[2] & 0x80:
248+
new_temperature = -new_temperature
249+
# calc checksum
250+
chk_sum = 0
251+
for b in buf[0:4]:
252+
chk_sum += b
253+
254+
# checksum is the last byte
255+
if chk_sum & 0xFF != buf[4]:
256+
# check sum failed to validate
257+
raise RuntimeError("Checksum did not validate. Try again.")
258+
259+
if new_humidity < 0 or new_humidity > 100:
260+
# We received unplausible data
261+
raise RuntimeError("Received unplausible data. Try again.")
262+
263+
self._temperature = new_temperature
264+
self._humidity = new_humidity
265+
266+
@property
267+
def temperature(self) -> Union[int, float, None]:
268+
"""temperature current reading. It makes sure a reading is available
269+
270+
Raises RuntimeError exception for checksum failure and for insufficient
271+
data returned from the device (try again)
272+
"""
273+
self.measure()
274+
return self._temperature
275+
276+
@property
277+
def humidity(self) -> Union[int, float, None]:
278+
"""humidity current reading. It makes sure a reading is available
279+
280+
Raises RuntimeError exception for checksum failure and for insufficient
281+
data returned from the device (try again)
282+
"""
283+
self.measure()
284+
return self._humidity
285+
286+
287+
class DHT11(DHTBase):
288+
"""Support for DHT11 device.
289+
290+
:param ~board.Pin pin: digital pin used for communication
291+
"""
292+
293+
def __init__(self, pin: Pin, use_pulseio: bool = _USE_PULSEIO):
294+
super().__init__(True, pin, 18000, use_pulseio)
295+
296+
297+
class DHT22(DHTBase):
298+
"""Support for DHT22 device.
299+
300+
:param ~board.Pin pin: digital pin used for communication
301+
"""
302+
303+
def __init__(self, pin: Pin, use_pulseio: bool = _USE_PULSEIO):
304+
super().__init__(False, pin, 1000, use_pulseio)
305+
306+
307+
class DHT21(DHTBase):
308+
"""Support for DHT21/AM2301 device.
309+
310+
:param ~board.Pin pin: digital pin used for communication
311+
"""
312+
313+
# DHT21/AM2301 is sending three more dummy bytes after the "official" protocol.
314+
# Pulseio will take only the last pulses up to maxPulses.
315+
# If that would be 81, the dummy pulses will be read and the real data would be truncated.
316+
# Hence setting maxPulses to 129, taking both real data and dummy bytes into buffer.
317+
def __init__(self, pin: Pin, use_pulseio: bool = _USE_PULSEIO):
318+
super().__init__(False, pin, 1000, use_pulseio, max_pulses=129)

0 commit comments

Comments
 (0)