Source code for bmp180
# SPDX-FileCopyrightText: Copyright (c) 2020 BadTigrou, 2023 Jose D. Montoya
#
# SPDX-License-Identifier: MIT
"""
`bmp180` - Temperature & Barometric Pressure Sensor
===============================================================================
CircuitPython driver from BMP180 Temperature and Barometric Pressure sensor
* Author(s): BadTigrou, Jose D. Montoya
"""
from time import sleep
from micropython import const
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct, Struct
from adafruit_bus_device import i2c_device
try:
from busio import I2C
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)
"""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)
[docs]
class BMP180:
"""Driver for the BMP180 Sensor connected over I2C.
:param ~busio.I2C i2c_bus: The I2C bus the BMP180 is connected to.
:param int address: The I2C device address. Defaults to :const:`0x77`
:raises RuntimeError: if the sensor is not found
**Quickstart: Importing and using the device**
Here is an example of using the :class:`BMP180` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
import board
import bmp180
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 = bmp180.BMP180(i2c)
Now you have access to the attributes
.. code-block:: python
press = bmp.pressure
temp = bmp.temperature
"""
_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(f"Failed to find BMP180! Chip ID {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.
Calculation of true temperature in steps of 0.1°C.
"""
self._reg_control = TEMPERATURE_CMD
sleep(0.005) # Wait 5ms
UT = self._raw_temperature
X1 = ((UT - self.coeffs_mem[5]) * self.coeffs_mem[4]) / 2**15.0
X2 = (self.coeffs_mem[9] * 2**11.0) / (X1 + self.coeffs_mem[10])
B5 = X1 + X2
temp = ((B5 + 8) / 2**4.0) / 10.0
return temp
@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
"""
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.
Calculation of true pressure in steps of 1Pa (= 0.01hPa = 0.01mbar)
"""
self._reg_control = TEMPERATURE_CMD
sleep(0.005) # Wait 5ms
UT = self._raw_temperature
UP = self._read_raw_pressure()
X1 = ((UT - self.coeffs_mem[5]) * self.coeffs_mem[4]) / 2**15.0
X2 = (self.coeffs_mem[9] * 2**11.0) / (X1 + self.coeffs_mem[10])
B5 = X1 + X2
B6 = B5 - 4000
X1 = (self.coeffs_mem[7] * (B6 * B6) / 2**12.0) / 2**11.0
X2 = (self.coeffs_mem[1] * B6) / 2**11.0
X3 = X1 + X2
B3 = (((self.coeffs_mem[0] * 4 + X3) * 2 ** float(self._mode)) + 2) / 4
X1 = (self.coeffs_mem[2] * B6) / 2**13.0
X2 = (self.coeffs_mem[6] * ((B6 * B6) / 2**12.0)) / 2**16.0
X3 = ((X1 + X2) + 2) / 2**2.0
B4 = (self.coeffs_mem[3] * (X3 + 32768.0)) / 2**15.0
B7 = (UP - B3) * (50000.0 / 2 ** float(self._mode))
if B7 < 2147483648.0:
press = (B7 * 2) / B4
else:
press = (B7 / B4) * 2
X1 = (press / 2**8.0) * (press / 2**8.0)
X1 = (X1 * 3038) / 2**16.0
X2 = (-7357 * press) / 2**16.0
return (press + ((X1 + X2 + 3791) / 2**4.0)) / 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
+----------------------------------------+-------------------------+
| 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 value not in _BMP180_MODES:
raise ValueError(f"Mode {value} not supported")
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(f"Overscan value {value} not supported")
self._oversampling_setting = value