Skip to content

Instantly share code, notes, and snippets.

@jposada202020
Created March 4, 2023 22:21
Show Gist options
  • Select an option

  • Save jposada202020/a9f5967c430a4e6e2f85b0ec6eb46a4d to your computer and use it in GitHub Desktop.

Select an option

Save jposada202020/a9f5967c430a4e6e2f85b0ec6eb46a4d to your computer and use it in GitHub Desktop.
Not so good version
# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_mpl3115a2`
====================================================
CircuitPython module for the MPL3115A2 barometric pressure & temperature sensor.
* Author(s): Tony DiCola
Implementation Notes
--------------------
**Hardware:**
* `Adafruit MPL3115A2 - I2C Barometric Pressure/Altitude/Temperature Sensor
<https://www.adafruit.com/product/1893>`_
**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 micropython import const
from adafruit_bus_device import i2c_device
__version__ = "2.0.4"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MPL3115A2.git"
# Internal constants:
_MPL3115A2_ADDRESS = const(0x60)
_MPL3115A2_REGISTER_STATUS = const(0x00)
_MPL3115A2_REGISTER_PRESSURE_MSB = const(0x01)
_MPL3115A2_REGISTER_PRESSURE_CSB = const(0x02)
_MPL3115A2_REGISTER_PRESSURE_LSB = const(0x03)
_MPL3115A2_REGISTER_TEMP_MSB = const(0x04)
_MPL3115A2_REGISTER_TEMP_LSB = const(0x05)
_MPL3115A2_REGISTER_DR_STATUS = const(0x06)
_MPL3115A2_OUT_P_DELTA_MSB = const(0x07)
_MPL3115A2_OUT_P_DELTA_CSB = const(0x08)
_MPL3115A2_OUT_P_DELTA_LSB = const(0x09)
_MPL3115A2_OUT_T_DELTA_MSB = const(0x0A)
_MPL3115A2_OUT_T_DELTA_LSB = const(0x0B)
_MPL3115A2_WHOAMI = const(0x0C)
_MPL3115A2_BAR_IN_MSB = const(0x14)
_MPL3115A2_BAR_IN_LSB = const(0x15)
_MPL3115A2_REGISTER_STATUS_TDR = const(0x02)
_MPL3115A2_REGISTER_STATUS_PDR = const(0x04)
_MPL3115A2_REGISTER_STATUS_PTDR = const(0x08)
_MPL3115A2_PT_DATA_CFG = const(0x13)
_MPL3115A2_PT_DATA_CFG_TDEFE = const(0x01)
_MPL3115A2_PT_DATA_CFG_PDEFE = const(0x02)
_MPL3115A2_PT_DATA_CFG_DREM = const(0x04)
_MPL3115A2_CTRL_REG1 = const(0x26)
_MPL3115A2_CTRL_REG2 = const(0x27)
_MPL3115A2_CTRL_REG3 = const(0x28)
_MPL3115A2_CTRL_REG4 = const(0x29)
_MPL3115A2_CTRL_REG5 = const(0x2A)
_MPL3115A2_CTRL_REG1_SBYB = const(0x01)
_MPL3115A2_CTRL_REG1_OST = const(0x02)
_MPL3115A2_CTRL_REG1_RST = const(0x04)
_MPL3115A2_CTRL_REG1_RAW = const(0x40)
_MPL3115A2_CTRL_REG1_ALT = const(0x80)
_MPL3115A2_CTRL_REG1_BAR = const(0x00)
_MPL3115A2_CTRL_REG1_OS1 = const(0x00)
_MPL3115A2_CTRL_REG1_OS2 = const(0x08)
_MPL3115A2_CTRL_REG1_OS4 = const(0x10)
_MPL3115A2_CTRL_REG1_OS8 = const(0x18)
_MPL3115A2_CTRL_REG1_OS16 = const(0x20)
_MPL3115A2_CTRL_REG1_OS32 = const(0x28)
_MPL3115A2_CTRL_REG1_OS64 = const(0x30)
_MPL3115A2_CTRL_REG1_OS128 = const(0x38)
_MPL3115A2_REGISTER_STARTCONVERSION = const(0x12)
class MPL3115A2:
"""Instance of the MPL3115A2 sensor.
:param ~busio.I2C i2c: The I2C bus the MPL3115A2 is connected to.
:param int address: The I2C device address. Defaults to :const:`0x60`
**Quickstart: Importing and using the MPL3115A2**
Here is an example of using the :class:`MPL3115A2` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
import board
import adafruit_mpl3115a2
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 = adafruit_mpl3115a2.MPL3115A2(i2c)
Now you have access to the :attr:`temperature`, :attr:`pressure`
and :attr:`altitude` attributes
.. code-block:: python
temperature = sensor.temperature
pressure = sensor.pressure
altitude = sensor.altitude
"""
# Class level buffer to reduce memory usage and allocations.
# Note this is not thread safe by design!
_BUFFER = bytearray(4)
def __init__(self, i2c, *, address=_MPL3115A2_ADDRESS):
self._device = i2c_device.I2CDevice(i2c, address)
# Validate the chip ID.
if self._read_u8(_MPL3115A2_WHOAMI) != 0xC4:
raise RuntimeError("Failed to find MPL3115A2, check your wiring!")
# Reset. Note the chip immediately resets and won't send an I2C back
# so we need to catch the OSError and swallow it (otherwise this fails
# expecting an ACK that never comes).
try:
self._write_u8(_MPL3115A2_CTRL_REG1, _MPL3115A2_CTRL_REG1_RST)
except OSError:
pass
time.sleep(0.01)
# Poll for the reset to finish.
# try/except is a hack for RP2040
try:
self._poll_reg1(_MPL3115A2_CTRL_REG1_RST)
except OSError:
self._poll_reg1(_MPL3115A2_CTRL_REG1_RST)
# Configure the chip registers with default values.
self._ctrl_reg1 = _MPL3115A2_CTRL_REG1_OS128 | _MPL3115A2_CTRL_REG1_ALT
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
self._write_u8(
_MPL3115A2_PT_DATA_CFG,
_MPL3115A2_PT_DATA_CFG_TDEFE
| _MPL3115A2_PT_DATA_CFG_PDEFE
| _MPL3115A2_PT_DATA_CFG_DREM,
)
def _read_into(self, address, buf, count=None):
# Read bytes from the specified 8-bit address into the provided buffer.
# If the count is not specified then the entire buffer is filled,
# otherwise count bytes are copied in.
if count is None:
count = len(buf)
with self._device as i2c:
i2c.write_then_readinto(bytes([address & 0xFF]), buf, in_end=count)
def _read_u8(self, address):
# Read an 8-bit unsigned value from the specified 8-bit address.
self._read_into(address, self._BUFFER, count=1)
return self._BUFFER[0]
def _write_u8(self, address, val):
# Write an 8-bit unsigned value to the specified 8-bit address.
with self._device as i2c:
self._BUFFER[0] = address & 0xFF
self._BUFFER[1] = val & 0xFF
i2c.write(self._BUFFER, end=2)
def _write_u16_be(self, address, val):
# Write a 16-bit big endian unsigned value to the specified 8-bit
# address.
with self._device as i2c:
self._BUFFER[0] = address & 0xFF
self._BUFFER[1] = (val >> 8) & 0xFF
self._BUFFER[2] = val & 0xFF
i2c.write(self._BUFFER, end=3)
def _poll_reg1(self, mask):
# Poll the CTRL REG1 value for the specified masked bits to NOT be
# present.
while self._read_u8(_MPL3115A2_CTRL_REG1) & mask > 0:
time.sleep(0.05)
@property
def pressure(self):
"""Read the barometric pressure detected by the sensor in Pascals."""
# First poll for a measurement to be finished.
self._poll_reg1(_MPL3115A2_CTRL_REG1_OST)
# Set control bits for pressure reading.
self._ctrl_reg1 &= ~0b10000000 # Turn off bit 7, ALT.
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
self._ctrl_reg1 |= 0b00000010 # Set OST to 1 to start measurement.
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
# Poll status for PDR to be set = press conversion complete
while (
self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_PDR
== 0
):
time.sleep(0.01)
# Read 3 bytes of pressure data into buffer.
self._read_into(_MPL3115A2_REGISTER_PRESSURE_MSB, self._BUFFER, count=3)
# Reconstruct 20-bit pressure value.
pressure = (
(self._BUFFER[0] << 16) | (self._BUFFER[1] << 8) | self._BUFFER[2]
) & 0xFFFFFF
pressure >>= 4
return pressure / 400.0
@property
def temperature(self):
"""Read the temperature as measured by the sensor in degrees Celsius."""
# First poll for a measurement to be finished.
self._poll_reg1(_MPL3115A2_CTRL_REG1_OST)
# # Set control bits for pressure reading.
self._ctrl_reg1 &= ~0b10000000 # Turn off bit 7, ALT
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
# Initatiate a one-shot measurement
self._ctrl_reg1 |= 0b00000010 # Set OST to 1 to start measurement.
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
# Poll status for TDR to be set = temp conv complete
while (
self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_TDR
== 0
):
time.sleep(0.01)
# Read 2 bytes of data from temp register.
self._read_into(_MPL3115A2_REGISTER_TEMP_MSB, self._BUFFER, count=2)
# Reconstruct signed 12-bit value.
temperature = struct.unpack(">h", self._BUFFER[0:2])[0]
temperature >>= 4
return temperature / 16.0
@property
def sealevel_pressure(self):
"""Read and write the pressure at sea-level used to calculate altitude.
You must look this up from a local weather or meteorological report for
the best accuracy. This is a value in Pascals.
"""
# Read the sea level pressure in bars.
self._read_into(_MPL3115A2_BAR_IN_MSB, self._BUFFER, count=2)
# Reconstruct 16-bit value and scale back to pascals.
pressure = (self._BUFFER[0] << 8) | self._BUFFER[1]
return pressure * 2.0
@sealevel_pressure.setter
def sealevel_pressure(self, val):
# Convert to bars of pressure and write to the sealevel register.
bars = val // 2
self._write_u16_be(_MPL3115A2_BAR_IN_MSB, bars)
@property
def altitude(self):
"""Read the altitude as calculated based on the sensor pressure and
previously configured pressure at sea-level. This will return a
value in meters. Set the sea-level pressure by updating the
:attr:`sealevel_pressure` property first to get a more accurate altitude value.
"""
self._ctrl_reg1 = _MPL3115A2_CTRL_REG1_OS128 | _MPL3115A2_CTRL_REG1_ALT
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
self._write_u8(
_MPL3115A2_PT_DATA_CFG,
_MPL3115A2_PT_DATA_CFG_TDEFE
| _MPL3115A2_PT_DATA_CFG_PDEFE
| _MPL3115A2_PT_DATA_CFG_DREM,
)
# First poll for a measurement to be finished.
self._poll_reg1(_MPL3115A2_CTRL_REG1_OST)
self._ctrl_reg1 |= 0b10000000 # Turn on bit 0, ALT.
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
self._ctrl_reg1 |= 0b00000010 # Set OST to 1 to start measurement.
self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1)
# Poll status for PDR to be set.
while (
self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_PDR
== 0
):
time.sleep(0.01)
# Read 3 bytes of altitude data into buffer.
# Yes even though this is the address of the pressure register it
# returns altitude when the ALT bit is set above.
self._read_into(_MPL3115A2_REGISTER_PRESSURE_MSB, self._BUFFER, count=3)
# Reconstruct signed 32-bit altitude value (actually 24 bits shifted up
# and then scaled down).
self._BUFFER[3] = 0 # Top 3 bytes of buffer were read from the chip.
altitude = struct.unpack(">i", self._BUFFER[0:4])[0]
# Scale down to meters.
return altitude / 65535.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment