MicroPython Driver for BMP180 Sensor

BMP180 Driver Code for micro:bit

Download as zip file

'''
BMP180 Temperature and Barometric Pressure sensor
MicroPython driver for micro:bit

This is a major revision of Ver 1.00

EXAMPLE USAGE:
from fc_bmp180 import *
sensor = BMP180()
Temperature = sensor.T
Pressure = sensor.P

OSS Modes:
0 : Ultra low power
1 : Standard (default)
2 : High resolution
3 : Ultra high resolution

Temperature returned in Celsius.
Pressure returned in HPa.

AUTHOR: fredscave.com
DATE  : 2025/06
VERSION : 2.00
'''

from microbit import i2c, sleep
from micropython import const

_ADDR = const(0x77)

_REG_OUT     = const(0xF6)
_REG_MEASURE = const(0xF4)
_REG_RESET   = const(0xE0)
_REG_ID      = const(0xD0)

_CMD_READ_T = const(0x2E)
_CMD_READ_P = const(0x34)
_CMD_RESET  = const(0xB6)

# Pressure conversion times (ms)
# for Modes 0 to 3
PCT = (6, 9, 15, 27)

# Convert Celsius to Fahrenheit
CtoF = lambda C, d=2: round((C * 9/5) +32, d)

class BMP180():
    def __init__(self):
        self._Load_Calibration_Data()
        self.SetMode()

    # Mode 0 : Ultra low power
    # Mode 1 : Standard
    # Mode 2 : High resolution
    # Mode 3 : Ultra high resolution
    def SetMode(self, Mode=1):
        if Mode in range(0, 4):
           self.OSS = Mode
        else:
           self.OSS = 1

    # Soft reset, power-on defaults reloaded.
    def Reset(self):
        i2c.write(_ADDR, bytes([_REG_RESET, _CMD_RESET]))
        sleep(10)

# *******************************************
#              Properties
# *******************************************

    # Trigger temperature and pressure measurements.
    # Read uncompensated data and do the compensation
    # calculations.
    @property
    def Reading(self):
       # Get uncompensated temperature
        i2c.write(_ADDR, bytes([_REG_MEASURE, _CMD_READ_T]))
        sleep(6)
        i2c.write(_ADDR, bytes([_REG_OUT]))
        Buf = i2c.read(_ADDR, 2)
        UT = Buf[0]*256 + Buf[1]
        # Get uncompensated pressure
        i2c.write(_ADDR, bytes([_REG_MEASURE,
                  _CMD_READ_P + (self.OSS << 6)]))
        sleep(PCT[self.OSS])
        i2c.write(_ADDR, bytes([_REG_OUT]))
        Buf = i2c.read(_ADDR, 3)
        UP = ((Buf[0] << 16) + (Buf[1] << 8) + Buf[2]) >> (8-self.OSS)
        # Convert to actual temperature and pressure.
        return self._Compensate(UT, UP)

    # Returns temperature
    @property
    def T(self):
        temp = self.Reading
        return temp[0]

    # Returns pressure
    @property
    def P(self):
        pressure = self.Reading
        return pressure[1]

    # Return the chip's ID
    @property
    def ID(self):
        i2c.write(_ADDR, bytes([_REG_ID]))
        return hex(i2c.read(_ADDR, 1)[0])

    # Returns Mode (0..3)
    @property
    def GetMode(self):
        return self.OSS

# *******************************************
#             Altitude Calculations
# *******************************************

    # Calculate mean sea level pressure (MSLP)
    # If the altitude of the sensor is known then
    # the absolute pressure can be adjusted to its
    # equivalent sea level pressure.
    #
    # This is the pressure that is reported by
    # official weather services.
    def MSLP(self, Altitude=None):
        if Altitude == None:
            return None
        else:
            P0 = 1013.25
            a = 2.25577E-5
            b = 5.25588
            P = self.Reading[1]
            PS = P0 * (1 - a * Altitude) ** b
            offset = P0 - PS
            return P + offset

    # If pressure readings are taken at different
    # altitudes then this method will calculate
    # the difference between these two altitudes
    # in meters.
    def AltDiff(self, P1, P2):
        a = 0.1157227
        return (P1 - P2) / a

    # If the Mean Sea Level pressure is known
    # (usually obtained from the local weather service)
    # then this method will calculate the altitude
    # of the sensor in meters.
    # The sensor is read to obtain the absolute
    # pressure value.
    def Altitude(self, MSLP=None):
        if MSLP == None:
            return None
        else:
            p = self.P
            a = -2.25577E-5
            b = 0.1902631
            h = (((p/MSLP) ** b) - 1) / a
            return int(round(h, 0))

# *******************************************
#              Private Methods
# *******************************************

    # Load all the calibration data from
    # the chip's non-volatile memory (NVM).
    def _Load_Calibration_Data(self):
        self.AC1 = self._get(0xAA, 1)
        self.AC2 = self._get(0xAC, 1)
        self.AC3 = self._get(0xAE, 1)
        self.AC4 = self._get(0xB0, 0)
        self.AC5 = self._get(0xB2, 0)
        self.AC6 = self._get(0xB4, 0)
        self.B1  = self._get(0xB6, 1)
        self.B2  = self._get(0xB8, 1)
        self.MB  = self._get(0xBA, 1)
        self.MC  = self._get(0xBC, 1)
        self.MD  = self._get(0xBE, 1)

    # Used to retrieve calibration data
    # which can be returned as either a
    # signed integer or unsigned integer.
    def _get(self, Reg, Signed):
        i2c.write(_ADDR, bytes([Reg]))
        buf = i2c.read(_ADDR, 2)
        d = buf[0]*256 + buf[1]
        if Signed == 1:
            if d > 32767:
                return d - 65536
            else:
                return d
        else:
            return d

    # Convert uncompensated temperature and pressure
    # to actual (compensated) values.
    def _Compensate(self, UT, UP):
        # Calculate actual temperature
        X1 = (UT - self.AC6) * self.AC5/(1 << 15)
        X2 = self.MC * (1 << 11) / (X1 + self.MD)
        B5 = X1 + X2
        T = (B5 + 8)/160
        # Calculate actual pressure
        B6 = B5 - 4000
        X1 = (self.B2 * (B6*B6/(1 << 12))) / (1 << 11)
        X2 = (self.AC2 * B6)/(1 << 11)
        X3 = X1 + X2
        B3 = ((int((self.AC1*4+X3)) << self.OSS) + 2)/4
        X1 = self.AC3 * B6 / (1 << 13)
        X2 = (self.B1 * (B6*B6/(1 << 12))) / (1 << 16)
        X3 = (X1 + X2 + 2)/4
        B4 = self.AC4 * (X3 + 32768)/(1 << 15)
        B7 = (UP-B3) * (50000 >> self.OSS)
        if B7 < 0x80000000:
            p = (B7*2)/B4
        else:
            p = (B7/B4) * 2
        X1 = (p/(1 << 8))*(p/(1 << 8))
        X1 = (X1 * 3038)/(1 << 16)
        X2 = (-7357*p)/(1 << 16)
        P = p + (X1 + X2 + 3791)/16
        return [T, P/100]

          

Using All Methods & Properties

The following example uses most of the methods and properties already described.


The Code:
from fc_bmp180 import *

# Create BMP180 sensor object.
sensor = BMP180()
sensor.SetMode(3)
altitude = 400
print('BMP180 sensor configuration:')
print('Mode is', sensor.GetMode, '; Ultra high resolution')
print('Height above sea level =', altitude, 'meters\n')

# Get temperature and pressure
# Test all properties
print('Get Temperature and Pressure measurements:')
print('Reading property:', sensor.Reading)
print('Temperature only property:', sensor.T)
print('Pressure only property:', sensor.P)

# Calculate Mean Sea Level Pressure (MSLP)
print('\nMean Sea Level Pressure:', sensor.MSLP(altitude))

# Reset the chip
# Test by getting ID, Mode and MSLP
print('\nResetting the sensor...')
sensor.Reset()
print('Chip ID:', sensor.ID)
print('Mode is', sensor.GetMode)
print('Mean Sea Level Pressure:', sensor.MSLP(altitude))

Typical Output:
BMP180 sensor configuration:
Mode is 3 ; Ultra high resolution
Height above sea level = 400 meters

Get Temperature and Pressure measurements:
Reading property: [21.71325, 981.4588]
Temperature only property: 21.72578
Pressure only property: 981.4178

Mean Sea Level Pressure: 1028.634

Resetting the sensor...
Chip ID: 0x55
Mode is 3
Mean Sea Level Pressure: 1028.631