Source code for bmp180

# SPDX-FileCopyrightText: Copyright (c) 2020 BadTigrou, 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT

"""
`bmp180` - Temperature & Barometic Pressure Sensor
===============================================================================

CircuitPython driver from BMP180 Temperature and Barometic Pressure sensor

* Author(s): BadTigrou, Jose D. Montoya
"""
# pylint: disable=consider-using-from-import, import-outside-toplevel, unused-import

from time import sleep
from micropython import const
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct, Struct
import adafruit_bus_device.i2c_device as i2c_device

try:
    from busio import I2C
    from typing_extensions import NoReturn
    import struct
except ImportError:
    pass

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/jposada202020/CircuitPython_BMP180.git"

_CHIP_ID = const(0x255)
_I2C_ADDR = const(0x77)

_REGISTER_CHIPID = const(0xD0)
_REGISTER_SOFTRESET = const(0xE0)
_REGISTER_CONTROL = const(0xF4)
_REGISTER_DATA = const(0xF6)
_REGISTER_AC1 = const(0xAA)

_BMP180_PRESSURE_MIN_HPA = const(300)
_BMP180_PRESSURE_MAX_HPA = const(1100)


"""oversampling values for temperature, pressure, and humidity"""
TEMPERATURE_CMD = const(0x2E)
PRESSURE_OVERSAMPLING_X1 = const(0x01)
PRESSURE_OVERSAMPLING_X2 = const(0x02)
PRESSURE_OVERSAMPLING_X4 = const(0x03)
PRESSURE_OVERSAMPLING_X8 = const(0x04)

_BMP180_PRESSURE_CMD = {
    PRESSURE_OVERSAMPLING_X1: 0x34,
    PRESSURE_OVERSAMPLING_X2: 0x74,
    PRESSURE_OVERSAMPLING_X4: 0xB4,
    PRESSURE_OVERSAMPLING_X8: 0xF4,
}


"""mode values"""
MODE_ULTRALOWPOWER = const(0x00)
MODE_STANDARD = const(0x01)
MODE_HIGHRES = const(0x02)
MODE_ULTRAHIGHRES = const(0x03)

_BMP180_MODES = (MODE_ULTRALOWPOWER, MODE_STANDARD, MODE_HIGHRES, MODE_ULTRAHIGHRES)

# pylint: disable=invalid-name, too-many-instance-attributes, too-few-public-methods


[docs]class BMP180: """Base BMP180 object. Use :class:`BMP180_I2C` instead of this. This checks the BMP180 was found, reads the coefficients and enables the sensor for continuous reads""" _device_id = ROUnaryStruct(_REGISTER_CHIPID, "H") _reg_control = UnaryStruct(_REGISTER_CONTROL, "H") _reg_soft_reset = UnaryStruct(_REGISTER_SOFTRESET, "H") _regdata_MSB = UnaryStruct(_REGISTER_DATA, "H") _regdata_LSB = UnaryStruct(_REGISTER_DATA + 1, "H") _regdata_XLSB = UnaryStruct(_REGISTER_DATA + 2, "H") _coeffs = Struct(_REGISTER_AC1, ">hhhHHHhhhhh") _raw_temperature = UnaryStruct(_REGISTER_DATA, ">H") def __init__(self, i2c_bus: I2C, address: int = 0x77) -> None: self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) if self._device_id != _CHIP_ID: raise RuntimeError( "Failed to find BMP180! Chip ID {}".format(self._device_id) ) self._oversampling_setting = PRESSURE_OVERSAMPLING_X8 self._mode = MODE_HIGHRES self.coeffs_mem = self._coeffs self.sea_level_pressure = 1013.25 def _reset(self): """Soft reset the sensor""" self._reg_soft_reset = 0xB6 # reset the device sleep(0.004) # Datasheet says 2ms. Using 4ms just to be safe @property def temperature(self): """The compensated temperature in Celsius.""" self._reg_control = TEMPERATURE_CMD sleep(0.005) # Wait 5ms UT = self._raw_temperature X1 = int(((UT - self.coeffs_mem[5]) * self.coeffs_mem[4]) >> 15) X2 = int((self.coeffs_mem[9] << 11) / (X1 + self.coeffs_mem[10])) B5 = X1 + X2 temp = ((B5 + 8) >> 4) / 10.0 return temp @property def altitude(self): """The altitude based on the sea level pressure - which you must enter ahead of time""" altitude = 44330.0 * ( 1.0 - ((self.pressure / self.sea_level_pressure) ** 0.19025) ) return round(altitude, 1) @altitude.setter def altitude(self, value: float) -> None: self.sea_level_pressure = self.pressure / (1.0 - value / 44330.0) ** 5.255 @property def pressure(self): """The compensated pressure in hectoPascals.""" self._reg_control = TEMPERATURE_CMD sleep(0.005) # Wait 5ms UT = self._raw_temperature UP = self._read_raw_pressure() X1 = int(((UT - self.coeffs_mem[5]) * self.coeffs_mem[4]) >> 15) X2 = int((self.coeffs_mem[9] << 11) / (X1 + self.coeffs_mem[10])) B5 = X1 + X2 B6 = B5 - 4000 X1 = int((self.coeffs_mem[7] * (B6 * B6) >> 12) >> 11) X2 = int((self.coeffs_mem[1] * B6) >> 11) X3 = X1 + X2 B3 = int((((self.coeffs_mem[0] * 4 + X3) << self._mode) + 2) / 4) X1 = int((self.coeffs_mem[2] * B6) >> 13) X2 = int((self.coeffs_mem[6] * ((B6 * B6) >> 12)) >> 16) X3 = int(((X1 + X2) + 2) >> 2) B4 = int((self.coeffs_mem[3] * (X3 + 32768)) >> 15) B7 = int((UP - B3) * (50000 >> self._mode)) if B7 < 0x80000000: p = int((B7 * 2) / B4) else: p = int((B7 / B4) * 2) X1 = int((p >> 8) * (p >> 8)) X1 = int((X1 * 3038) >> 16) X2 = int((-7357 * p) >> 16) return int(p + ((X1 + X2 + 3791) >> 4)) / 100 def _read_raw_pressure(self): self._reg_control = _BMP180_PRESSURE_CMD[self._mode] if self._mode == PRESSURE_OVERSAMPLING_X8: sleep(0.026) elif self._mode == PRESSURE_OVERSAMPLING_X4: sleep(0.014) elif self._mode == PRESSURE_OVERSAMPLING_X2: sleep(0.008) else: sleep(0.005) msb = self._regdata_MSB & 0xFF lsb = self._regdata_LSB & 0xFF xlsb = self._regdata_XLSB & 0xFF return ((msb << 16) + (lsb << 8) + xlsb) >> (8 - self._mode) @property def mode(self): """ Operation mode Allowed values are set in the MODE enum class +----------------------------------------+-------------------------+ | Mode | Value | +========================================+=========================+ | :py:const:`bmp180.MODE_ULTRALOWPOWER` | :py:const:`0x00` | +----------------------------------------+-------------------------+ | :py:const:`bmp180.MODE_STANDARD` | :py:const:`0x01` | +----------------------------------------+-------------------------+ | :py:const:`bmp180.MODE_HIGHRES` | :py:const:`0x02` | +----------------------------------------+-------------------------+ | :py:const:`bmp180.MODE_ULTRAHIGHRES` | :py:const:`0x03` | +----------------------------------------+-------------------------+ Example --------------------- .. code-block:: python i2c = board.I2C() qmc = bmp180.BMP180(i2c) bmp180.mode = bmp180.MODE_HIGHRES """ return self._mode @mode.setter def mode(self, value): if not value in _BMP180_MODES: raise ValueError("Mode {} not supported".format(value)) self._mode = value @property def oversampling_setting(self): """ Oversampling setting Allowed values are set in the OVERSAMPLES enum class """ return self._oversampling_setting @oversampling_setting.setter def oversampling_setting(self, value): if not value in _BMP180_PRESSURE_CMD: raise ValueError("Overscan value {} not supported".format(value)) self._overscan_temperature = value