-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
243 additions
and
109 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |