This repository was archived by the owner on Jan 15, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmpl115a2.py
More file actions
373 lines (299 loc) · 11 KB
/
mpl115a2.py
File metadata and controls
373 lines (299 loc) · 11 KB
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
"""
mpl115a2.py
===========
Implements support for the Freescale MPL115A2 barometric pressure sensor.
Copyright (c) 2014 by Kevin Houlihan
License: MIT, see LICENSE for more details.
Usage
=====
The main functionality of this module is contained in the Mpl115A2 class, which
wraps an I2C object from the pyb module to control an MPL115A2 device. The RST
and SHDN pins can also optionally be controlled via Pin objects.
The device must be instructed to convert its sensor data by calling the
initiate_conversion method. The readings should be ready after about 3ms.
bus = I2C(1, I2C.MASTER)
sensor = Mpl115A2(bus)
sensor.initiate_conversion()
pyb.delay(3)
print(sensor.temperature)
print(sensor.pressure)
Shutdown and Reset
==================
The device can be shut down when not required to save power. This is not done
via I2C, but is controlled by a GPIO pin. A Pin object or name can be passed to
the constructor to allow this state to be managed.
sensor = Mpl115A2(bus, shutdown_pin='X9')
sensor.shutdown = True
Another pin can be used to "reset" the device. That is Freescale's term, not mine,
and it is poorly documented, but apparently it shuts down the I2C interface.
sensor = Mpl115A2(bus, reset_pin='X10')
sensor.reset = True
When the device is awoken from a shutdown or reset state, it takes up to 5ms before
it is ready to respond to commands.
sensor.shutdown = False
pyb.delay(5)
sensor.initiate_conversion()
Temperature and Pressure Scales/Units
=====================================
By default, the temperature is in celsius and the pressure is in kilopascals.
If other scales/units are preferred, convertors can be provided to the constructor.
Fahrenheit and Kelvin convertor classes are included in this module for temperature.
The pressure convertors also allow an adjustment to be specified, which is intended
for adjusting the pressure to sea-level. As such, there is a convertor for
AdjustedKiloPascals, HectoPascals, Atmospheres, PSI and Bars.
sensor = Mpl115A2(
bus,
temperature_convertor=Fahrenheit(),
pressure_convertor=HectoPascals(0.9)
)
Custom convertors can be provided as objects with these signatures:
class TemperatureScaleUnit(object):
def convert_to(self, temperature):
'''
Convert TO the custom unit FROM celsius.
'''
...
return new_temp
class PressureScaleUnit(object):
def convert_to(self, pressure):
'''
Convert TO the custom unit FROM kPa.
'''
...
return new_pressure
"""
import pyb
def _parse_signed(msb, lsb):
combined = msb << 8 | lsb
negative = combined & 0x8000
if negative:
combined ^= 0xffff
combined *= -1
return combined
class Celsius(object):
"""
Dummy convertor for celsius. Providing an instance of this convertor
is the same as providing no convertor.
"""
def convert_to(self, temperature):
return temperature
class Fahrenheit(object):
"""
Convertor for fahrenheit scale.
"""
def convert_to(self, temperature):
"""
Convert celsius input to fahrenheit
"""
return (temperature * 1.8) + 32.0
class Kelvin(object):
"""
Convertor for the kelvin temperature scale.
"""
def convert_to(self, temperature):
"""
Convert celsius input to kelvin.
"""
return temperature + 273.15
class AdjustedKiloPascals(object):
"""
Convertor for KiloPascals, adjusted by some amount (most usefully, to sea-level).
"""
def __init__(self, adjustment=None):
self.adjustment = adjustment
def convert_to(self, pressure):
"""
Convert kPa to... adjusted kPa.
"""
if self.adjustment is None:
return pressure
return pressure + self.adjustment
class HectoPascals(AdjustedKiloPascals):
"""
Pressure convertor for HectoPascals.
"""
def __init__(self, adjustment=None):
if adjustment is not None:
adjustment /= 10.0
super().__init__(adjustment)
def convert_to(self, pressure):
"""
Convert kPa to hPa.
"""
adjusted = super().convert_to(pressure)
return adjusted * 10.0
class Atmospheres(AdjustedKiloPascals):
"""
Pressure convertor for Atmospheres.
"""
def __init__(self, adjustment=None):
if adjustment is not None:
adjustment /= 0.009869233
super().__init__(adjustment)
def convert_to(self, pressure):
"""
Convert kPa to Atm.
"""
adjusted = super().convert_to(pressure)
return adjusted * 0.009869233
class PSI(AdjustedKiloPascals):
"""
Pressure convertor for PSI.
"""
def __init__(self, adjustment=None):
if adjustment is not None:
adjustment /= 0.14503773801
super().__init__(adjustment)
def convert_to(self, pressure):
"""
Convert kPa to PSI.
"""
adjusted = super().convert_to(pressure)
return adjusted * 0.14503773801
class Bars(AdjustedKiloPascals):
"""
Pressure convertor for Bars.
"""
def __init__(self, adjustment=None):
if adjustment is not None:
adjustment /= 0.01
super().__init__(adjustment)
def convert_to(self, pressure):
"""
Convert kPa to Bars.
"""
adjusted = super().convert_to(pressure)
return adjusted * 0.01
class Mpl115A2(object):
"""
Reads temperature and pressure from a Freescale MPL115A2 sensor over I2C.
See the module docstring for usage information.
"""
def __init__(
self,
bus,
shutdown_pin=None,
reset_pin=None,
temperature_convertor=None,
pressure_convertor=None,
**kwargs
):
"""
Create the device on the specified bus.
The bus must be a pyb.I2C object in master mode, or an object implementing
the same interface.
pyb.Pin objects can be provided for controlling the shutdown and reset pins
of the sensor. They must be in output mode. The pins can also be specified
as strings (e.g. 'X9'), for which Pin objects will be created and configured.
Temperature and pressure convertor objects can be passed which will convert
from celsius and kilopascals as necessary if you want to work in a different scale.
"""
# There doesn't seem to be a way to check this at present. The first
# send or recv should throw an error instead if the mode is incorrect.
#if not bus.in_master_mode():
# raise ValueError('bus must be in master mode')
self.bus = bus
self.address = 0x60
self.shutdown_pin = shutdown_pin
if self.shutdown_pin is not None:
if isinstance(self.shutdown_pin, str):
from pyb import Pin
# Not sure what are the appropriate settings here...
self.shutdown_pin = Pin(
self.shutdown_pin,
Pin.OUT_PP,
Pin.PULL_UP
)
if 'shutdown' in kwargs:
self.shutdown_pin.value(not kwargs['shutdown'])
else:
self.shutdown_pin.high()
self.reset_pin = reset_pin
if self.reset_pin is not None:
if isinstance(self.reset_pin, str):
from pyb import Pin
# Not sure what are the appropriate settings here...
self.reset_pin = Pin(
self.reset_pin,
Pin.OUT_PP,
Pin.PULL_UP
)
if 'reset' in kwargs:
self.reset_pin.value(not kwargs['reset'])
else:
self.reset_pin.high()
self.temperature_convertor = temperature_convertor
self.pressure_convertor = pressure_convertor
# Coefficients for compensation calculations - will be set on first
# attempt to read pressure or temperature
self._a0 = None
self._b1 = None
self._b2 = None
self._c12 = None
def _send_command(self, command, value=None):
bvals = bytearray()
bvals.append(command)
if value is not None:
for val in value:
bvals.append(val)
self.bus.send(bvals, addr=self.address)
self._last_command = command
def _read_coefficients(self):
self._send_command(0x04)
coefficients = self.bus.recv(8, addr=self.address)
self._a0 = float(_parse_signed(coefficients[0], coefficients[1])) / 8.0
self._b1 = float(_parse_signed(coefficients[2], coefficients[3])) / 8192.0
self._b2 = float(_parse_signed(coefficients[4], coefficients[5])) / 16384.0
self._c12 = float(_parse_signed(coefficients[6], coefficients[7]) >> 2) / 4194304.0
def _read_raw_pressure(self):
self._send_command(0x00)
rp = self.bus.recv(2, addr=self.address)
return int((rp[0] << 8 | rp[1]) >> 6)
def _read_raw_temperature(self):
self._send_command(0x02)
rt = self.bus.recv(2, addr=self.address)
return int((rt[0] << 8 | rt[1]) >> 6)
def initiate_conversion(self):
self._send_command(0x12, (0x00,))
@property
def pressure(self):
if self._a0 is None:
self._read_coefficients()
raw_pressure = self._read_raw_pressure()
raw_temp = self._read_raw_temperature()
compensated = (((self._b1 + (self._c12 * raw_temp)) * raw_pressure) + self._a0) + (self._b2 * raw_temp)
kpa = (compensated * (65.0 / 1023.0)) + 50.0
if self.pressure_convertor is None:
return kpa
return self.pressure_convertor.convert_to(kpa)
@property
def temperature(self):
if self._a0 is None:
self._read_coefficients()
raw_temp = self._read_raw_temperature()
celsius = ((float(raw_temp) - 498.0) / -5.35) + 25.0
if self.temperature_convertor is None:
return celsius
return self.temperature_convertor.convert_to(celsius)
def _set_shutdown(self, value):
if self.shutdown_pin is None:
raise Exception("No shutdown pin has been set")
if not value:
self._last_wake_millis = pyb.millis()
self.shutdown_pin.value(not value)
def _get_shutdown(self):
if self.shutdown_pin is None:
raise Exception("No shutdown pin has been set")
return not self.shutdown_pin.value()
shutdown = property(_get_shutdown, _set_shutdown)
def _set_reset(self, value):
if self.reset_pin is None:
raise Exception("No reset pin has been set")
if not value:
self._last_wake_millis = pyb.millis()
self.reset_pin.value(not value)
def _get_reset(self):
if self.reset_pin is None:
raise Exception("No reset pin has been set")
return not self.reset_pin.value()
reset = property(_get_reset, _set_reset)