-
Notifications
You must be signed in to change notification settings - Fork 10
/
adafruit_htu21d.py
210 lines (158 loc) · 6.21 KB
/
adafruit_htu21d.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# SPDX-FileCopyrightText: 2018 ktown for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_htu21d`
====================================================
This is a breakout for the Adafruit HTU21D-F Temperature & Humidity
Sensor Breakout Board
* Author(s): ktown
Implementation Notes
--------------------
**Hardware:**
* `Adafruit HTU21D-F Temperature & Humidity Sensor Breakout Board
<https://www.adafruit.com/product/1899>`_ (Product ID: 1899)
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://circuitpython.org/downloads
* Adafruit's Bus Device library:
https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
"""
import struct
import time
from adafruit_bus_device.i2c_device import I2CDevice
from micropython import const
try:
import typing # pylint: disable=unused-import
from typing_extensions import Literal
from busio import I2C
except ImportError:
pass
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_HTU21D.git"
HUMIDITY = const(0xF5)
"""Constant command used to measure humidity."""
TEMPERATURE = const(0xF3)
"""Constant command used to measure temperature."""
_RESET = const(0xFE)
_WRITE_USER1 = const(0xE6)
_READ_USER1 = const(0xE7)
_USER1_VAL = const(0x3A)
_TEMP_RH_RES = (0, 1, 128, 129)
def _crc(data: bytearray) -> int:
crc = 0
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x80:
crc <<= 1
crc ^= 0x131
else:
crc <<= 1
return crc
class HTU21D:
"""
A driver for the HTU21D-F temperature and humidity sensor.
:param i2c_bus: The I2C bus the device is connected to
:param int address: (optional) The I2C address of the device. Defaults to :const:`0x40`
**Quickstart: Importing and using the HTU21D-F**
Here is an example of using the :class:`HTU21D` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
import board
from adafruit_htu21d import HTU21D
Once this is done you can define your `board.I2C` object and define your sensor object
.. code-block:: python
i2c = board.I2C() # uses board.SCL and board.SDA
sensor = HTU21D(i2c)
Now you have access to the :attr:`temperature` and :attr:`relative_humidity` attributes
.. code-block:: python
temperature = sensor.temperature
relative_humidity = sensor.relative_humidity
"""
def __init__(self, i2c_bus: I2C, address: int = 0x40) -> None:
self.i2c_device = I2CDevice(i2c_bus, address)
self._command(_RESET)
self._measurement = 0
self._buffer = bytearray(3)
time.sleep(0.01)
def _command(self, command: int) -> None:
with self.i2c_device as i2c:
i2c.write(struct.pack("B", command))
def _data(self) -> int:
data = bytearray(3)
while True:
# While busy, the sensor doesn't respond to reads.
try:
with self.i2c_device as i2c:
i2c.readinto(data)
if data[0] != 0xFF: # Check if read succeeded.
break
except OSError:
pass
value, checksum = struct.unpack(">HB", data)
if checksum != _crc(data[:2]):
raise ValueError("CRC mismatch")
return value
@property
def relative_humidity(self) -> float:
"""The measured relative humidity in percent."""
self.measurement(HUMIDITY)
self._measurement = 0
time.sleep(0.016)
return self._data() * 125.0 / 65536.0 - 6.0
@property
def temperature(self) -> float:
"""The measured temperature in degrees Celsius."""
self.measurement(TEMPERATURE)
self._measurement = 0
time.sleep(0.050)
return self._data() * 175.72 / 65536.0 - 46.85
def measurement(self, what: Literal[0xF5, 0xF3]) -> float:
"""
Starts a measurement.
Starts a measurement of either ``HUMIDITY`` or ``TEMPERATURE``
depending on the ``what`` argument. Returns immediately, and the
result of the measurement can be retrieved with the
:attr:`temperature` and :attr:`relative_humidity` properties. This way it
will take much less time.
This can be useful if you want to start the measurement, but don't
want the call to block until the measurement is ready -- for instance,
when you are doing other things at the same time.
:param int what: What to measure, one of (`HUMIDITY`, `TEMPERATURE`).
"""
if what not in (HUMIDITY, TEMPERATURE):
raise ValueError()
if not self._measurement:
self._command(what)
elif self._measurement != what:
raise RuntimeError("other measurement in progress")
self._measurement = what
@property
def temp_rh_resolution(self) -> int:
"""The temperature and relative humidity resolution
Have one of the following values: [#f1]_
======= ============== ==============
value RH res % T res C
======= ============== ==============
0 0.04 (12bit) 0.01 (14bit)
1 0.7 (8bit) 0.04 (12bit)
2 0.17 (10bit) 0.02 (13bit)
3 0.08 (11bit) 0.08 (11bit)
======= ============== ==============
.. [#f1] HTU21D(F) RH/T Sensor IC Datasheet. TE connectivity. 2017. p13
"""
self._buffer[0] = _READ_USER1
with self.i2c_device as i2c:
i2c.write_then_readinto(self._buffer, self._buffer, out_end=1)
return self._buffer[0]
@temp_rh_resolution.setter
def temp_rh_resolution(self, value: int) -> None:
self._buffer[0] = _READ_USER1
with self.i2c_device as i2c:
i2c.write_then_readinto(self._buffer, self._buffer, out_end=1)
register = (self._buffer[0] & 0xFE) | _TEMP_RH_RES[value]
self._buffer[0] = 0xE6
self._buffer[1] = register
with self.i2c_device as i2c:
i2c.write(self._buffer)