Skip to content

Commit

Permalink
fix: fixed BMP180
Browse files Browse the repository at this point in the history
  • Loading branch information
AsCress committed Aug 19, 2024
1 parent 4e53d8f commit 8a76740
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 109 deletions.
109 changes: 0 additions & 109 deletions pslab/external/BMP180.py

This file was deleted.

243 changes: 243 additions & 0 deletions pslab/external/bmp180.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
"""BMP180 Altimeter."""

import time
import logging
from pslab.bus import I2CSlave

# BMP180 default address
_ADDRESS = 0x77

# Operating Modes
_ULTRALOWPOWER = 0
_STANDARD = 1
_HIGHRES = 2
_ULTRAHIGHRES = 3

# BMP180 Registers
_CAL_AC1 = 0xAA # R Calibration data (16 bits)
_CAL_AC2 = 0xAC # R Calibration data (16 bits)
_CAL_AC3 = 0xAE # R Calibration data (16 bits)
_CAL_AC4 = 0xB0 # R Calibration data (16 bits)
_CAL_AC5 = 0xB2 # R Calibration data (16 bits)
_CAL_AC6 = 0xB4 # R Calibration data (16 bits)
_CAL_B1 = 0xB6 # R Calibration data (16 bits)
_CAL_B2 = 0xB8 # R Calibration data (16 bits)
_CAL_MB = 0xBA # R Calibration data (16 bits)
_CAL_MC = 0xBC # R Calibration data (16 bits)
_CAL_MD = 0xBE # R Calibration data (16 bits)
_CONTROL = 0xF4
_TEMPDATA = 0xF6
_PRESSUREDATA = 0xF6

# Commands
_READTEMPCMD = 0x2E
_READPRESSURECMD = 0x34


class BMP180(I2CSlave):
"""Class to interface with the BMP180 Altimeter.
Parameters
----------
mode : int, optional
The mode of operation for the sensor, determining the oversampling setting.
The default mode is `_HIGHRES`. This parameter affects the precision and speed
of the temperature and pressure measurements.
**kwargs : dict, optional
Additional keyword arguments, such as:
- address (int): The I2C address of the BMP180 sensor. Default is `_ADDRESS`.
Attributes
----------
temperature : float or None
The last measured temperature in degrees Celsius, initialized to `None`.
This attribute is updated each time the `read_temperature()` method is called.
pressure : float or None
The last measured pressure in Pa (Pascals), initialized to `None`.
This attribute is updated each time the `read_pressure()` method is called.
altitude : float or None
The calculated altitude in meters based on the current pressure reading
and a reference sea level pressure, initialized to `None`. This attribute
is updated each time the `altitude()` method is called.
_ac1 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_ac2 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_ac3 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_ac4 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_ac5 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_ac6 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_b1 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_b2 : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_mb : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_mc : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
_md : int
Calibration value for the sensor. This attribute is read-only and is loaded from
the sensor during initialization.
"""

NUMPLOTS = 3
PLOTNAMES = ["Temperature", "Pressure", "Altitude"]
name = "BMP180 Altimeter"

def __init__(self, mode=_HIGHRES, **kwargs):
self._ADDRESS = kwargs.get("address", _ADDRESS)
super().__init__(self._ADDRESS)
self._mode = mode

_logger = logging.getLogger(__name__)

# Load calibration values
self._ac1 = self._read_int16(_CAL_AC1)
self._ac2 = self._read_int16(_CAL_AC2)
self._ac3 = self._read_int16(_CAL_AC3)
self._ac4 = self._read_uint16(_CAL_AC4)
self._ac5 = self._read_uint16(_CAL_AC5)
self._ac6 = self._read_uint16(_CAL_AC6)
self._b1 = self._read_int16(_CAL_B1)
self._b2 = self._read_int16(_CAL_B2)
self._mb = self._read_int16(_CAL_MB)
self._mc = self._read_int16(_CAL_MC)
self._md = self._read_int16(_CAL_MD)

_logger.debug(f"ac1: {self._ac1}")
_logger.debug(f"ac2: {self._ac2}")
_logger.debug(f"ac3: {self._ac3}")
_logger.debug(f"ac4: {self._ac4}")
_logger.debug(f"ac5: {self._ac5}")
_logger.debug(f"ac6: {self._ac6}")
_logger.debug(f"b1: {self._b1}")
_logger.debug(f"b2: {self._b2}")
_logger.debug(f"mb: {self._mb}")
_logger.debug(f"mc: {self._mc}")
_logger.debug(f"md: {self._md}")

self.temperature = None
self.pressure = None
self.altitude = None

def _read_int16(self, addr):
data = self.read(2, addr)
value = (data[0] << 8) | data[1]
if value & 0x8000: # Check if the sign bit is set
value -= 0x10000
return value

def _read_uint16(self, addr):
data = self.read(2, addr)
value = (data[0] << 8) | data[1]
return value

def _read_raw_temperature(self):
"""Reads the raw temperature from the sensor."""
self.write([_READTEMPCMD], _CONTROL)
time.sleep(0.005)
raw = self._read_uint16(_TEMPDATA)
return raw

def read_temperature(self):
"""Get the actual temperature in degrees celsius."""
ut = self._read_raw_temperature()
# Calculations from section 3.5 of the datasheet
x1 = ((ut - self._ac6) * self._ac5) >> 15
x2 = (self._mc << 11) // (x1 + self._md)
b5 = x1 + x2
self.temperature = ((b5 + 8) >> 4) / 10.0
return self.temperature

@property
def oversampling(self):
"""oversampling : int
The oversampling setting used by the sensor. This attribute is settable and
determines the trade-off between measurement accuracy and speed. Possible values
include `_ULTRALOWPOWER`, `_STANDARD`, `_HIGHRES`, and `_ULTRAHIGHRES`.
"""
return self._mode

@oversampling.setter
def oversampling(self, value):
self._mode = value

def _read_raw_pressure(self):
"""Read the raw pressure level from the sensor."""
delays = [0.005, 0.008, 0.014, 0.026]
self.write([_READPRESSURECMD + (self._mode << 6)], _CONTROL)
time.sleep(delays[self._mode])
msb = self.read_byte(_PRESSUREDATA) & 0xFF
lsb = self.read_byte(_PRESSUREDATA + 1) & 0xFF
xlsb = self.read_byte(_PRESSUREDATA + 2) & 0xFF
raw = ((msb << 16) + (lsb << 8) + xlsb) >> (8 - self._mode)
return raw

def read_pressure(self):
"""Get the actual pressure in Pascals."""
ut = self._read_raw_temperature()
up = self._read_raw_pressure()
# Calculations from section 3.5 of the datasheet
x1 = ((ut - self._ac6) * self._ac5) >> 15
x2 = (self._mc << 11) // (x1 + self._md)
b5 = x1 + x2
# Pressure Calculations
b6 = b5 - 4000
x1 = (self._b2 * (b6 * b6) >> 12) >> 11
x2 = (self._ac2 * b6) >> 11
x3 = x1 + x2
b3 = (((self._ac1 * 4 + x3) << self._mode) + 2) // 4
x1 = (self._ac3 * b6) >> 13
x2 = (self.b1 * ((b6 * b6) >> 12)) >> 16
x3 = ((x1 + x2) + 2) >> 2
b4 = (self._ac4 * (x3 + 32768)) >> 15
b7 = (up - b3) * (50000 >> self._mode)
if b7 < 0x80000000:
p = (b7 * 2) // b4
else:
p = (b7 // b4) * 2
x1 = (p >> 8) * (p >> 8)
x1 = (x1 * 3038) >> 16
x2 = (-7357 * p) >> 16
self.pressure = p + ((x1 + x2 + 3791) >> 4)
return self.pressure

def altitude(self, sea_level_pressure=101325.0):
# Calculation from section 3.6 of datasheet
pressure = float(self.read_pressure())
self.altitude = 44330.0 * (
1.0 - pow(pressure / sea_level_pressure, (1.0 / 5.255))
)
return self.altitude

def sealevel_pressure(self, local_pressure, altitude):
"""Calculate the sea level pressure."""
return local_pressure / pow(1 - (altitude / 44330.0), 5.255)

0 comments on commit 8a76740

Please sign in to comment.