From 57f7c341c6afc099c25f7e4843ee4de81ef4fcdd Mon Sep 17 00:00:00 2001 From: Anashuman Singh Date: Wed, 21 Aug 2024 12:12:27 +0530 Subject: [PATCH] fix: fixed BMP180 --- pslab/external/BMP180.py | 109 ----------------------- pslab/external/bmp180.py | 186 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 109 deletions(-) delete mode 100644 pslab/external/BMP180.py create mode 100644 pslab/external/bmp180.py diff --git a/pslab/external/BMP180.py b/pslab/external/BMP180.py deleted file mode 100644 index fe5d9b99..00000000 --- a/pslab/external/BMP180.py +++ /dev/null @@ -1,109 +0,0 @@ -import time -from numpy import int16 -from pslab.bus import I2CSlave - -class BMP180(I2CSlave): - _ADDRESS = 0x77 - _REG_CONTROL = 0xF4 - _REG_RESULT = 0xF6 - _CMD_TEMP = 0x2E - _CMD_P0 = 0x34 - _CMD_P1 = 0x74 - _CMD_P2 = 0xB4 - _CMD_P3 = 0xF4 - oversampling = 0 - NUMPLOTS = 3 - PLOTNAMES = ['Temperature', 'Pressure', 'Altitude'] - name = 'BMP180 Altimeter' - - def __init__(self, **args): - self._ADDRESS = args.get('address', self._ADDRESS) - super().__init__(self._ADDRESS) - - self.MB = self.__readInt__(0xBA) - - self.c3 = 160.0 * pow(2, -15) * self.__readInt__(0xAE) - self.c4 = pow(10, -3) * pow(2, -15) * self.__readUInt__(0xB0) - self.b1 = pow(160, 2) * pow(2, -30) * self.__readInt__(0xB6) - self.c5 = (pow(2, -15) / 160) * self.__readUInt__(0xB2) - self.c6 = self.__readUInt__(0xB4) - self.mc = (pow(2, 11) / pow(160, 2)) * self.__readInt__(0xBC) - self.md = self.__readInt__(0xBE) / 160.0 - self.x0 = self.__readInt__(0xAA) - self.x1 = 160.0 * pow(2, -13) * self.__readInt__(0xAC) - self.x2 = pow(160, 2) * pow(2, -25) * self.__readInt__(0xB8) - self.y0 = self.c4 * pow(2, 15) - self.y1 = self.c4 * self.c3 - self.y2 = self.c4 * self.b1 - self.p0 = (3791.0 - 8.0) / 1600.0 - self.p1 = 1.0 - 7357.0 * pow(2, -20) - self.p2 = 3038.0 * 100.0 * pow(2, -36) - self.T = 25 - print('calib:', self.c3, self.c4, self.b1, self.c5, self.c6, self.mc, self.md, self.x0, self.x1, self.x2, - self.y0, self.y1, self.p0, self.p1, self.p2) - self.params = {'setOversampling': [0, 1, 2, 3]} - - self.initTemperature() - self.initPressure() - self.baseline = self.readPressure() - - def __readInt__(self, addr): - return int16(self.read_byte(addr)) - - def __readUInt__(self, addr): - vals = self.read(2, addr) - v = 1. * ((vals[0] << 8) | vals[1]) - return v - - def initTemperature(self): - self.write([self._REG_CONTROL, self._CMD_TEMP]) - time.sleep(0.005) - - def readTemperature(self): - vals = self.read(2, self._REG_RESULT) - if len(vals) == 2: - T = (vals[0] << 8) + vals[1] - a = self.c5 * (T - self.c6) - self.T = a + (self.mc / (a + self.md)) - return self.T - else: - return False - - def setOversampling(self, num): - self.oversampling = num - - def initPressure(self): - os = [0x34, 0x74, 0xb4, 0xf4] - delays = [0.005, 0.008, 0.014, 0.026] - self.write([self._REG_CONTROL, os[self.oversampling]]) - time.sleep(delays[self.oversampling]) - - def readPressure(self): - vals = self.read(3, self._REG_RESULT) - if len(vals) == 3: - P = 1. * (vals[0] << 8) + vals[1] + (vals[2] / 256.0) - s = self.T - 25.0 - x = (self.x2 * pow(s, 2)) + (self.x1 * s) + self.x0 - y = (self.y2 * pow(s, 2)) + (self.y1 * s) + self.y0 - z = (P - x) / y - self.P = (self.p2 * pow(z, 2)) + (self.p1 * z) + self.p0 - return self.P - else: - return False - - def altitude(self): - # baseline pressure needs to be provided - return (44330.0 * (1 - pow(self.P / self.baseline, 1 / 5.255))) - - def sealevel(self, P, A): - ''' - given a calculated pressure and altitude, return the sealevel - ''' - return (P / pow(1 - (A / 44330.0), 5.255)) - - def getRaw(self): - self.initTemperature() - self.readTemperature() - self.initPressure() - self.readPressure() - return [self.T, self.P, self.altitude()] diff --git a/pslab/external/bmp180.py b/pslab/external/bmp180.py new file mode 100644 index 00000000..c7e669fa --- /dev/null +++ b/pslab/external/bmp180.py @@ -0,0 +1,186 @@ +"""BMP180 Altimeter.""" + +import time +import logging +import struct +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 + +_logger = logging.getLogger(__name__) + + +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 + The measured temperature in degrees Celsius. + + pressure : float + The measured pressure in Pa (Pascals). + + altitude : float + The calculated altitude in meters based on the current pressure reading + and a reference sea level pressure. + """ + + 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 + + # 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}") + + def _read_int16(self, addr): + BE_INT16 = struct.Struct(">h") # signed short, big endian + return BE_INT16.unpack(self.read(2, addr)) + + def _read_uint16(self, addr): + BE_UINT16 = struct.Struct(">H") # unsigned short, big endian + return BE_UINT16.unpack(self.read(2, addr)) + + def _read_raw_temperature(self): + """Read the raw temperature from the sensor.""" + self.write([_READTEMPCMD], _CONTROL) + time.sleep(0.005) + raw = self._read_uint16(_TEMPDATA) + return raw + + @property + def 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 + temperature = ((b5 + 8) >> 4) / 10.0 + return 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 + + @property + def 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 + pressure = p + ((x1 + x2 + 3791) >> 4) + return pressure + + @property + def altitude(self): + # Calculation from section 3.6 of datasheet + pressure = float(self.pressure) + altitude = 44330.0 * (1.0 - pow(pressure / 101325.0, (1.0 / 5.255))) + return altitude