/ adafruit_mpl3115a2.py
adafruit_mpl3115a2.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2017 Tony DiCola for Adafruit Industries
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  """
 23  `adafruit_mpl3115a2`
 24  ====================================================
 25  
 26  CircuitPython module for the MPL3115A2 barometric pressure & temperature sensor.
 27  See examples/simpletest.py for a demo of the usage.
 28  
 29  * Author(s): Tony DiCola
 30  """
 31  import time
 32  
 33  from micropython import const
 34  
 35  try:
 36      import ustruct as struct
 37  except ImportError:
 38      import struct
 39  
 40  import adafruit_bus_device.i2c_device as i2c_device
 41  
 42  
 43  __version__ = "0.0.0-auto.0"
 44  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MPL3115A2.git"
 45  
 46  
 47  # pylint: disable=bad-whitespace
 48  # Internal constants:
 49  _MPL3115A2_ADDRESS = const(0x60)
 50  _MPL3115A2_REGISTER_STATUS = const(0x00)
 51  _MPL3115A2_REGISTER_PRESSURE_MSB = const(0x01)
 52  _MPL3115A2_REGISTER_PRESSURE_CSB = const(0x02)
 53  _MPL3115A2_REGISTER_PRESSURE_LSB = const(0x03)
 54  _MPL3115A2_REGISTER_TEMP_MSB = const(0x04)
 55  _MPL3115A2_REGISTER_TEMP_LSB = const(0x05)
 56  _MPL3115A2_REGISTER_DR_STATUS = const(0x06)
 57  _MPL3115A2_OUT_P_DELTA_MSB = const(0x07)
 58  _MPL3115A2_OUT_P_DELTA_CSB = const(0x08)
 59  _MPL3115A2_OUT_P_DELTA_LSB = const(0x09)
 60  _MPL3115A2_OUT_T_DELTA_MSB = const(0x0A)
 61  _MPL3115A2_OUT_T_DELTA_LSB = const(0x0B)
 62  _MPL3115A2_WHOAMI = const(0x0C)
 63  _MPL3115A2_BAR_IN_MSB = const(0x14)
 64  _MPL3115A2_BAR_IN_LSB = const(0x15)
 65  
 66  _MPL3115A2_REGISTER_STATUS_TDR = const(0x02)
 67  _MPL3115A2_REGISTER_STATUS_PDR = const(0x04)
 68  _MPL3115A2_REGISTER_STATUS_PTDR = const(0x08)
 69  
 70  _MPL3115A2_PT_DATA_CFG = const(0x13)
 71  _MPL3115A2_PT_DATA_CFG_TDEFE = const(0x01)
 72  _MPL3115A2_PT_DATA_CFG_PDEFE = const(0x02)
 73  _MPL3115A2_PT_DATA_CFG_DREM = const(0x04)
 74  
 75  _MPL3115A2_CTRL_REG1 = const(0x26)
 76  _MPL3115A2_CTRL_REG2 = const(0x27)
 77  _MPL3115A2_CTRL_REG3 = const(0x28)
 78  _MPL3115A2_CTRL_REG4 = const(0x29)
 79  _MPL3115A2_CTRL_REG5 = const(0x2A)
 80  
 81  _MPL3115A2_CTRL_REG1_SBYB = const(0x01)
 82  _MPL3115A2_CTRL_REG1_OST = const(0x02)
 83  _MPL3115A2_CTRL_REG1_RST = const(0x04)
 84  _MPL3115A2_CTRL_REG1_RAW = const(0x40)
 85  _MPL3115A2_CTRL_REG1_ALT = const(0x80)
 86  _MPL3115A2_CTRL_REG1_BAR = const(0x00)
 87  
 88  _MPL3115A2_CTRL_REG1_OS1 = const(0x00)
 89  _MPL3115A2_CTRL_REG1_OS2 = const(0x08)
 90  _MPL3115A2_CTRL_REG1_OS4 = const(0x10)
 91  _MPL3115A2_CTRL_REG1_OS8 = const(0x18)
 92  _MPL3115A2_CTRL_REG1_OS16 = const(0x20)
 93  _MPL3115A2_CTRL_REG1_OS32 = const(0x28)
 94  _MPL3115A2_CTRL_REG1_OS64 = const(0x30)
 95  _MPL3115A2_CTRL_REG1_OS128 = const(0x38)
 96  
 97  _MPL3115A2_REGISTER_STARTCONVERSION = const(0x12)
 98  # pylint: enable=bad-whitespace
 99  
100  
101  class MPL3115A2:
102      """Instance of the MPL3115A2 sensor.  Must specify the following parameters
103      when creating an instance of this device:
104      - i2c: The I2C bus connected to the sensor.
105  
106      In addition you can specify the following optional keyword arguments:
107      - address: The I2C address of the device if it's different from the default.
108      """
109  
110      # Class level buffer to reduce memory usage and allocations.
111      # Note this is not thread safe by design!
112      _BUFFER = bytearray(4)
113  
114      def __init__(self, i2c, *, address=_MPL3115A2_ADDRESS):
115          self._device = i2c_device.I2CDevice(i2c, address)
116          # Validate the chip ID.
117          if self._read_u8(_MPL3115A2_WHOAMI) != 0xC4:
118              raise RuntimeError("Failed to find MPL3115A2, check your wiring!")
119          # Reset.  Note the chip immediately resets and won't send an I2C back
120          # so we need to catch the OSError and swallow it (otherwise this fails
121          # expecting an ACK that never comes).
122          try:
123              self._write_u8(_MPL3115A2_CTRL_REG1, _MPL3115A2_CTRL_REG1_RST)
124          except OSError:
125              pass
126          time.sleep(0.01)
127          # Poll for the reset to finish.
128          self._poll_reg1(_MPL3115A2_CTRL_REG1_RST)
129          # Configure the chip registers with default values.
130          self._ctrl_reg1 = _MPL3115A2_CTRL_REG1_OS128 | _MPL3115A2_CTRL_REG1_ALT
131          self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
132          self._write_u8(
133              _MPL3115A2_PT_DATA_CFG,
134              _MPL3115A2_PT_DATA_CFG_TDEFE
135              | _MPL3115A2_PT_DATA_CFG_PDEFE
136              | _MPL3115A2_PT_DATA_CFG_DREM,
137          )
138  
139      def _read_into(self, address, buf, count=None):
140          # Read bytes from the specified 8-bit address into the provided buffer.
141          # If the count is not specified then the entire buffer is filled,
142          # otherwise count bytes are copied in.
143          if count is None:
144              count = len(buf)
145          with self._device as i2c:
146              i2c.write_then_readinto(bytes([address & 0xFF]), buf, in_end=count)
147  
148      def _read_u8(self, address):
149          # Read an 8-bit unsigned value from the specified 8-bit address.
150          self._read_into(address, self._BUFFER, count=1)
151          return self._BUFFER[0]
152  
153      def _write_u8(self, address, val):
154          # Write an 8-bit unsigned value to the specified 8-bit address.
155          with self._device as i2c:
156              self._BUFFER[0] = address & 0xFF
157              self._BUFFER[1] = val & 0xFF
158              i2c.write(self._BUFFER, end=2)
159  
160      def _write_u16_be(self, address, val):
161          # Write a 16-bit big endian unsigned value to the specified 8-bit
162          # address.
163          with self._device as i2c:
164              self._BUFFER[0] = address & 0xFF
165              self._BUFFER[1] = (val >> 8) & 0xFF
166              self._BUFFER[2] = val & 0xFF
167              i2c.write(self._BUFFER, end=3)
168  
169      def _poll_reg1(self, mask):
170          # Poll the CTRL REG1 value for the specified masked bits to NOT be
171          # present.
172          while self._read_u8(_MPL3115A2_CTRL_REG1) & mask > 0:
173              time.sleep(0.01)
174  
175      @property
176      def pressure(self):
177          """Read the barometric pressure detected by the sensor in Pascals."""
178          # First poll for a measurement to be finished.
179          self._poll_reg1(_MPL3115A2_CTRL_REG1_OST)
180          # Set control bits for pressure reading.
181          self._ctrl_reg1 &= ~0b10000000  # Turn off bit 7, ALT.
182          self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
183          self._ctrl_reg1 |= 0b00000010  # Set OST to 1 to start measurement.
184          self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
185          # Poll status for PDR to be set.
186          while (
187              self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_PDR
188              == 0
189          ):
190              time.sleep(0.01)
191          # Read 3 bytes of pressure data into buffer.
192          self._read_into(_MPL3115A2_REGISTER_PRESSURE_MSB, self._BUFFER, count=3)
193          # Reconstruct 20-bit pressure value.
194          pressure = (
195              (self._BUFFER[0] << 16) | (self._BUFFER[1] << 8) | self._BUFFER[2]
196          ) & 0xFFFFFF
197          pressure >>= 4
198          # Scale down to pascals.
199          return pressure / 4.0
200  
201      @property
202      def altitude(self):
203          """Read the altitude as calculated based on the sensor pressure and
204          previously configured pressure at sea-level.  This will return a
205          value in meters.  Set the sea-level pressure by updating the
206          sealevel_pressure property first to get a more accurate altitude value.
207          """
208          # First poll for a measurement to be finished.
209          self._poll_reg1(_MPL3115A2_CTRL_REG1_OST)
210          # Set control bits for pressure reading.
211          self._ctrl_reg1 |= 0b10000000  # Turn on bit 0, ALT.
212          self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
213          self._ctrl_reg1 |= 0b00000010  # Set OST to 1 to start measurement.
214          self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
215          # Poll status for PDR to be set.
216          while (
217              self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_PDR
218              == 0
219          ):
220              time.sleep(0.01)
221          # Read 3 bytes of altitude data into buffer.
222          # Yes even though this is the address of the pressure register it
223          # returns altitude when the ALT bit is set above.
224          self._read_into(_MPL3115A2_REGISTER_PRESSURE_MSB, self._BUFFER, count=3)
225          # Reconstruct signed 32-bit altitude value (actually 24 bits shifted up
226          # and then scaled down).
227          self._BUFFER[3] = 0  # Top 3 bytes of buffer were read from the chip.
228          altitude = struct.unpack(">i", self._BUFFER[0:4])[0]
229          # Scale down to meters.
230          return altitude / 65535.0
231  
232      @property
233      def temperature(self):
234          """Read the temperature as measured by the sensor in degrees Celsius.
235          """
236          # Poll status for TDR to be set.
237          while (
238              self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_TDR
239              == 0
240          ):
241              time.sleep(0.01)
242          # Read 2 bytes of data from temp register.
243          self._read_into(_MPL3115A2_REGISTER_TEMP_MSB, self._BUFFER, count=2)
244          # Reconstruct signed 12-bit value.
245          temperature = struct.unpack(">h", self._BUFFER[0:2])[0]
246          temperature >>= 4
247          # Scale down to degrees Celsius.
248          return temperature / 16.0
249  
250      @property
251      def sealevel_pressure(self):
252          """Read and write the pressure at sea-level used to calculate altitude.
253          You must look this up from a local weather or meteorlogical report for
254          the best accuracy.  This is a value in Pascals.
255          """
256          # Read the sea level pressure in bars.
257          self._read_into(_MPL3115A2_BAR_IN_MSB, self._BUFFER, count=2)
258          # Reconstruct 16-bit value and scale back to pascals.
259          pressure = (self._BUFFER[0] << 8) | self._BUFFER[1]
260          return pressure * 2.0
261  
262      @sealevel_pressure.setter
263      def sealevel_pressure(self, val):
264          # Convert to bars of pressure and write to the sealevel register.
265          bars = val // 2
266          self._write_u16_be(_MPL3115A2_BAR_IN_MSB, bars)