1
1
# The MIT License (MIT)
2
2
#
3
3
# Copyright (c) 2017 Jerry Needell
4
+ # Copyright (c) 2019 Llewelyn Trahaearn
4
5
#
5
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
# of this software and associated documentation files (the "Software"), to deal
25
26
26
27
This is a CircuitPython driver for the SHT31-D temperature and humidity sensor.
27
28
28
- * Author(s): Jerry Needell
29
+ * Author(s): Jerry Needell, Llewelyn Trahaearn
29
30
30
31
Implementation Notes
31
32
--------------------
57
58
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SHT31D.git"
58
59
59
60
60
- SHT31_DEFAULT_ADDR = const (0x44 )
61
- SHT31_MEAS_HIGHREP_STRETCH = const (0x2C06 )
62
- SHT31_MEAS_MEDREP_STRETCH = const (0x2C0D )
63
- SHT31_MEAS_LOWREP_STRETCH = const (0x2C10 )
64
- SHT31_MEAS_HIGHREP = const (0x2400 )
65
- SHT31_MEAS_MEDREP = const (0x240B )
66
- SHT31_MEAS_LOWREP = const (0x2416 )
67
- SHT31_READSTATUS = const (0xF32D )
68
- SHT31_CLEARSTATUS = const (0x3041 )
69
- SHT31_SOFTRESET = const (0x30A2 )
70
- SHT31_HEATEREN = const (0x306D )
71
- SHT31_HEATERDIS = const (0x3066 )
61
+ _SHT31_DEFAULT_ADDRESS = const (0x44 )
62
+ _SHT31_SECONDARY_ADDRESS = const (0x45 )
63
+
64
+ _SHT31_ADDRESSES = (_SHT31_DEFAULT_ADDRESS , _SHT31_SECONDARY_ADDRESS )
65
+
66
+ _SHT31_READSERIALNBR = const (0x3780 )
67
+ _SHT31_READSTATUS = const (0xF32D )
68
+ _SHT31_CLEARSTATUS = const (0x3041 )
69
+ _SHT31_HEATER_ENABLE = const (0x306D )
70
+ _SHT31_HEATER_DISABLE = const (0x3066 )
71
+ _SHT31_SOFTRESET = const (0x30A2 )
72
+ _SHT31_NOSLEEP = const (0x303E )
73
+ _SHT31_PDA_FETCH = const (0xE000 )
74
+ _SHT31_PDA_BREAK = const (0x3093 )
75
+
76
+ MODE_SSDA = 'Single'
77
+ MODE_PDA = 'Periodic'
78
+
79
+ _SHT31_MODES = (MODE_SSDA , MODE_PDA )
80
+
81
+ REP_HIGH = 'High'
82
+ REP_MED = 'Medium'
83
+ REP_LOW = 'Low'
84
+
85
+ _SHT31_REP = (REP_HIGH , REP_MED , REP_LOW )
86
+
87
+ FREQUENCY_0_5 = 0.5
88
+ FREQUENCY_1 = 1
89
+ FREQUENCY_2 = 2
90
+ FREQUENCY_4 = 4
91
+ FREQUENCY_10 = 10
92
+
93
+ _SHT31_FREQUENCIES = (FREQUENCY_0_5 , FREQUENCY_1 , FREQUENCY_2 , FREQUENCY_4 , FREQUENCY_10 )
94
+
95
+ _SSDA_COMMANDS = ((REP_LOW , const (False ), const (0x2416 )),
96
+ (REP_MED , const (False ), const (0x240B )),
97
+ (REP_HIGH , const (False ), const (0x2400 )),
98
+ (REP_LOW , const (True ), const (0x2C10 )),
99
+ (REP_MED , const (True ), const (0x2C0D )),
100
+ (REP_HIGH , const (True ), const (0x2C06 )))
101
+
102
+ _PDA_COMMANDS = ((True , None , const (0x2B32 )),
103
+ (REP_LOW , FREQUENCY_0_5 , const (0x202F )),
104
+ (REP_MED , FREQUENCY_0_5 , const (0x2024 )),
105
+ (REP_HIGH , FREQUENCY_0_5 , const (0x2032 )),
106
+ (REP_LOW , FREQUENCY_1 , const (0x212D )),
107
+ (REP_MED , FREQUENCY_1 , const (0x2126 )),
108
+ (REP_HIGH , FREQUENCY_1 , const (0x2130 )),
109
+ (REP_LOW , FREQUENCY_2 , const (0x222B )),
110
+ (REP_MED , FREQUENCY_2 , const (0x2220 )),
111
+ (REP_HIGH , FREQUENCY_2 , const (0x2236 )),
112
+ (REP_LOW , FREQUENCY_4 , const (0x2329 )),
113
+ (REP_MED , FREQUENCY_4 , const (0x2322 )),
114
+ (REP_HIGH , FREQUENCY_4 , const (0x2334 )),
115
+ (REP_LOW , FREQUENCY_10 , const (0x272A )),
116
+ (REP_MED , FREQUENCY_10 , const (0x2721 )),
117
+ (REP_HIGH , FREQUENCY_10 , const (0x2737 )))
118
+
119
+ _DELAY = ((REP_LOW , .0045 ),
120
+ (REP_MED , .0065 ),
121
+ (REP_HIGH , .0155 ))
72
122
73
123
74
124
def _crc (data ):
@@ -83,6 +133,19 @@ def _crc(data):
83
133
crc <<= 1
84
134
return crc
85
135
136
+ def _unpack (data ):
137
+ length = len (data )
138
+ crc = [None ] * (length // 3 )
139
+ word = [None ] * (length // 3 )
140
+ for i in range (length // 6 ):
141
+ word [i * 2 ], crc [i * 2 ], word [(i * 2 )+ 1 ], crc [(i * 2 )+ 1 ] = struct .unpack ('>HBHB' , data [i * 6 :(i * 6 )+ 6 ])
142
+ if crc [i * 2 ] == _crc (data [i * 6 :(i * 6 )+ 2 ]):
143
+ length = (i + 1 )* 6
144
+ for i in range (length // 3 ):
145
+ if crc [i ] != _crc (data [i * 3 :(i * 3 )+ 2 ]):
146
+ raise RuntimeError ("CRC mismatch" )
147
+ return word [:length // 3 ]
148
+
86
149
87
150
class SHT31D :
88
151
"""
@@ -91,64 +154,230 @@ class SHT31D:
91
154
:param i2c_bus: The `busio.I2C` object to use. This is the only required parameter.
92
155
:param int address: (optional) The I2C address of the device.
93
156
"""
94
- def __init__ (self , i2c_bus , address = SHT31_DEFAULT_ADDR ):
157
+ def __init__ (self , i2c_bus , address = _SHT31_DEFAULT_ADDRESS ):
158
+ if address not in _SHT31_ADDRESSES :
159
+ raise ValueError ('Invalid address: 0x%x' % (address ))
95
160
self .i2c_device = I2CDevice (i2c_bus , address )
96
- self ._command (SHT31_SOFTRESET )
97
- time .sleep (.010 )
161
+ self ._mode = MODE_SSDA
162
+ self ._repeatability = REP_HIGH
163
+ self ._frequency = FREQUENCY_4
164
+ self ._clock_stretching = False
165
+ self ._art = False
166
+ self ._last_read = 0
167
+ self ._cached_temperature = None
168
+ self ._cached_humidity = None
169
+ self ._reset ()
98
170
99
171
def _command (self , command ):
100
172
with self .i2c_device as i2c :
101
173
i2c .write (struct .pack ('>H' , command ))
102
174
175
+ def _reset (self ):
176
+ """
177
+ Soft reset the device
178
+ The reset command is preceded by a break command as the
179
+ device will not respond to a soft reset when in PDA mode.
180
+ """
181
+ self ._command (_SHT31_PDA_BREAK )
182
+ time .sleep (.001 )
183
+ self ._command (_SHT31_SOFTRESET )
184
+ time .sleep (.0015 )
185
+
186
+ def _pda (self ):
187
+ for command in _PDA_COMMANDS :
188
+ if self .art == command [0 ] or \
189
+ (self .repeatability == command [0 ] and self .frequency == command [1 ]):
190
+ self ._command (command [2 ])
191
+ time .sleep (.001 )
192
+ self ._last_read = 0
193
+
103
194
def _data (self ):
104
- data = bytearray (6 )
105
- data [0 ] = 0xff
106
- self ._command (SHT31_MEAS_HIGHREP )
107
- time .sleep (.5 )
195
+ if self .mode == MODE_PDA :
196
+ data = bytearray (42 )
197
+ data [0 ] = 0xff
198
+ self ._command (_SHT31_PDA_FETCH )
199
+ time .sleep (.001 )
200
+ elif self .mode == MODE_SSDA :
201
+ data = bytearray (6 )
202
+ data [0 ] = 0xff
203
+ for command in _SSDA_COMMANDS :
204
+ if self .repeatability == command [0 ] and self .clock_stretching == command [1 ]:
205
+ self ._command (command [2 ])
206
+ if not self .clock_stretching :
207
+ for delay in _DELAY :
208
+ if self .repeatability == delay [0 ]:
209
+ time .sleep (delay [1 ])
210
+ else :
211
+ time .sleep (.001 )
108
212
with self .i2c_device as i2c :
109
213
i2c .readinto (data )
110
- temperature , tcheck , humidity , hcheck = struct .unpack ('>HBHB' , data )
111
- if tcheck != _crc (data [:2 ]):
112
- raise RuntimeError ("temperature CRC mismatch" )
113
- if hcheck != _crc (data [3 :5 ]):
114
- raise RuntimeError ("humidity CRC mismatch" )
214
+ word = _unpack (data )
215
+ length = len (word )
216
+ temperature = [None ] * (length // 2 )
217
+ humidity = [None ] * (length // 2 )
218
+ for i in range (length // 2 ):
219
+ temperature [i ] = - 45 + (175 * (word [i * 2 ] / 65535 ))
220
+ humidity [i ] = 100 * (word [(i * 2 )+ 1 ] / 65523 )
221
+ if (len (temperature ) == 1 ) and (len (humidity ) == 1 ):
222
+ return temperature [0 ], humidity [0 ]
115
223
return temperature , humidity
116
224
225
+ def _read (self ):
226
+ if self .mode == MODE_PDA and time .time () > self ._last_read + 1 / self .frequency :
227
+ self ._cached_temperature , self ._cached_humidity = self ._data ()
228
+ self ._last_read = time .time ()
229
+ elif self .mode == MODE_SSDA :
230
+ self ._cached_temperature , self ._cached_humidity = self ._data ()
231
+ return self ._cached_temperature , self ._cached_humidity
232
+
233
+ @property
234
+ def mode (self ):
235
+ """
236
+ Operation mode
237
+ Allowed values are the constants MODE_*
238
+ Return the device to 'Single' mode to stop periodic data acquisition and allow it to sleep.
239
+ """
240
+ return self ._mode
241
+
242
+ @mode .setter
243
+ def mode (self , value ):
244
+ if not value in _SHT31_MODES :
245
+ raise ValueError ("Mode '%s' not supported" % (value ))
246
+ if self ._mode == MODE_PDA and value != MODE_PDA :
247
+ self ._command (_SHT31_PDA_BREAK )
248
+ time .sleep (.001 )
249
+ if value == MODE_PDA and self ._mode != MODE_PDA :
250
+ self ._pda ()
251
+ self ._mode = value
252
+
253
+ @property
254
+ def repeatability (self ):
255
+ """
256
+ Repeatability
257
+ Allowed values are the constants REP_*
258
+ """
259
+ return self ._repeatability
260
+
261
+ @repeatability .setter
262
+ def repeatability (self , value ):
263
+ if not value in _SHT31_REP :
264
+ raise ValueError ("Repeatability '%s' not supported" % (value ))
265
+ if self .mode == MODE_PDA and not self ._repeatability == value :
266
+ self ._repeatability = value
267
+ self ._pda ()
268
+ else :
269
+ self ._repeatability = value
270
+
271
+ @property
272
+ def clock_stretching (self ):
273
+ """
274
+ Control clock stretching.
275
+ This feature only affects SSDA mode.
276
+ """
277
+ return self ._clock_stretching
278
+
279
+ @clock_stretching .setter
280
+ def clock_stretching (self , value ):
281
+ self ._clock_stretching = bool (value )
282
+
283
+ @property
284
+ def art (self ):
285
+ """
286
+ Control accelerated response time
287
+ This feature only affects PDA mode
288
+ """
289
+ return self ._art
290
+
291
+ @art .setter
292
+ def art (self , value ):
293
+ if value :
294
+ self .frequency = FREQUENCY_4
295
+ if self .mode == MODE_PDA and not self ._art == value :
296
+ self ._art = bool (value )
297
+ self ._pda ()
298
+ else :
299
+ self ._art = bool (value )
300
+
301
+ @property
302
+ def frequency (self ):
303
+ """
304
+ Periodic data acquisition frequency
305
+ Allowed values are the constants FREQUENCY_*
306
+ Frequency can not be modified when ART is enabled
307
+ """
308
+ return self ._frequency
309
+
310
+ @frequency .setter
311
+ def frequency (self , value ):
312
+ if self .art :
313
+ raise RuntimeError ("Frequency locked to '4 Hz' when ART enabled" )
314
+ if not value in _SHT31_FREQUENCIES :
315
+ raise ValueError ("Data acquisition frequency '%s Hz' not supported" % (value ))
316
+ if self .mode == MODE_PDA and not self ._frequency == value :
317
+ self ._frequency = value
318
+ self ._pda ()
319
+ else :
320
+ self ._frequency = value
321
+
117
322
@property
118
323
def temperature (self ):
119
- """The measured temperature in degrees celsius."""
120
- raw_temperature , _ = self ._data ()
121
- return - 45 + (175 * (raw_temperature / 65535 ))
324
+ """
325
+ The measured temperature in degrees celsius.
326
+ SSDA mode reads and returns the current temperature as a float.
327
+ PDA mode returns the most recent readings available from the sensor's cache
328
+ in a FILO list of seven floats. This list is backfilled with with the
329
+ sensor's maximum output of 130.0 when the sensor is read before the
330
+ cache is full.
331
+ """
332
+ temperature , _ = self ._read ()
333
+ return temperature
122
334
123
335
@property
124
336
def relative_humidity (self ):
125
- """The measured relative humidity in percent."""
126
- _ , raw_humidity = self ._data ()
127
- return 100 * (raw_humidity / 65523 )
128
-
129
- def reset (self ):
130
- """Execute a Soft RESET of the sensor."""
131
- self ._command (SHT31_SOFTRESET )
132
- time .sleep (.010 )
337
+ """
338
+ The measured relative humidity in percent.
339
+ SSDA mode reads and returns the current humidity as a float.
340
+ PDA mode returns the most recent readings available from the sensor's cache
341
+ in a FILO list of seven floats. This list is backfilled with with the
342
+ sensor's maximum output of 100.01831417975366 when the sensor is read
343
+ before the cache is full.
344
+ """
345
+ _ , humidity = self ._read ()
346
+ return humidity
133
347
134
348
@property
135
349
def heater (self ):
136
- """Control the sensor internal heater."""
350
+ """Control device's internal heater."""
137
351
return (self .status & 0x2000 ) != 0
138
352
139
353
@heater .setter
140
354
def heater (self , value = False ):
141
355
if value :
142
- self ._command (SHT31_HEATEREN )
356
+ self ._command (_SHT31_HEATER_ENABLE )
357
+ time .sleep (.001 )
143
358
else :
144
- self ._command (SHT31_HEATERDIS )
359
+ self ._command (_SHT31_HEATER_DISABLE )
360
+ time .sleep (.001 )
145
361
146
362
@property
147
363
def status (self ):
148
- """The Sensor status."""
364
+ """Device status."""
149
365
data = bytearray (2 )
150
- self ._command (SHT31_READSTATUS )
366
+ self ._command (_SHT31_READSTATUS )
367
+ time .sleep (.001 )
151
368
with self .i2c_device as i2c :
152
369
i2c .readinto (data )
153
370
status = data [0 ] << 8 | data [1 ]
154
371
return status
372
+
373
+ @property
374
+ def serial_number (self ):
375
+ """Device serial number."""
376
+ data = bytearray (6 )
377
+ data [0 ] = 0xff
378
+ self ._command (_SHT31_READSERIALNBR )
379
+ time .sleep (.001 )
380
+ with self .i2c_device as i2c :
381
+ i2c .readinto (data )
382
+ word = _unpack (data )
383
+ return (word [0 ] << 16 ) | word [1 ]
0 commit comments