BMP280 barometric pressure sensor Micropython example

In this example we connect a BMP280 barometric pressure sensor to an ESP32 running Micropython

BMP280 is an absolute barometric pressure sensor especially designed for mobile applications. The sensor module is housed in an extremely compact package. Its small dimensions and its low power consumption allow for the implementation in battery powered devices such as mobile phones, GPS modules or watches.

As its predecessor BMP180, BMP280 is based on Bosch’s proven Piezo-resistive pressure sensor technology featuring high accuracy and linearity as well as long term stability and high EMC robustness. Numerous device operation options offer highest flexibility to optimize the device regarding power consumption, resolution and filter performance. A tested set of default settings for example use case is provided to the developer in order to make design-in as easy as possible.

Applications

– Enhancement of GPS navigation (e.g. time-tofirst-fix improvement, dead-reckoning, slope detection)
– Indoor navigation (floor detection, elevator detection)
– Outdoor navigation, leisure and sports applications
– Weather forecast
– Health care applications (e.g. spirometry)
– Vertical velocity indication (e.g. rise/sink speed)

Parameter Technical data
Operation range (full accuracy) Pressure: 300…1100 hPa
Temperature: -40…85°C
Absolute accuracy
(Temp. @ 0…+65°C)
~ ±1 hPa
Relative accuracy
p = 700…900hPa
(Temp. @ +25…+40°C)
± 0.12 hPa (typical)
equivalent to ±1 m
Average current consumption (1 Hz data refresh rate) 2.74 μA, typical
(ultra-low power mode)
Average current consumption in sleep mode 0.1 μA
Average measurement time 5.5 msec
(ultra-low power preset)
Supply voltage VDDIO 1.2 … 3.6 V
Supply voltage VDD 1.71 … 3.6 V
Resolution of data Pressure: 0.01 hPa ( < 10 cm)
Temperature: 0.01° C
Temperature coefficient offset
(+25°…+40°C @900hPa)
± 0.12 hPa (typical)
equivalent to ±1 m
Interface I²C and SPI

 

Parts List

I actually used a GY-21P which is a combined BMP280 and Si7021 sensor in one module. We only deal with the BMP280 module here

Name Link
ESP32 WEMOS WiFi & Bluetooth Battery ESP32 development tool ESP32 battery esp8266 ESP WROOM 32 ESP32
BMP280 Atmospheric Humidity Temperature Sensor Breakout Barometric Pressure BMP280 SI7021 for Arduino GY-21P
Connecting cables Free shipping Dupont line 120pcs 20cm male to male + male to female and female to female jumper wire

 

Connection

An easy module to connect to an ESP32, SCL is 22 and SDA is 21 on the Wemos board I used, you can see this is the schematic below

esp32 and bmp280
esp32 and bmp280

Code

You can use any method to upload files or an IDE for development.

The following is based on a github library – https://github.com/Dafvid/micropython-bmp280 . The first part of this is the library which I upload to my ESP32

This is the library called bmp280.py

from ustruct import unpack as unp
import utime
 
# Author David Wahlund david@dafnet.se
 
# Power Modes
NORMAL = 0
 
BMP280_TEMP_OS_SKIP = 0
BMP280_TEMP_OS_1 = 1
BMP280_TEMP_OS_2 = 2
BMP280_TEMP_OS_4 = 3
BMP280_TEMP_OS_8 = 4
BMP280_TEMP_OS_16 = 5
 
BMP280_PRES_OS_SKIP = 0
BMP280_PRES_OS_1 = 1
BMP280_PRES_OS_2 = 2
BMP280_PRES_OS_4 = 3
BMP280_PRES_OS_8 = 4
BMP280_PRES_OS_16 = 5
 
# BMP280 Temperature Registers
BMP280_REGISTER_DIG_T1 = 0x88
BMP280_REGISTER_DIG_T2 = 0x8A
BMP280_REGISTER_DIG_T3 = 0x8C
# BMP280 Pressure Registers
BMP280_REGISTER_DIG_P1 = 0x8E
BMP280_REGISTER_DIG_P2 = 0x90
BMP280_REGISTER_DIG_P3 = 0x92
BMP280_REGISTER_DIG_P4 = 0x94
BMP280_REGISTER_DIG_P5 = 0x96
BMP280_REGISTER_DIG_P6 = 0x98
BMP280_REGISTER_DIG_P7 = 0x9A
BMP280_REGISTER_DIG_P8 = 0x9C
BMP280_REGISTER_DIG_P9 = 0x9E
 
BMP280_REGISTER_ID = 0xD0
BMP280_REGISTER_RESET = 0xE0
BMP280_REGISTER_STATUS = 0xF3
BMP280_REGISTER_CONTROL = 0xF4
BMP280_REGISTER_CONFIG = 0xF5  # IIR filter config
 
BMP280_REGISTER_DATA = 0xF7
 
 
class BMP280:
    def __init__(self, i2c_bus, addr=0x76):
        self._bmp_i2c = i2c_bus
        self._i2c_addr = addr
 
        self._bmp_i2c.start()
        self.chip_id = self._read(BMP280_REGISTER_ID, 2)
 
        # read calibration data
        # < little-endian
        # H unsigned short
        # h signed short
        self._T1 = unp('<H', self._read(BMP280_REGISTER_DIG_T1, 2))[0]
        self._T2 = unp('<h', self._read(BMP280_REGISTER_DIG_T2, 2))[0]
        self._T3 = unp('<h', self._read(BMP280_REGISTER_DIG_T3, 2))[0]
        self._P1 = unp('<H', self._read(BMP280_REGISTER_DIG_P1, 2))[0]
        self._P2 = unp('<h', self._read(BMP280_REGISTER_DIG_P2, 2))[0]
        self._P3 = unp('<h', self._read(BMP280_REGISTER_DIG_P3, 2))[0]
        self._P4 = unp('<h', self._read(BMP280_REGISTER_DIG_P4, 2))[0]
        self._P5 = unp('<h', self._read(BMP280_REGISTER_DIG_P5, 2))[0]
        self._P6 = unp('<h', self._read(BMP280_REGISTER_DIG_P6, 2))[0]
        self._P7 = unp('<h', self._read(BMP280_REGISTER_DIG_P7, 2))[0]
        self._P8 = unp('<h', self._read(BMP280_REGISTER_DIG_P8, 2))[0]
        self._P9 = unp('<h', self._read(BMP280_REGISTER_DIG_P9, 2))[0]
 
        self._t_os = BMP280_TEMP_OS_2  # temperature oversampling
        self._p_os = BMP280_PRES_OS_16  # pressure oversampling
 
        # output raw
        self._t_raw = 0
        self._t_fine = 0
        self._t = 0
 
        self._p_raw = 0
        self._p = 0
 
        self._read_wait_ms = 100  # interval between forced measure and readout
        self._new_read_ms = 200  # interval between
        self._last_read_ts = 0
 
    def _read(self, addr, size=1):
        return self._bmp_i2c.readfrom_mem(self._i2c_addr, addr, size)
 
    def _write(self, addr, b_arr):
        if not type(b_arr) is bytearray:
            b_arr = bytearray([b_arr])
        return self._bmp_i2c.writeto_mem(self._i2c_addr, addr, b_arr)
 
    def _gauge(self):
        # TODO limit new reads
        now = utime.ticks_ms()
        if utime.ticks_diff(now, self._last_read_ts) > self._new_read_ms:
            self._last_read_ts = now
            r = self._t_os + (self._p_os << 3) + (1 << 6)
            self._write(BMP280_REGISTER_CONTROL, r)
            utime.sleep_ms(100)  # TODO calc sleep
            d = self._read(BMP280_REGISTER_DATA, 6)  # read all data at once (as by spec)
 
            self._p_raw = (d[0] << 12) + (d[1] << 4) + (d[2] >> 4)
            self._t_raw = (d[3] << 12) + (d[4] << 4) + (d[5] >> 4)
 
            self._t_fine = 0
            self._t = 0
            self._p = 0
 
    def load_test_calibration(self):
        self._T1 = 27504
        self._T2 = 26435
        self._T3 = -1000
        self._P1 = 36477
        self._P2 = -10685
        self._P3 = 3024
        self._P4 = 2855
        self._P5 = 140
        self._P6 = -7
        self._P7 = 15500
        self._P8 = -14600
        self._P9 = 6000
 
    def load_test_data(self):
        self._t_raw = 519888
        self._p_raw = 415148
 
    def print_calibration(self):
        print("T1: {} {}".format(self._T1, type(self._T1)))
        print("T2: {} {}".format(self._T2, type(self._T2)))
        print("T3: {} {}".format(self._T3, type(self._T3)))
        print("P1: {} {}".format(self._P1, type(self._P1)))
        print("P2: {} {}".format(self._P2, type(self._P2)))
        print("P3: {} {}".format(self._P3, type(self._P3)))
        print("P4: {} {}".format(self._P4, type(self._P4)))
        print("P5: {} {}".format(self._P5, type(self._P5)))
        print("P6: {} {}".format(self._P6, type(self._P6)))
        print("P7: {} {}".format(self._P7, type(self._P7)))
        print("P8: {} {}".format(self._P8, type(self._P8)))
        print("P9: {} {}".format(self._P9, type(self._P9)))
 
    def _calc_t_fine(self):
        # From datasheet page 22
        self._gauge()
        if self._t_fine == 0:
            var1 = (((self._t_raw >> 3) - (self._T1 << 1)) * self._T2) >> 11
            var2 = (((((self._t_raw >> 4) - self._T1) * ((self._t_raw >> 4) - self._T1)) >> 12) * self._T3) >> 14
            self._t_fine = var1 + var2
 
    @property
    def temperature(self):
        self._calc_t_fine()
        if self._t == 0:
            self._t = ((self._t_fine * 5 + 128) >> 8) / 100.
        return self._t
 
    @property
    def pressure(self):
        # From datasheet page 22
        self._calc_t_fine()
        if self._p == 0:
            var1 = self._t_fine - 128000
            var2 = var1 * var1 * self._P6
            var2 = var2 + ((var1 * self._P5) << 17)
            var2 = var2 + (self._P4 << 35)
            var1 = ((var1 * var1 * self._P3) >> 8) + ((var1 * self._P2) << 12)
            var1 = (((1 << 47) + var1) * self._P1) >> 33
 
            if var1 == 0:
                return 0
 
            p = 1048576 - self._p_raw
            p = int((((p << 31) - var2) * 3125) / var1)
            var1 = (self._P9 * (p >> 13) * (p >> 13)) >> 25
            var2 = (self._P8 * p) >> 19
 
            p = ((p + var1 + var2) >> 8) + (self._P7 << 4)
            self._p = p / 256.0
        return self._p

Now you upload this library and then you can create or modify the main.py with this example code

from machine import I2C
from bmp280 import BMP280
 
bus = machine.I2C(sda=machine.Pin(21),scl=machine.Pin(22))
bmp = BMP280(bus)
 
print(bmp.temperature)
print(bmp.pressure)

 

Testing

Open up the REPL window. Here is what I saw in uPyCraft

Ready to download this file,please wait!
..
download ok
exec(open(‘./main.py’).read(),globals())
22.72
98859.11
>>>

 

Links

https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001.pdf

LEAVE A REPLY

Please enter your comment!
Please enter your name here