MicroPython Driver for BMP280 Sensor
Contents
Introduction
This a MicroPython driver written specifically for the BBC micro:bit that will work with the BMP280 ambient temperature and barometric pressure sensor.
This driver is now in Version 2.00 which is a major rewrite of the original (Version 1.00)
The BMP280 sensor is discussed in some detail here.
The BMP280 is the next generation replacement for the BMP180 sensor.
The sensor is quite small and for projects using a breadboard its much easier to use a BMP280 breakout board. The breakout boards are quite inexpensive. The breakout boards still appear to be readily available at time of writing (May 2025).
Connecting the BMP280
The BMP280 communicates via I2C and SPI. This driver will only use I2C. Hooking it up to the micro:bit for I2C is easy:
| micro:bit | BMP280 |
|---|---|
| 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 VCC 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 BMP280 functionality as described in the product's Datasheet.
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_bmp280.py - OR -
- Download as a zip file using the link. Unzip the file and save it as fc_bmp280.py into the default directory where the MicroPython editor e.g. Mu Editor saves python code files.
After saving the fc_bmp280.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 BMP280 breakout board has a default I2C address of 0x76. This can be changed to 0x77 if a jumper wire is connected between the VCC pin and SDO pin.
Class constructorThe driver is implemented as a class. The first thing to do is call the constructor. This provides an instance of the class.
Syntax:
BMP280(ADDR=0x76)
Where:
ADDR : The I2C address.
• Default is 0x76
• An alternative 0x77 is possible
by using a jumper between the
SDO pin and VCC pin.
Example
from fc_bmp280 import *
# Declare a sensor object with default address.
sensor1 = BMP280()
# Declare a sensor object with alternate address.
sensor2 =BMP280(0x77)
This assumes that the file fc_bmp280.py has been successfully copied to the micro:bit's filesystem as described above.
Methods and PropertiesThe driver provides methods to:
- Set the sampling mode
- Perform a soft reset
- Perform some basic altitude calculations
The driver also provides properties to:
- Get the sampling mode
- Read temperature and pressure
- Return the sensor chip's ID
The driver implements most functions described in the product Datasheet.
Get & Set the Sampling Mode
There are a myriad of possible combinations with temperature and pressure oversampling and the IIR filter coefficients. Based on suggestions in the Datasheet there are five different sampling modes preconfigured in this driver.
The BMP280 is very sensitive to pressure changes caused by extraneous air movement or currents e.g. doors closing, people movement or breeze. If the sensor is being used in such an environment choose a higher resolution mode that will combat these disturbances.
If the sensor is in an environmental enclosure or air disturbance is minimal then use a lower power mode.
Syntax:
SetMode(Mode=2)
Sets the sampling mode:
0 : Ultra low power, lowest resolution
1 : Low power
2 : Standard (default)
3 : High resolution
4 : Highest resolution
GetMode
Property that returns the current mode.
Example:
from fc_bmp280 import *
# Declare sensor object
# with default sampling mode
sensor = BMP280()
print('Sensor sampling mode:', sensor.GetMode)
# Change sampling mode
print('Changing sampling mode...')
sensor.SetMode(4)
print('Sensor sampling mode:', sensor.GetMode)
Output:
Sensor sampling mode: 2
Changing sampling mode...
Sensor sampling mode: 4
Reading Temperature and Pressure
The BMP280 has two power modes:
- Forced Mode: A single measurement is taken. The sensor then enters a sleep state until the next measurement is requested.
- Normal Mode : Measurements are taken on a continuous cycle. Between each measurement there is a user defined standby period.
This driver only uses the Forced Mode. The user simply requests the value of a property (choice of three) to obtain temperature and/or pressure at any time and frequency. All wait times, etc are handled internally by the driver.
There are three properties and one function that return temperature and/or pressure readings.
Syntax:
Reading
Property that returns temperature
and pressure as a list.
• Temperature is in degrees Celsius.
• Pressure is in HPa (hectopascals)
Example:
from fc_bmp280 import *
sensor = BMP280()
reading = sensor.Reading
print('[Temperature, Pressure] :', reading)
Typical Output:
[Temperature, Pressure] : [19.36, 975.08]
Syntax:
CtoF(C, d = 2)
Function that converts Celsius to Fahrenheit
Where:
C : Celsius value to convert to Fahrenheit
d : Number of rounded decimals.
Example:
from fc_bmp280 import *
C = 11.98 # Degrees Celsius
F = CtoF(C) # Converted to Fahrenheit
print('11.98 C =', F, 'F')
Output:
11.98 C = 53.56 F
Syntax:
T
Property that returns the temperature.
Syntax:
P
Property that returns the pressure.
Example:
from fc_bmp280 import *
sensor = BMP280()
print('Temperature:', sensor.T)
print('Pressure:', sensor.P)
Sample Output:
Temperature: 19.48
Pressure: 975.07
Altitude Calculations
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 BMP280'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_bmp280 import *
from microbit import *
sensor = BMP280()
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 1022.199 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_bmp280 import *
from microbit import *
sensor = BMP280()
mslp = 1022 # hPa
altitude = sensor.Altitude(mslp)
print('Mean Sea Level Pressure is', mslp, 'hPa')
print('Altitude is', altitude, 'metres')
Typical Output:
Mean Sea Level Pressure is 1022 hPa
Altitude is 395 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_bmp280 import *
from microbit import *
sensor = BMP280()
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
Getting the Chip ID
BMP280 chips have the ID value 0x58 written to non-volatile memory (NVM) at point of manufacture . The following property can be used to check that a BMP280 chip is connected to the microcontroller's I2C bus.
Syntax:
ID
Example:
from fc_bmp280 import *
sensor = BMP280()
print('BMP280 ID:', sensor.ID)
Expected Output:
BMP280 ID: 0x58
BMP280 Soft Reset
The Reset() method causes the BMP280 to perform a soft reset. All power-on defaults are reloaded.
The sampling mode that was last in use is restored.
Most users might never use this method but it is presented here for completeness.
Syntax:
Reset()
Causes the BMP280 sensor to perform a soft reset.
Example:
from fc_bmp280 import *
sensor = BMP280()
sensor.Reset()
Enjoy!
BMP280 Driver Code for micro:bit
Download as zip file
'''
BMP280 Temperature and Barometric Pressure sensor
MicroPython driver for micro:bit
This is a major revision of Ver 1.00
EXAMPLE USAGE:
from fc_bmp280 import *
sensor = BMP280()
Temperature = sensor.T
Pressure = sensor.P
Modes:
0 : Ultra low power
1 : Low power
2 : Standard (default)
3 : High resolution
4 : Ultra high resolution
Temperature returned in Celsius.
Pressure returned in hPa.
AUTHOR: fredscave.com
DATE : 2025/06
VERSION : 2.00
'''
from microbit import *
from micropython import const
_REG_MEASURE = const(0xF7)
_REG_CTRL_MEAS = const(0xF4)
_REG_CONFIG = const(0xF5)
_REG_ID = const(0xD0)
_REG_RESET = const(0xE0)
_CMD_RESET = const(0xB6)
# Force measurement configuration per mode.
CTRL_MEAS = (0x25, 0x29, 0x2D, 0x31, 0x55)
# IIR filter coefficient per mode.
CONFIG = (0x00, 0x04, 0x08, 0x0C, 0x10)
# Sample conversion time (ms) per mode.
ADCT = (8, 10, 15, 24, 45)
# Convert Celsius to Fahrenheit
CtoF = lambda C, d=2: round((C * 9/5) +32, d)
class BMP280():
def __init__(self, ADDR=0x76):
self.ADDR = ADDR
self.SetMode()
self._Load_Calibration_Data()
# Mode 0 : Ultra low power
# Mode 1 : Low power
# Mode 2 : Standard (default)
# Mode 3 : High resolution
# Mode 4 : Ultra high resolution
def SetMode(self, Mode=2):
if Mode in range(0, 5):
self.Mode = Mode
else:
self.Mode = 2
self.Reset()
# Performs a soft reset.
# All registers are loaded with power-on values.
# The selected sampling mode is reconfigured.
def Reset(self):
self._writeReg([_REG_RESET, _CMD_RESET])
sleep(20)
self._writeReg([_REG_CONFIG, CONFIG[self.Mode]])
# *******************************************
# Properties
# *******************************************
# Trigger temperature and pressure measurements.
# Read uncompensated data and do the compensation
# calculations.
@property
def Reading(self):
# Read uncompensated temperature and pressure.
self._writeReg([_REG_CTRL_MEAS, CTRL_MEAS[self.Mode]])
sleep(ADCT[self.Mode])
buf = self._readReg(_REG_MEASURE, 6)
adc_P = (buf[0] << 12) + (buf[1] << 4) + (buf[2] >> 4)
adc_T = (buf[3] << 12) + (buf[4] << 4) + (buf[5] >> 4)
# Convert to actual temperature and pressure.
return self._Compensate(adc_T, adc_P)
# Returns temperature
@property
def T(self):
temp = self.Reading
return temp[0]
# Returns pressure
@property
def P(self):
pressure = self.Reading
return pressure[1]
# Returns Mode (0..4)
@property
def GetMode(self):
return self.Mode
# Returns the chip's ID
@property
def ID(self):
id = self._readReg(_REG_ID, 1)
return hex(id[0])
# *******************************************
# 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
# *******************************************
# Get the trimming constants from NVM.
def _Load_Calibration_Data(self):
self.dig_T1 = self._getCal(0x88, 0)
self.dig_T2 = self._getCal(0x8A, 1)
self.dig_T3 = self._getCal(0x8C, 1)
self.dig_P1 = self._getCal(0x8E, 0)
self.dig_P2 = self._getCal(0x90, 1)
self.dig_P3 = self._getCal(0x92, 1)
self.dig_P4 = self._getCal(0x94, 1)
self.dig_P5 = self._getCal(0x96, 1)
self.dig_P6 = self._getCal(0x98, 1)
self.dig_P7 = self._getCal(0x9A, 1)
self.dig_P8 = self._getCal(0x9C, 1)
self.dig_P9 = self._getCal(0x9E, 1)
# Does the grunt work retrieving the
# trimming constants from the device's
# non-volatile memory (NVM).
def _getCal(self, Reg, Signed):
buf = self._readReg(Reg, 2)
d = buf[1]*256 + buf[0]
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, adc_T, adc_P):
var1 = (((adc_T>>3)-(self.dig_T1<<1))*self.dig_T2) >>11
var2 = (((((adc_T >>4)-self.dig_T1)*((adc_T >>4) - self.dig_T1)) >>12)*self.dig_T3) >>14
t_fine = var1 + var2
T = ((t_fine * 5 + 128) >> 8)/100
# Calculate actual pressure
var1 = (t_fine >> 1) - 64000
var2 = (((var1 >> 2) * (var1 >> 2)) >> 11 ) * self.dig_P6
var2 = var2 + ((var1 * self.dig_P5) << 1)
var2 = (var2 >> 2)+(self.dig_P4 << 16)
var1 = (((self.dig_P3*((var1>>2)*(var1>>2))>>13)>>3) + (((self.dig_P2) * var1)>>1))>>18
var1 = ((32768 + var1) * self.dig_P1) >> 15
if var1 == 0:
return # avoid exception caused by division by zero
p = ((1048576 - adc_P) - (var2 >> 12)) * 3125
if p < 0x80000000:
p = (p << 1) // var1
else:
p = (p // var1) * 2
var1 = (self.dig_P9 * (((p >> 3) * (p >> 3)) >> 13)) >> 12
var2 = (((p >> 2)) * self.dig_P8) >> 13
P = p + ((var1 + var2 + self.dig_P7) >> 4)
return [T, P/100]
# 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
Using Two BMP280 Sensors
It's easy to use two BMP280 simultaneously on the same I2C bus.
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 one only board, connect the VCC pin to the SDO pin. This is the white wire in the image below.
The fc_bmp280.py driver file must also be on the micro:bit's file system.
# Test MicroPython driver for the BMP280
# temperature and barometric pressure sensor.
# Two BMP280 sensors will be used on the
# same I2C bus.
# Sensor1:
# Address = 0x76
# Sensor2:
# Address = 0x77
# SDO is tied to VCC.
from fc_bmp280 import *
# Create 2 x BMP80 sensor objects.
Sensor1 = BMP280()
Sensor1.SetMode(4)
Sensor2 = BMP280(ADDR=0x77)
Sensor2.SetMode(4)
# Read temperature and pressure
# from both sensors.
T1, P1 = Sensor1.T, Sensor1.P
altitude = 400 # Metres
MSLP1 = Sensor1.MSLP(altitude)
T2, P2 = Sensor2.T, Sensor2.P
MSLP2 = Sensor2.MSLP(altitude)
# Report
print(' T P MSLP')
print('Sensor 1', ' ', T1, ' ', P1, ' ', MSLP1)
print('Sensor 2', ' ', T2, ' ', P2, ' ', MSLP2)
Typical Output:
T P MSLP
Sensor 1 19.68 975.09 1022.239
Sensor 2 19.98 976.88 1024.009
The local weather service's published barometric pressure observation was 1023 hPa.