Source code for bmp581

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

CircuitPython Driver for the Bosch BMP581 pressure sensor


* Author(s): Jose D. Montoya


"""

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

try:
    from busio import I2C
except ImportError:
    pass

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

_REG_WHOAMI = const(0x01)
_OSR_CONF = const(0x36)
_ODR_CONFIG = const(0x37)


# Power Modes
STANDBY = const(0x00)
NORMAL = const(0x01)
FORCED = const(0x02)
NON_STOP = const(0x03)
power_mode_values = (STANDBY, NORMAL, FORCED, NON_STOP)


# Oversample Rate
OSR1 = const(0x00)
OSR2 = const(0x01)
OSR4 = const(0x02)
OSR8 = const(0x03)
OSR16 = const(0x04)
OSR32 = const(0x05)
OSR64 = const(0x06)
OSR128 = const(0x07)
pressure_oversample_rate_values = (OSR1, OSR2, OSR4, OSR8, OSR16, OSR32, OSR64, OSR128)
temperature_oversample_rate_values = (
    OSR1,
    OSR2,
    OSR4,
    OSR8,
    OSR16,
    OSR32,
    OSR64,
    OSR128,
)


[docs] class BMP581: """Driver for the BMP581 Sensor connected over I2C. :param ~busio.I2C i2c_bus: The I2C bus the BMP581 is connected to. :param int address: The I2C device address. Defaults to :const:`0x47` :raises RuntimeError: if the sensor is not found **Quickstart: Importing and using the device** Here is an example of using the :class:`BMP581` class. First you will need to import the libraries to use the sensor .. code-block:: python import board import bmp581 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 bmp = bmp581.BMP581(i2c) Now you have access to the attributes .. code-block:: python press = bmp.pressure """ _device_id = ROUnaryStruct(_REG_WHOAMI, "B") _power_mode = RWBits(2, _ODR_CONFIG, 0) _temperature_oversample_rate = RWBits(3, _OSR_CONF, 0) _pressure_oversample_rate = RWBits(3, _OSR_CONF, 3) _output_data_rate = RWBits(5, _ODR_CONFIG, 2) _pressure_enabled = RWBits(1, _OSR_CONF, 6) _temperature = ROBits(24, 0x1D, 0, 3) _pressure = ROBits(24, 0x20, 0, 3) def __init__(self, i2c_bus: I2C, address: int = 0x47) -> None: self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) if self._device_id != 0x50: raise RuntimeError("Failed to find BMP581") self._power_mode = NORMAL self._pressure_enabled = True self.sea_level_pressure = 101.325 @property def power_mode(self) -> str: """ Sensor power_mode +-----------------------------+------------------+ | Mode | Value | +=============================+==================+ | :py:const:`bmp581.STANDBY` | :py:const:`0x00` | +-----------------------------+------------------+ | :py:const:`bmp581.NORMAL` | :py:const:`0x01` | +-----------------------------+------------------+ | :py:const:`bmp581.FORCED` | :py:const:`0x02` | +-----------------------------+------------------+ | :py:const:`bmp581.NON_STOP` | :py:const:`0X03` | +-----------------------------+------------------+ """ values = ( "STANDBY", "NORMAL", "FORCED", "NON_STOP", ) return values[self._power_mode] @power_mode.setter def power_mode(self, value: int) -> None: if value not in power_mode_values: raise ValueError("Value must be a valid power_mode setting") self._power_mode = value @property def pressure_oversample_rate(self) -> str: """ Sensor pressure_oversample_rate +---------------------------+------------------+ | Mode | Value | +===========================+==================+ | :py:const:`bmp581.OSR1` | :py:const:`0x00` | +---------------------------+------------------+ | :py:const:`bmp581.OSR2` | :py:const:`0x01` | +---------------------------+------------------+ | :py:const:`bmp581.OSR4` | :py:const:`0x02` | +---------------------------+------------------+ | :py:const:`bmp581.OSR8` | :py:const:`0x03` | +---------------------------+------------------+ | :py:const:`bmp581.OSR16` | :py:const:`0x04` | +---------------------------+------------------+ | :py:const:`bmp581.OSR32` | :py:const:`0x05` | +---------------------------+------------------+ | :py:const:`bmp581.OSR64` | :py:const:`0x06` | +---------------------------+------------------+ | :py:const:`bmp581.OSR128` | :py:const:`0x07` | +---------------------------+------------------+ """ values = ( "OSR1", "OSR2", "OSR4", "OSR8", "OSR16", "OSR32", "OSR64", "OSR128", ) return values[self._pressure_oversample_rate] @pressure_oversample_rate.setter def pressure_oversample_rate(self, value: int) -> None: if value not in pressure_oversample_rate_values: raise ValueError("Value must be a valid pressure_oversample_rate setting") self._pressure_oversample_rate = value @property def temperature_oversample_rate(self) -> str: """ Sensor temperature_oversample_rate +---------------------------+------------------+ | Mode | Value | +===========================+==================+ | :py:const:`bmp581.OSR1` | :py:const:`0x00` | +---------------------------+------------------+ | :py:const:`bmp581.OSR2` | :py:const:`0x01` | +---------------------------+------------------+ | :py:const:`bmp581.OSR4` | :py:const:`0x02` | +---------------------------+------------------+ | :py:const:`bmp581.OSR8` | :py:const:`0x03` | +---------------------------+------------------+ | :py:const:`bmp581.OSR16` | :py:const:`0x04` | +---------------------------+------------------+ | :py:const:`bmp581.OSR32` | :py:const:`0x05` | +---------------------------+------------------+ | :py:const:`bmp581.OSR64` | :py:const:`0x06` | +---------------------------+------------------+ | :py:const:`bmp581.OSR128` | :py:const:`0x07` | +---------------------------+------------------+ """ values = ( "OSR1", "OSR2", "OSR4", "OSR8", "OSR16", "OSR32", "OSR64", "OSR128", ) return values[self._temperature_oversample_rate] @temperature_oversample_rate.setter def temperature_oversample_rate(self, value: int) -> None: if value not in temperature_oversample_rate_values: raise ValueError( "Value must be a valid temperature_oversample_rate setting" ) self._temperature_oversample_rate = value @property def output_data_rate(self) -> int: """ Sensor output_data_rate. for a complete list of values please see the datasheet """ return self._output_data_rate @output_data_rate.setter def output_data_rate(self, value: int) -> None: if value not in range(0, 32, 1): raise ValueError("Value must be a valid output_data_rate setting") self._output_data_rate = value @property def temperature(self) -> float: """ The temperature sensor in C :return: Temperature """ raw_temp = self._temperature return self._twos_comp(raw_temp, 24) / 2**16.0 @property def pressure(self) -> float: """ The sensor pressure in kPa :return: Pressure in kPa """ raw_pressure = self._pressure return self._twos_comp(raw_pressure, 24) / 2**6.0 / 1000 @property def altitude(self): """ With the measured pressure p and the pressure at sea level p0 e.g. 1013.25hPa, the altitude in meters can be calculated with the international barometric formula With the measured pressure p and the absolute altitude the pressure at sea level can be calculated too. See the altitude setter for this calculation """ # updated to Bosch’s formula in bmp180 datasheet, and eliminate excessive rounding of altitude altitude = 44330.0 * ( 1.0 - ((self.pressure / self.sea_level_pressure) ** (1.0 / 5.255)) ) return altitude @altitude.setter def altitude(self, value: float) -> None: self.sea_level_pressure = self.pressure / (1.0 - value / 44330.0) ** 5.255 @staticmethod def _twos_comp(val: int, bits: int) -> int: if val & (1 << (bits - 1)) != 0: return val - (1 << bits) return val