Source code for lps28

# SPDX-FileCopyrightText: Copyright (c) 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`lps28`
================================================================================

LPS28 pressure sensor drive for CircuitPython


* Author(s): Jose D. Montoya


"""

from micropython import const
from adafruit_bus_device import i2c_device
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct
from adafruit_register.i2c_bits import RWBits, ROBits
from adafruit_register.i2c_bit import RWBit

try:
    from busio import I2C
except ImportError:
    pass


__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/jposada202020/CircuitPython_LPS28.git"


_REG_WHOAMI = const(0x0F)
_INTERRUPT_CFG = const(0x0B)
_CTRL_REG1 = const(0x10)
_CTRL_REG2 = const(0x11)
_INT_SOURCE = const(0x24)
_PRESS_OUT_XL = const(0x28)
_TEMP_OUT_L = const(0x2B)
_THS_P_L = const(0x0C)


# Data Rate
ONE_SHOT = const(0b0000)
RATE_1_HZ = const(0b0001)
RATE_4_HZ = const(0b0010)
RATE_10_HZ = const(0b0011)
RATE_25_HZ = const(0b0100)
RATE_50_HZ = const(0b0101)
RATE_75_HZ = const(0b0110)
RATE_100_HZ = const(0b0111)
RATE_200_HZ = const(0b1000)
data_rate_values = (
    ONE_SHOT,
    RATE_1_HZ,
    RATE_4_HZ,
    RATE_10_HZ,
    RATE_25_HZ,
    RATE_50_HZ,
    RATE_75_HZ,
    RATE_100_HZ,
    RATE_200_HZ,
)

# Resolution
RES_4 = const(0b000)
RES_8 = const(0b001)
RES_16 = const(0b010)
RES_32 = const(0b011)
RES_64 = const(0b100)
RES_128 = const(0b101)
RES_512 = const(0b111)
resolution_values = (RES_4, RES_8, RES_16, RES_32, RES_64, RES_128, RES_512)

FULL_SCALE = const(0b1)
NORMAL = const(0b0)
full_scale_values = (NORMAL, FULL_SCALE)


[docs] class LPS28: """Driver for the LPS28 Sensor connected over I2C. :param ~busio.I2C i2c_bus: The I2C bus the LPS28 is connected to. :param int address: The I2C device address. Defaults to :const:`0x5D` :raises RuntimeError: if the sensor is not found **Quickstart: Importing and using the device** Here is an example of using the :class:`LPS28` class. First you will need to import the libraries to use the sensor .. code-block:: python import board import lps28 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 lps28 = LPS28.lps28(i2c) Now you have access to the attributes .. code-block:: python press = lps28.pressure """ _device_id = ROUnaryStruct(_REG_WHOAMI, "B") _raw_temperature = ROUnaryStruct(_TEMP_OUT_L, "<h") _raw_pressure = ROBits(24, _PRESS_OUT_XL, 0, 3) _pressure_threshold = UnaryStruct(_THS_P_L, "<H") # INT_SOURCE(0x24) # | BOOT-ON | 0 | 0 | 0 | 0 | IA | PL | PH | _pressure_low = RWBit(_INTERRUPT_CFG, 1) _pressure_high = RWBit(_INTERRUPT_CFG, 0) # INT status registers (0xB0) # | AUTOREFP | RESET_ARP | AUTOZERO | RESET_AZ | ---- | LIR | PLE | PHE | _interrupt_low = RWBit(_INTERRUPT_CFG, 1) _interrupt_high = RWBit(_INTERRUPT_CFG, 0) # Register CTRL_REG1 (0x10) # | ---- | ODR(3) | ODR(2) | ODR(1) | ODR(0) | AVG(2) | AVG(1) | AVG(0)| _resolution = RWBits(3, _CTRL_REG1, 0) _data_rate = RWBits(4, _CTRL_REG1, 3) # Register CTRL_REG2 (0x11) # | BOOT | FS_MODE | LFPF_CFG | EN_LPFP | BDU | SWRESET | ---- | ONESHOOT| _full_scale = RWBits(1, _CTRL_REG2, 6) def __init__(self, i2c_bus: I2C, address: int = 0x5D) -> None: self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) if self._device_id != 0xB4: raise RuntimeError("Failed to find LPS28") self._data_rate = RATE_10_HZ self._pressure_scale = 4096 @property def data_rate(self) -> str: """ Sensor data_rate +-------------------------------+--------------------+ | Mode | Value | +===============================+====================+ | :py:const:`lps28.ONE_SHOT` | :py:const:`0b0000` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_1_HZ` | :py:const:`0b0001` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_4_HZ` | :py:const:`0b0010` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_10_HZ` | :py:const:`0b0011` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_25_HZ` | :py:const:`0b0100` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_50_HZ` | :py:const:`0b0101` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_75_HZ` | :py:const:`0b0110` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_100_HZ` | :py:const:`0b0111` | +-------------------------------+--------------------+ | :py:const:`lps28.RATE_200_HZ` | :py:const:`0b1000` | +-------------------------------+--------------------+ """ values = ( "ONE_SHOT", "RATE_1_HZ", "RATE_4_HZ", "RATE_10_HZ", "RATE_25_HZ", "RATE_50_HZ", "RATE_75_HZ", "RATE_100_HZ", "RATE_200_HZ", ) return values[self._data_rate] @data_rate.setter def data_rate(self, value: int) -> None: if value not in data_rate_values: raise ValueError("Value must be a valid data_rate setting") self._data_rate = value @property def resolution(self) -> None: """ Sensor resolution +---------------------------+-------------------+ | Mode | Value | +===========================+===================+ | :py:const:`lps28.RES_4` | :py:const:`0b000` | +---------------------------+-------------------+ | :py:const:`lps28.RES_8` | :py:const:`0b001` | +---------------------------+-------------------+ | :py:const:`lps28.RES_16` | :py:const:`0b010` | +---------------------------+-------------------+ | :py:const:`lps28.RES_32` | :py:const:`0b011` | +---------------------------+-------------------+ | :py:const:`lps28.RES_64` | :py:const:`0b100` | +---------------------------+-------------------+ | :py:const:`lps28.RES_128` | :py:const:`0b101` | +---------------------------+-------------------+ | :py:const:`lps28.RES_512` | :py:const:`0b111` | +---------------------------+-------------------+ """ values = ( "RES_4", "RES_8", "RES_16", "RES_32", "RES_64", "RES_128", "RES_512", ) return values[self._resolution] @resolution.setter def resolution(self, value: int) -> None: if value not in resolution_values: raise ValueError("Value must be a valid resolution setting") self._resolution = value @property def full_scale(self) -> None: """ Sensor full_scale (0: mode 1, full scale up to 1260 hPa; 1: mode 2, full scale up to 4060 hPa) +--------------------------------+-------------------+ | Mode | Value | +================================+===================+ | :py:const:`lps28.FULL_SCALE` | :py:const:`0b1` | +--------------------------------+-------------------+ | :py:const:`lps28.NORMAL` | :py:const:`0b0` | +--------------------------------+-------------------+ """ values = ("NORMAL", "FULL_SCALE") return values[self._resolution] @full_scale.setter def full_scale(self, value: int) -> None: if value not in full_scale_values: raise ValueError("Value must be a valid scale setting") options = (4096, 2048) self._full_scale = options[value] @property def pressure(self) -> float: """ Pressure value in hPa """ pressure_reading = self._raw_pressure raw_pressure = self._twos_comp(pressure_reading, 24) / self._pressure_scale return raw_pressure / 100 @property def temperature(self) -> float: """The current temperature measurement in Celsius""" return self._raw_temperature / 100 @property def high_threshold_enabled(self) -> bool: """Set to `True` or `False` to enable or disable the high pressure threshold""" value = {0: False, 1: True} return value[self._interrupt_high] @high_threshold_enabled.setter def high_threshold_enabled(self, value: bool) -> None: if value not in (True, False): raise ValueError("value must be a valid setting") self._interrupt_high = value @property def low_threshold_enabled(self) -> bool: """Set to `True` or `False` to enable or disable the low pressure threshold.""" value = {0: False, 1: True} return value[self._interrupt_low] @low_threshold_enabled.setter def low_threshold_enabled(self, value: bool) -> None: if value not in (True, False): raise ValueError("value must be a valid setting") self._interrupt_low = value @property def high_threshold_exceeded(self) -> bool: """Returns `True` if the pressure high threshold has been exceeded. Must be enabled by setting :attr:`high_threshold_enabled` to `True` and setting a :attr:`pressure_threshold`.""" return self._pressure_high @property def low_threshold_exceeded(self) -> bool: """Returns `True` if the pressure low threshold has been exceeded. Must be enabled by setting :attr:`high_threshold_enabled` to `True` and setting a :attr:`pressure_threshold`.""" return self._pressure_low @property def pressure_threshold(self) -> float: """The high pressure threshold. Use :attr:`high_threshold_enabled` or :attr:`high_threshold_enabled` to use it""" options = (16, 8) return self._pressure_threshold / options[self._full_scale] @pressure_threshold.setter def pressure_threshold(self, value: float) -> None: """The high value threshold""" options = (16, 8) self._pressure_threshold = value * options[self._full_scale] @staticmethod def _twos_comp(val: int, bits: int) -> int: if val & (1 << (bits - 1)) != 0: return val - (1 << bits) return val