Skip to content

Commit 72fcacd

Browse files
authored
Merge pull request #281 from SiLab-Bonn/sensirion_sht45
ADD: Add support for sensirion SHT4x family of sensors
2 parents a9c27a1 + 30c3978 commit 72fcacd

1 file changed

Lines changed: 139 additions & 0 deletions

File tree

basil/HL/sensirion_sht45.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# ------------------------------------------------------------
2+
# Copyright (c) All rights reserved
3+
# SiLab, Institute of Physics, University of Bonn
4+
# ------------------------------------------------------------
5+
#
6+
import logging
7+
import struct
8+
9+
from basil.HL.SensirionBridgeDevice import SensirionBridgeI2CDevice
10+
11+
logger = logging.getLogger(__name__)
12+
logging.getLogger("sensirion_shdlc_driver.connection").setLevel(logging.ERROR)
13+
14+
15+
class sensirionSHT45(SensirionBridgeI2CDevice):
16+
"""
17+
Driver for the Sensirion SHT45 temperature and humidity sensor (SHT4x family).
18+
19+
Unlike the SHT3x/SHT85, the SHT4x only supports single-shot measurements
20+
(no periodic/ART mode and no status register).
21+
22+
Measurements can be performed in three repeatability modes:
23+
low (default) (0.2°C, 0.21%RH), medium (0.12°C, 0.21%RH), high (0.1°C, 0.08%RH)
24+
with respective drawbacks in readout speed and power consumption.
25+
26+
The dew point can be estimated using humidity and temperature.
27+
28+
The sensor has an integrated heater (20/110/200 mW) that can be activated
29+
for 0.1s or 1s. A heater command always performs a high-repeatability
30+
measurement right before the heater is switched off again and returns
31+
that reading; there is no separate heater on/off command.
32+
"""
33+
34+
def __init__(self, intf, conf):
35+
super(sensirionSHT45, self).__init__(intf, conf)
36+
37+
def init(self):
38+
super(sensirionSHT45, self).init(0x44)
39+
40+
try:
41+
import crcmod
42+
43+
self.crc_func = crcmod.mkCrcFun(0x131, initCrc=0xFF, rev=False, xorOut=0x00)
44+
except ImportError:
45+
logger.warning("You have to install the package 'crcmod'! Transmission errors will not be caught.")
46+
self.crc_func = lambda x: 0
47+
48+
self.repeatability = self._init.get("repeatability", "low")
49+
50+
def _read(self, command, read_n_words=0, timeout_us=20e3, n_tries=10):
51+
for _ in range(n_tries):
52+
rx_data = super(sensirionSHT45, self)._read(command, read_n_words * 3, timeout_us)
53+
data = [0] * read_n_words
54+
for i in range(read_n_words):
55+
if self.crc_func(rx_data[i * 3 : (i + 1) * 3]):
56+
break
57+
else:
58+
data[i] = struct.unpack(">H", rx_data[i * 3 : i * 3 + 2])[0]
59+
else:
60+
return data
61+
continue
62+
raise Exception("Checksum repeatedly ({0}x) wrong".format(n_tries), rx_data)
63+
64+
def _write(self, command):
65+
super(sensirionSHT45, self)._write(command)
66+
67+
def _perform_measurement(self, read_n_words=2):
68+
# command byte, max measurement duration in us (see datasheet Table 4)
69+
params = {
70+
"low": ([0xE0], 1700),
71+
"medium": ([0xF6], 4500),
72+
"high": ([0xFD], 8200),
73+
}[self.repeatability]
74+
return self._read(params[0], read_n_words=read_n_words, timeout_us=params[1])
75+
76+
def get_temperature(self):
77+
data = self._perform_measurement(read_n_words=2)
78+
return self._to_temperature(data)
79+
80+
def get_humidity(self):
81+
data = self._perform_measurement(read_n_words=2)
82+
return self._to_humidity(data)
83+
84+
def get_temperature_and_humidity(self):
85+
data = self._perform_measurement(read_n_words=2)
86+
return self._to_temperature(data), self._to_humidity(data)
87+
88+
def get_dew_point(self):
89+
T, RH = self.get_temperature_and_humidity()
90+
return self.to_dew_point(T, RH)
91+
92+
def measure_with_heater(self, power="high", duration="short"):
93+
"""
94+
Fire the integrated heater and return the T/RH reading taken with
95+
high repeatability just before the heater switches off again.
96+
97+
power: "low" (20mW), "medium" (110mW) or "high" (200mW)
98+
duration: "short" (0.1s) or "long" (1s)
99+
100+
Note: the heater is rated for a maximum on-time of 1s and should not
101+
be fired back-to-back without a cool-down; see the datasheet.
102+
"""
103+
cmd, timeout_us = {
104+
"high": {"long": ([0x39], 1.1e6), "short": ([0x32], 0.11e6)},
105+
"medium": {"long": ([0x2F], 1.1e6), "short": ([0x24], 0.11e6)},
106+
"low": {"long": ([0x1E], 1.1e6), "short": ([0x15], 0.11e6)},
107+
}[power][duration]
108+
109+
data = self._read(cmd, read_n_words=2, timeout_us=timeout_us)
110+
return self._to_temperature(data), self._to_humidity(data)
111+
112+
def get_serial_number(self):
113+
data = self._read([0x89], read_n_words=2, timeout_us=1e3)
114+
return (data[0] << 16) | data[1]
115+
116+
# This soft-reset re-initializes all registers.
117+
# A general call reset (not implemented here) also resets the sensor.
118+
def reset_sensor(self):
119+
self._write([0x94])
120+
121+
def _to_temperature(self, data):
122+
return -45 + 175 * (float(data[0]) / (2**16 - 1))
123+
124+
def _to_humidity(self, data):
125+
RH = -6 + 125 * (float(data[1]) / (2**16 - 1))
126+
return min(max(RH, 0), 100)
127+
128+
def to_dew_point(self, T, RH):
129+
"""returns the dew point using an approximation
130+
approximation specified by Sensirion:
131+
http://irtfweb.ifa.hawaii.edu/~tcs3/tcs3/Misc/Dewpoint_Calculation_Humidity_Sensor_E.pdf
132+
"""
133+
import numpy as np
134+
135+
if RH == 0:
136+
RH = self._to_humidity((0, 1)) # lowest non-zero rel. humidity
137+
H = (np.log10(RH) - 2) / 0.4343 + (17.62 * T) / (243.12 + T)
138+
Dp = 243.12 * H / (17.62 - H)
139+
return Dp

0 commit comments

Comments
 (0)