MicroPython Driver for BMP388 & BMP390 Sensors
Contents
Introduction
This a MicroPython driver written specifically for the BBC micro:bit that will work with the BMP388 and BMP390 ambient temperature and barometric pressure sensors.
The BMP280 and BMP390 sensors are discussed in some detail here.
These BMP3xx are the next generation replacements for the BMP280 sensor.
FIG 1 - Two different styles of breakout boards: [Left] BMP388 and [Right] BMP390
The sensors are quite small and for projects using a breadboard its much easier to use a breakout board. The breakout boards are quite inexpensive. Both boards still appear to be readily available at time of writing (June 2025).
Connecting the BMP388/BMP390
The BMP388 and BMP390 communicate via I2C and SPI. This driver will only use I2C. Hooking them up to the micro:bit for I2C is easy:
| micro:bit | Sensor board |
|---|---|
| 3.3V | VCC |
| GND | GND |
| Pin 20 | SDA |
| Pin 19 | SCL |
This utilises the standard I2C pins of the micro:bit.
Optionally, a jumper wire can be placed between the GND pin and the SDO pin on the breakout board. This changes the default I2C address and is discussed below.
Driver Overview
This driver implements most of the BMP288/390 functionality as described in the product Datasheets.
Driver codeThe driver code can be:
- Copied from this webpage onto the clipboard then pasted into the MicroPython editor e.g. the Mu Editor. It should be saved as fc_bmp3xx.py - OR -
- Download as a zip file using the link. Unzip the file and save it as fc_bmp3xx.py into the default directory where the MicroPython editor e.g. Mu Editor saves python code files.
After saving the fc_bmp3xx.py file to the computer it should be copied to the small filesystem on the micro:bit. The examples on this page will not work if this step is omitted. Any MicroPython editor that recognises the micro:bit will have an option to do this.
The Mu Editor is recommended for its simplicity. It provides a Files button on the toolbar for just this purpose.
I2C addressThe BMP388/390 breakout boards have a default I2C address of 0x77. This can be changed to 0x76 if a jumper wire is connected between the GND pin and SDO pin.
Class constructorThe driver is implemented as a base class. There are two derived classes from the base class; one each for the BMP388 and BMP390. The example below illustrates this.
The first thing to do is call the constructor of the derived class for the sensor being used.
Syntax:
BMP388(ADDR=0x77)
BMP390(ADDR=0x77)
Where:
ADDR : The I2C address.
Example
from fc_bmp3xx import *
# Declare a BMP388 sensor object
# The I2C address is 0x76.
bmp388 = BMP388(0x76)
# Declare a BMP390 sensor object
# The I2C address is 0x77 (default).
bmp390 = BMP390()
Each of the sensor classes have the Type property.
Syntax:
Type
Example 1
from fc_bmp3xx import *
# Declare a BMP388 sensor object
sensor = BMP388()
print(sensor.Type)
Output:
BMP388
Example 2
from fc_bmp3xx import *
# Declare a BMP390 sensor object
sensor = BMP390()
print(sensor.Type)
Output:
BMP390
This assumes that the file fc_bmp3xx.py has been successfully copied to the micro:bit's filesystem as described above.
Methods and PropertiesThe driver provides methods and properties to:
- Set and get the sampling mode.
- Read temperature and pressure.
- Operate the FIFO queue.
- Perform altitude calculations.
- Return Chip ID and Revision Number.
- Return the sensor time.
The driver implements most functions described in the product Datasheet. The INT pin is not used.
Sampling Modes
These two sensors have such a plethora of possible settings that the user can become totally confused. Temperature and pressure have a multitude of oversampling possibilities. Additionally, there is a very useful IIR filter that counteracts pressure errors when in an environment with extraneous air movement.
The pressure and temperature oversampling and IIR filter have multiple settings. While in continuous sampling mode (explained below) the sampling period can also be configured. There are various combinations that make no sense and other combinations that simply won't work together.
The approach with this driver has been to simply all these possibilities down to two combinations of the above mentioned parameters.
Mode 1 is a low power, lower resolution mode suitable for less frequent readings such as in a weather station.
Mode 1 uses more power but provides a maximum resolution solution. This mode is suitable for measuring rapid changes in altitude or as a component of a situational awareness sensory package.
The datasheet describes three distinct power modes. This driver implements the three power modes in the following manner:
- Sleep - Measurements circuitry is shutdown but the registers are still available. Uses minimal power.
- Forced - called Mode 0 in this driver. Takes a sample on demand then enters Sleep mode. Sample time is only 5ms. This has a minimal power, lower resolution setting.
- Normal - called Mode 1 in this driver. Takes continuous samples with a user defined sample period time. Default is 80ms. This has a higher power, highest possible resolution setting.
Syntax:
SleepOn()
This method puts the sensor into Sleep mode.
It is only available for Mode 1.
SleepOff()
This method wakes up the sensor. The sensor
enters Mode 1 (continuous sampling).
SetMode(Mode=0, odr_set=4)
This method is used to set the sampling mode.
Mode can have a value of 0 or 1.
Mode 0 : Forced
Mode 1 : Normal (continuous)
The parameter odr_set determines the sample
period time when Mode 1 is selected. It can
have values between 4 and 17 inclusive;
Sample Time = 5 * (2 ** odr_set) in ms.
See the datasheet for details.
GetMode
Property that returns the current
mode : 0 or 1.
GetODR
Property that returns the current
odr_set : 4 to 17.
Example
from fc_bmp3xx import *
sensor = BMP390()
print('Mode is:', sensor.GetMode)
print('Changing mode...')
sensor. SetMode(1, 10)
print('Mode is:', sensor.GetMode)
print('odr_set is:', sensor.GetODR)
print('Sensor entering sleep mode...')
sensor.SleepOn()
print('...Waking up the sensor')
sensor.SleepOff()
Output
Mode is: 0
Changing mode...
Mode is: 1
odr_set is: 10
Sensor entering sleep mode...
...Waking up the sensor
Reading Temperature & Pressure
There are three properties that return temperature and/or pressure readings Reading, T and P.
There is one property, IsDataReady that returns whether there is data available to be read from the sensor.
The function CtoF is available to convert Celsius to Fahrenheit.
Syntax:
Reading
Returns temperature and pressure as a tuple.
The first element of the tuple is temperature.
The second element is pressure.
Temperature is in degrees Celsius.
Pressure is in hPa (hectopascals)
T
Returns only the temperature in Celsius.
P
Returns only the pressure in hPa.
IsDataReady
Returns True if data is ready to be read from
the sensor, otherwise False
CtoF(C, d = 1)
Function converts from Celsius to Fahrenheit
C : Celsius value to convert to Fahrenheit
d : Number of decimals for rounding to.
Example:
# Read temperature and pressure from
# a BMP390 sensor.
from fc_bmp3xx import *
from microbit import sleep
sensor = BMP390()
# Put sensor into continuous
# sampling mode.
sensor.SetMode(1, 5)
# Is there any data available?
AnyData = sensor.IsDataReady
print('Any data?', AnyData)
sleep(500)
# Try again!
AnyData = sensor.IsDataReady
print('Any data now?', AnyData)
# We have data!
print('Fetching data...')
print(sensor.Reading)
# Test the T and P properties.
sleep(500)
t = sensor.T
f = CtoF(t, 5)
print('\nTemperature:', t, 'C')
print(t, 'C is', f, 'F')
print('Pressure:', sensor.P)
Typical Output:
Any data? False
Any data now? True
Fetching data...
(16.85474, 957.497)
Temperature: 16.85496 C
16.85496 C is 62.33892 F
Pressure: 957.497
In the above example there is no data available from the sensor when the IsDataReady property is first read. This is because the sensor has been configured for continuous sampling with a sampling period of 160ms (odr_set=5) and this amount of time had not yet elapsed.
Waiting for 500ms ensured that there was now data ready to be read from the sensor.
The FIFO Queue
The FIFO queue is a 512 byte first-in, first-out buffer where the sensor will store temperature and/of pressure uncompensated data records.
Commands written to registers configure options and kick off the storage process. It's important to note that while the sensor is storing data to the FIFO it is still possible for the user to read temperature and/or pressure with the Reading, T and P properties. This is demonstrated in an example below. Read the datasheet for full details.
These are the first Bosch BMPxxx barometric pressure sensors to implement this feature. In our view it is way over engineered. It's interesting to note that the newer generation Bosch sensors (BMP580/581/585) all retain the FIFO queue but in a very much simplified form.
This driver does implement the FIFO queue but keeps things reasonably simple. For example in all cases both temperature and pressure uncompensated values are stored. The queue is also configured not to be overwritten when it becomes full.
FIFO storage can be initiated and stopped. The entire buffer can also be manually flushed.
There are properties that return the number of records in the queue and if the queue is full. Another property reads the entire FIFO in a burst read and converts any uncompensated records found to actual temperatures and pressures.
Syntax:
FIFOStart()
This method starts the storage of records
to the FIFO.
FIFOStop()
This method stops the storage of records
to the FIFO.
FIFOFlush()
This method flushes (empties) the FIFO queue.
IsFIFOFull
This property returns True if the queue is
full. When full no more records are stored
till the queue is either read or flushed.
FIFOLength
This property returns the number of records
in the FIFO queue. See the datasheet for an
explanation of the structure of FIFO records.
FIFORead
A burst read of the FIFO is done to retrieve
all uncompensated temperature and pressure
records. This property performs the conversion
of the uncompensated values to actual
temperatures and pressures.
A list of tuples is returned. The first
element of each tuple is temperature.
The second tuple element is pressure.
Records read from the FIFO are also deleted
from the queue.
Example:
# Demonstrates the use of the BMP390 FIFO
from fc_bmp3xx import *
from microbit import *
sensor = BMP390()
print('Starting FIFO')
sensor.FIFOStart()
read = []
# At the same time that FIFO is automatically
# filling up, take a manual sensor reading.
while not sensor.IsFIFOFull:
sleep(5 * (2 ** sensor.GetODR))
read.append(sensor.Reading)
# FIFO is now full
print('FIFO is now full')
print('FIFO length:', sensor.FIFOLength)
# Get FIFO compensated temperatures
# and pressures.
# Output the first five sets of manual and
# FIFO readings for comparison.
queue = sensor.FIFORead
print('\n Reading FIFO')
for i in range(5):
print(read[i], ' ', queue[i])
# Collect some more data, check FIFO length
print('\nCollecting some more data...')
sleep(500)
print('FIFO length:', sensor.FIFOLength)
# Stop FIFO, check queue length
print('Stopping FIFO')
sensor.FIFOStop()
sleep(500)
print('FIFO length:', sensor.FIFOLength)
# Flush FIFO, check queue length
print('\nFlushing FIFO')
sensor.FIFOFlush()
print('FIFO length:', sensor.FIFOLength)
Typical Output:
Starting FIFO
FIFO is now full
FIFO length: 508
Reading FIFO
(22.94184, 968.1693) (22.94184, 968.1693)
(22.94285, 968.1684) (22.94285, 968.1684)
(22.94441, 968.1673) (22.94441, 968.1673)
(22.94619, 968.1661) (22.94619, 968.1661)
(22.94822, 968.1649) (22.94822, 968.1649)
Collecting some more data...
FIFO length: 63
Stopping FIFO
FIFO length: 63
Flushing FIFO
FIFO length: 0
The above example demonstrates the use of all FIFO methods and properties.
The FIFO process is turned on. At the same time manual readings are taken using the Reading property. A loop monitors the queue till it is full. The queue is then read and the compensated temperature and pressure values are returned.
The first five records from the FIFO are compared to the manually obtained readings. As shown in the Typical Output the manual and queue values match.
Altitude Calculation
This driver provides three different altitude calculations.
Mean Sea Level PressureThe absolute barometric pressure measured with a sensor cannot be compared to absolute values from other weather stations unless they're all at the same altitude.
To get around this problem the accepted practice is to convert pressure readings to their sea level equivalents. These are the the pressure values that are published by meteorological services. The technical term is MSLP (Mean Sea Level Pressure).
This driver provides the MSLP() method to perform this calculation. However it can only be done if the BMP388/390's elevation above sea level is known with reasonable accuracy. This can be obtained from such sources as the local government authority, contour maps or, at a pinch, from GPS.
Syntax:
MSLP(Altitude)
This method returns the mean sea level
pressure given the altitude of the sensor
in metres.
Example:
# Demonstrates the MSLP calculation.
from fc_bmp3xx import *
from microbit import *
sensor = BMP390()
altitude = 400 # metres
mslp = sensor.MSLP(altitude)
print('Altitude is ', altitude, 'metres')
print('Mean Sea Level Pressure is', mslp, 'hPa')
Typical Output:
Altitude is 400 metres
Mean Sea Level Pressure is 1014.803 hPa
Calculating the Altitude
If the mean sea level pressure is known than the altitude of the sensor can be calculated. The MSLP can be obtained from an official weather station if this station is at the same elevation as the sensor.
Syntax:
Altitude(MSLP)
This method returns the altitude if the
MSLP (in hPa) is known.
Example:
# Demonstrates calculation of altitude.
from fc_bmp3xx import *
from microbit import *
sensor = BMP390()
mslp = 1015 # hPa
altitude = sensor.Altitude(mslp)
print('Mean Sea Level Pressure is', mslp, 'hPa')
print('Altitude is ', altitude, 'metres')
Output:
Mean Sea Level Pressure is 1015 hPa
Altitude is 402 metres
Calculating Difference in Altitude
If absolute barometric pressures are obtained from the sensor at two different altitudes it is possible to calculate the difference between the two altitudes.
Syntax:
AltDiff(P1, P2)
This method returns the difference in
altitude in metres if the absolute
pressure is known for both altitudes.
Example:
# Calculates the difference between
# two different altitudes given the
# absolute barometric pressures of
# the two altitudes.
from fc_bmp3xx import *
from microbit import *
sensor = BMP390()
p1 = 975 # hPa
p2 = 974 # hPa
diff = sensor.AltDiff(p1, p2)
print('Absolute pressure at P1:', p1, 'hPa')
print('Absolute pressure at P2:', p2, 'hPa')
print('Altitude difference:', int(diff), 'M')
Output:
Absolute pressure at P1: 975 hPa
Absolute pressure at P2: 974 hPa
Altitude difference: 8 M
Chip ID
Since the second generation BMP180 sensor all Bosch BMPxxx barometric pressure sensors have had an 8-bit model-specific ID number written to NVM (non-volatile memory) during manufacture. The specific chip ID's for BMP388 and BMP390 are:
- BMP388 : (0x50)
- BMP390 : (0x60)
The BMP390 also has an 8-bit REV_ID number with a value of 0x01.
Syntax:
ID
This property returns the sensor's ID number.
RevID
This property returns the REV_ID number of
the BMP390.
Example:
# The following I2C addresses are assumed:
# BMP388 : 0x76
# BMP390 : 0x77
from fc_bmp3xx import *
bmp390 = BMP390()
bmp388 = BMP388(0x76)
print('BMP388 ID:', bmp388.ID)
print('BMP388 REV_ID:', bmp388.RevID)
print('\nBMP390 ID:', bmp390.ID)
print('BMP390 REV_ID:', bmp390.RevID)
Output:
BMP388 ID: 0x50
BMP388 REV_ID: None
BMP390 ID: 0x60
BMP390 REV_ID: 0x1
Sensor Time
The BMP388/390 sensors have a 24-bit sensor time that is user readable. The datasheets and other Bosch literature (that we found) don't explain how this is useful. A sensor time record can be appended when FIFO records are being read so it may have application in synchronising sets of FIFO records.
This is first of the BMPxxx barometric pressure sensors to have this feature. It also seems to have been dropped from the next generation BMP580/581/585 series.
Syntax:
Time
This property returns the sensor's 24-bit time.
Example:
# This example compares the sensor 'tick'
# of the BMP388 and BMP390
# The following I2C addresses are assumed:
# BMP388 : 0x76
# BMP390 : 0x77
from fc_bmp3xx import *
from microbit import sleep
bmp390 = BMP390()
bmp388 = BMP388(0x76)
t388_start = bmp388.Time
t390_start = bmp390.Time
sleep(60000) # 1 minute
t388_end = bmp388.Time
t390_end = bmp390.Time
t388_tick = (t388_end - t388_start) / 60000
t390_tick = (t390_end - t390_start) / 60000
print('BMP388 start time:', t388_start)
print('BMP388 end time:', t388_end)
print('BMP388 ticks/ms:', t388_tick)
print('\nBMP390 start time:', t390_start)
print('BMP390 end time:', t390_end)
print('BMP390 ticks/ms:', t390_tick)
Typical Output:
BMP388 start time: 573
BMP388 end time: 1561094
BMP388 ticks/ms: 26.00868
BMP390 start time: 1275
BMP390 end time: 1582087
BMP390 ticks/ms: 26.34687
The above example collects the number of sensor time 'ticks' for a one minute period. This is divided by 60,000 to calculate ticks/ms.
This simple test is a bit crude but never the less both sensors returned a clock rate of ~26 ticks/ms or 26kHz.
NOTE: A clock speed of 25.6 kHz is a common frequency for various sensor types. This frequency is often used for sensors used in industrial automation, vibration monitoring and some audio processing applications.
Enjoy!
BMP388 & BMP390 Driver Code for micro:bit
Download as zip file
'''
BMP388 & BMP390 Temperature and
Barometric Pressure sensor
MicroPython driver for micro:bit
This driver is for the user that wishes to
explore and exploit the full feature set
of the BMP388 and BMP390 sensors.
EXAMPLE USAGE:
from fc_bmp3xx import *
sensor = BMP390()
Temperature = sensor.T
Pressure = sensor.P
Modes:
0 : Ultra low power
Sample mode = Forced
Sampling period = 5ms
1 : Highest resolution
Sample mode = Normal
Sampling period = 80ms (default)
The sampling period is user configurable.
Temperature returned in Celsius.
Pressure returned in hPa.
AUTHOR: fredscave.com
DATE : 2025/06
VERSION : 1.00
'''
from microbit import *
from micropython import const
from ustruct import *
_REG_CHIP_ID = const(0x00)
_REG_REV_ID = const(0x01)
_REG_STATUS = const(0x03)
_REG_MEASURE = const(0x04)
_REG_TIME = const(0x0C)
_REG_INT_STATUS = const(0x11)
_REG_FIFO_LENGTH = const(0x12)
_REG_FIFO_DATA = const(0x14)
_REG_FIFO_CMD = const(0x17)
_REG_FIFO_CONFIG = const(0x18)
_REG_INT_CTRL = const(0x19)
_REG_PWR_CTRL = const(0x1B)
_REG_OSR = const(0x1C)
_REG_ODR = const(0x1D)
_REG_CONFIG = const(0x1F)
_REG_CMD = const(0x7E)
_REG_COEFF = const(0x31)
_CMD_CTRL_SLEEP = const(0x00)
_CMD_CTRL_FORCED = const(0x13)
_CMD_CTRL_NORMAL = const(0x33)
_CMD_RESET = const(0xB6)
_CMD_FIFO_START = const(0x1B)
_CMD_FIFO_STOP = const(0x00)
_CMD_FIFO_FLUSH = const(0xB0)
_FIFO_CONFIG = const(0x08)
_FIFO_SENSOR_FRAME = const(0x94)
_ADCT_FORCED = const(0x05)
# Over-sampling setting per mode.
OSR = (0x00, 0x0D)
# IIR filter coefficient per mode.
CONFIG = (0x00, 0x0A)
# Convert Celsius to Fahrenheit
CtoF = lambda C, d=1: round((C * 9/5) +32, d)
# Convert feet to meters
FtoM = lambda F: int(F / 3.28084)
class BMP3XX():
def __init__(self, ADDR=0x77):
self.ADDR = ADDR
self._Load_Calibration_Data()
self.SetMode()
# Mode 0 : Forced readings, lower resolution
# Mode 1 : Continuous readings, highest resolution
# odr_set defines period of continuous sampling.
# It has a value between 4 and 17 where
# Period = 5 * (odr_set ** 2) in ms.
def SetMode(self, Mode=0, odr_set=4):
if Mode not in (0, 1):
Mode = 0
self.Mode = Mode
self._Reset()
sleep(20)
# Set IIR Filter Coefficient.
self._writeReg([_REG_CONFIG, CONFIG[self.Mode]])
# Set temperature & pressure over-sampling.
self._writeReg([_REG_OSR, OSR[self.Mode]])
if self.Mode == 1:
if odr_set < 4:
odr_set = 4
elif odr_set > 17:
odr_set = 17
self.odr_set = odr_set
# Start Normal sampling
self._writeReg([_REG_ODR, odr_set])
self._writeReg([_REG_PWR_CTRL, _CMD_CTRL_NORMAL])
#sleep(int(5 * 2**odr_set + 1))
else:
self.odr_set = None
# If Mode 1 (Normal) is active this method
# will put the sensor to sleep.
# All registers are still available but
# no measurements are taken.
def SleepOn(self):
if self.Mode == 1:
self._writeReg([_REG_PWR_CTRL, _CMD_CTRL_SLEEP])
# Wakes the sensor up into Mode 1.
# This Normal (continuous) sampling with the
# settings that were in place before sleep.
def SleepOff(self):
if self.Mode == 1:
self.SetMode(1)
# *******************************************
# Properties
# *******************************************
# Trigger and return both temperature
# and pressure measurements.
@property
def Reading(self):
# Read uncompensated temperature and pressure.
if self.Mode == 0:
self._writeReg([_REG_PWR_CTRL, _CMD_CTRL_FORCED])
sleep(_ADCT_FORCED)
buf = self._readReg(_REG_MEASURE, 6)
pressure = buf[0] + buf[1]*256 + buf[2]*65536
temperature = buf[3] + buf[4]*256 + buf[5]*65536
# Convert to actual values
return self._Compensate(temperature, pressure)
# Returns temperature only
@property
def T(self):
return self.Reading[0]
# Returns pressure only
@property
def P(self):
return self.Reading[1]
# Returns the chip's ID
@property
def ID(self):
id = self._readReg(_REG_CHIP_ID, 1)
return hex(id[0])
# Chip revision number.
# Overridden by child method where appropriate.
@property
def RevID(self):
return None
# Returns the sensor time.
# Clock frequency appears to be ~26kHz.
# Value returned is an unsigned 32-bit integer.
# There is no information in the product
# datasheet about its meaning.
@property
def Time(self):
buf = self._readReg(_REG_TIME, 3)
time = buf[2] * 65536 + buf[1] * 256 + buf[0]
return time
# Returns True if there is pressure and/or
# temperature value(s) ready to be read.
@property
def IsDataReady(self):
buf = self._readReg(_REG_STATUS, 1)
return (buf[0] & 0b1100000) == 0b1100000
# Returns Mode where:
# 0 : Forced sampling mode.
# 1 : Normal (continuous) sampling mode.
@property
def GetMode(self):
return self.Mode
# Returns odr_set parameter.
# This value determines the sampling period
# when the sensor is in Mode 1 i.e Normal
# (continuous) sampling mode.
# Period = 5 * (odr_set ** 2) in ms.
@property
def GetODR(self):
return self.odr_set
# *******************************************
# FIFO Methods and Properties
# *******************************************
# Starts writing temperature and pressure
# uncompensated values to the FIFO queue.
def FIFOStart(self):
# Ensure Normal (Continuous) Sampling mode is on.
if self.Mode != 1:
self.SetMode(1)
self._writeReg([_REG_FIFO_CONFIG, _FIFO_CONFIG])
self._writeReg([_REG_FIFO_CMD, _CMD_FIFO_START])
# Stops writing to the FIFO queue.
def FIFOStop(self):
self._writeReg([_REG_FIFO_CMD, _CMD_FIFO_STOP])
# Flushes the FIFO queue. All stored data is lost.
def FIFOFlush(self):
self._writeReg([_REG_CMD, _CMD_FIFO_FLUSH])
# Returns the number of bytes used in the FIFO queue.
@property
def FIFOLength(self):
buf = self._readReg(_REG_FIFO_LENGTH, 2)
return buf[0] + buf[1] * 256
# Reads all bytes in the FIFO queue.
# Sensor data frames are parsed to retrieve
# all stored uncompensated temperature and
# pressure values. They are compensated and
# returned as a list of tuples of actual values.
@property
def FIFORead(self):
length = self.FIFOLength
buf = self._readReg(_REG_FIFO_DATA, length)
i = 0
data = []
while i < length:
if buf[i] == _FIFO_SENSOR_FRAME:
temperature = buf[i+1] + buf[i+2]*256 + buf[i+3]*65536
pressure = buf[i+4] + buf[i+5]*256 + buf[i+6]*65536
i += 7
data.append(self._Compensate(temperature, pressure))
else:
i += 1
return data
# Returns True if the FIFO queue is full.
@property
def IsFIFOFull(self):
buf = self._readReg(_REG_INT_STATUS, 1)
return (buf[0] & 0b10) == 0b10
# *******************************************
# 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
# *******************************************
# Convert uncompensated temperature and pressure
# to actual (compensated) values.
def _Compensate(self, temperature, pressure):
# Calculate actual temperature.
pd1 = temperature - 256 * self.T1
pd2 = self.T2 * pd1
pd3 = pd1 * pd1
pd4 = pd3 * self.T3
pd5 = (pd2 * 262144) + pd4
pd6 = pd5 / 4294967296
t_lin = pd6
comp_temp = (pd6 * 25) / 16384
# Calculate actual pressure
pd1 = t_lin * t_lin
pd2 = pd1 / 64
pd3 = (pd2 * t_lin) / 256
pd4 = (self.P8 * pd3) / 32
pd5 = self.P7 * pd1 * 16
pd6 = self.P6 * t_lin * 4194304
offset = (self.P5 * 140737488355328) + pd4 + pd5 + pd6
pd2 = (self.P4 * pd3) / 32
pd4 = (self.P3 * pd1) * 4
pd5 = (self.P2 - 16384) * t_lin * 2097152
sensitivity = ((self.P1 - 16384) * 70368744177664) + pd2 + pd4 + pd5
pd1 = (sensitivity / 16777216) * pressure
pd2 = self.P10 * t_lin
pd3 = pd2 + (65536 * self.P9)
pd4 = pd3 * pressure / 8192
pd5 = (pressure * pd4) / 512
pd6 = pressure * pressure
pd2 = (self.P11 * pd6) / 65536
pd3 = pd2 * pressure / 128
pd4 = (offset / 4) + pd1 +pd5 +pd3
comp_press = pd4 *25 / 1099511627776
return comp_temp / 100, comp_press / 10000
# Get the trimming constants from NVM.
def _Load_Calibration_Data(self):
fmt = '<HHbhhbbHHbbhbb'
coeff = self._readReg(_REG_COEFF, 21)
coeff = unpack(fmt, coeff)
self.T1 = coeff[0]
self.T2 = coeff[1]
self.T3 = coeff[2]
self.P1 = coeff[3]
self.P2 = coeff[4]
self.P3 = coeff[5]
self.P4 = coeff[6]
self.P5 = coeff[7]
self.P6 = coeff[8]
self.P7 = coeff[9]
self.P8 = coeff[10]
self.P9 = coeff[11]
self.P10 = coeff[12]
self.P11 = coeff[13]
# Writes one or more bytes to register.
# Bytes is expected to be a list.
# First element is the register address.
def _writeReg(self, Bytes):
i2c.write(self.ADDR, bytes(Bytes))
# Read a given number of bytes from
# a register.
def _readReg(self, Reg, Num):
self._writeReg([Reg])
buf = i2c.read(self.ADDR, Num)
return buf
# Performs a soft reset.
# All registers are loaded with power-on values.
def _Reset(self):
self._writeReg([_REG_CMD, _CMD_RESET])
# *******************************************
# Derived Classes
# *******************************************
class BMP390(BMP3XX):
@property
def Type(self):
return 'BMP390'
# Chip revision number
@property
def RevID(self):
id = self._readReg(_REG_REV_ID, 1)
return hex(id[0])
class BMP388(BMP3XX):
@property
def Type(self):
return 'BMP388'
Comparing BMP388 with BMP390
It's easy to use a BMP388 and BMP390 simultaneously on the same I2C bus on the micro:bit.
Hookup guideMake the following connections:
- micro:bit 3.3V to VCC pins on both boards.
- micro:bit GND to GND pins on both boards.
- micro:bit Pin 19 to SCL pins on both boards.
- micro:bit pin 20 to SDA pins on both board.
- On the BMP388 board, connect the GND pin to the SDO pin. This is the white wire in the image below. This ensures the alternative I2C address of 0x76.
The fc_bmp3xx.py driver file must also be on the micro:bit's file system.
# Compare temperature & pressure readings
# from BMP388 and BMP390 sensors.
# A BMP388 sensor and a BMP390 sensor
# will be used on the same I2C bus.
# BMP388:
# Address = 0x76
# SDO is tied to GND.
# BMP390:
# Address = 0x77
from fc_bmp3xx import *
# Create BMP388 and BMP390 sensor objects.
bmp388 = BMP388(0x76)
bmp390 = BMP390()
# Read temperature and pressure
# from both sensors.
# Both sensors are at an altitude of 400m.
T1, P1 = bmp388.T, bmp388.P
MSLP1 = bmp388.MSLP(400)
T2, P2 = bmp390.T, bmp390.P
MSLP2 = bmp390.MSLP(400)
# Report
print(' T P MSLP')
print(bmp388.Type, ' ', T1, ' ', P1, ' ', MSLP1)
print(bmp390.Type, ' ', T2, ' ', P2, ' ', MSLP2)
Typical Output:
T P MSLP
BMP388 17.71968 955.8941 1003.131
BMP390 17.84964 956.0693 1003.241
This program was run with micro:bit and sensors at an altitude of 400 metres. If you know your altitude above seal level then you can change the line in the above code where the MSLP is calculated.
The local weather service's published barometric pressure observation was 1002 hPa. The BMP388 sensor and BMP390 sensor gave close results for temperature and pressure.