The BMP280 measures air pressure precisely enough to estimate altitude — it is the BME280 without humidity, and cheaper. On a Pico W you can read it with a short driver that lives in main.py itself.
Wire it to 3V3 and I2C0 (GP4/GP5) with SDO to GND for address 0x76. The script prints temperature, pressure in hPa and an altitude estimate every two seconds.
Wiring
| Raspberry Pi Pico W | BMP280 breakout (GY-BMP280) | Wire | Notes |
|---|---|---|---|
| 3V3 | VCC | red | 3V3 is pin 36. The purple 6-pin board has no regulator — 3.3 V only. |
| GND | GND | black | |
| GP5 | SCL | yellow | GP5 is physical pin 7 (I2C0 SCL). |
| GP4 | SDA | blue | GP4 is physical pin 6 (I2C0 SDA). |
| GND | SDO | black | SDO to GND selects address 0x76. Leave CSB unconnected (pulled up = I2C mode). |
3.3 V (boards with a regulator: 3.3–5 V) part, 3.3 V board. The common purple 6-pin GY-BMP280-3.3 board has no regulator and no level shifting: power it from 3.3 V only. On a 5 V Uno, use a board with a regulator or a level shifter on SDA/SCL.
Board quirks
- The driver is about 35 lines inside
main.py— Bosch’s floating-point compensation formulas from the datasheet, so there is nothing extra to copy to the board. - Chip ID
0x58is a BMP280;0x60is a BME280 (it also has humidity — use the BME280 guide for that). - Altitude is estimated from 1013.25 hPa at sea level. Weather moves that by ±10 hPa (±80 m), so for real altitude enter your city’s current QNH from a METAR report.
- The IIR filter and ×16 pressure oversampling make readings steady enough to see a floor change in a building.
Code
# BMP280 temperature + pressure on a Raspberry Pi Pico W (MicroPython).
# Wiring (purple 6-pin GY-BMP280-3.3): VCC -> 3V3 (pin 36), GND -> GND, SCL -> GP5 (pin 7),
# SDA -> GP4 (pin 6), SDO -> GND (address 0x76), CSB unconnected (I2C mode).
# Minimal driver written for FlashReady (MIT). Compensation = Bosch BMP280 datasheet float formulas.
from machine import Pin, I2C
import struct
import time
SEA_LEVEL_HPA = 1013.25 # for the altitude estimate; use your city's current QNH for accuracy
class BMP280:
def __init__(self, i2c, addr):
self.i2c, self.addr = i2c, addr
chip = i2c.readfrom_mem(addr, 0xD0, 1)[0]
if chip not in (0x56, 0x57, 0x58, 0x60):
raise OSError("chip ID 0x%02X - not a BMP280" % chip)
self.chip = chip
(self.T1, self.T2, self.T3, self.P1, self.P2, self.P3, self.P4,
self.P5, self.P6, self.P7, self.P8, self.P9) = struct.unpack("<HhhHhhhhhhhh", i2c.readfrom_mem(addr, 0x88, 24))
i2c.writeto_mem(addr, 0xF5, b"\x10") # IIR filter x16 smooths pressure
def read(self):
"""One forced measurement -> (temperature C, pressure hPa)."""
self.i2c.writeto_mem(self.addr, 0xF4, bytes([(2 << 5) | (5 << 2) | 1])) # T x2, P x16, forced
time.sleep_ms(45)
while self.i2c.readfrom_mem(self.addr, 0xF3, 1)[0] & 0x08:
time.sleep_ms(2)
d = self.i2c.readfrom_mem(self.addr, 0xF7, 6)
adc_p = (d[0] << 12) | (d[1] << 4) | (d[2] >> 4)
adc_t = (d[3] << 12) | (d[4] << 4) | (d[5] >> 4)
v1 = (adc_t / 16384.0 - self.T1 / 1024.0) * self.T2
v2 = (adc_t / 131072.0 - self.T1 / 8192.0) ** 2 * self.T3
t_fine = v1 + v2
v1 = t_fine / 2.0 - 64000.0
v2 = v1 * v1 * self.P6 / 32768.0 + v1 * self.P5 * 2.0
v2 = v2 / 4.0 + self.P4 * 65536.0
v1 = (self.P3 * v1 * v1 / 524288.0 + self.P2 * v1) / 524288.0
v1 = (1.0 + v1 / 32768.0) * self.P1
if v1 == 0:
return t_fine / 5120.0, 0.0
p = (1048576.0 - adc_p - v2 / 4096.0) * 6250.0 / v1
p += (self.P9 * p * p / 2147483648.0 + p * self.P8 / 32768.0 + self.P7) / 16.0
return t_fine / 5120.0, p / 100.0
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100_000)
found = i2c.scan()
addr = 0x76 if 0x76 in found else (0x77 if 0x77 in found else None)
if addr is None:
raise SystemExit("No BMP280 at 0x76/0x77 - found {}".format([hex(a) for a in found]))
bmp = BMP280(i2c, addr)
print("BMP280 at {} (chip ID 0x{:02X}{})".format(hex(addr), bmp.chip, " = BME280, humidity ignored" if bmp.chip == 0x60 else ""))
while True:
t, p = bmp.read()
alt = 44330.0 * (1.0 - (p / SEA_LEVEL_HPA) ** 0.1903)
print("Temp {:.2f} C Pressure {:.2f} hPa Altitude ~{:.0f} m".format(t, p, alt))
time.sleep(2)# BMP280 temperature + pressure on a Raspberry Pi Pico W (MicroPython).
# Wiring (purple 6-pin GY-BMP280-3.3): VCC -> 3V3 (pin 36), GND -> GND, SCL -> GP5 (pin 7),
# SDA -> GP4 (pin 6), SDO -> GND (address 0x76), CSB unconnected (I2C mode).
# Minimal driver written for FlashReady (MIT). Compensation = Bosch BMP280 datasheet float formulas.
from machine import Pin, I2C
import struct
import time
SEA_LEVEL_HPA = 1013.25 # for the altitude estimate; use your city's current QNH for accuracy
class BMP280:
def __init__(self, i2c, addr):
self.i2c, self.addr = i2c, addr
chip = i2c.readfrom_mem(addr, 0xD0, 1)[0]
if chip not in (0x56, 0x57, 0x58, 0x60):
raise OSError("chip ID 0x%02X - not a BMP280" % chip)
self.chip = chip
(self.T1, self.T2, self.T3, self.P1, self.P2, self.P3, self.P4,
self.P5, self.P6, self.P7, self.P8, self.P9) = struct.unpack("<HhhHhhhhhhhh", i2c.readfrom_mem(addr, 0x88, 24))
i2c.writeto_mem(addr, 0xF5, b"\x10") # IIR filter x16 smooths pressure
def read(self):
"""One forced measurement -> (temperature C, pressure hPa)."""
self.i2c.writeto_mem(self.addr, 0xF4, bytes([(2 << 5) | (5 << 2) | 1])) # T x2, P x16, forced
time.sleep_ms(45)
while self.i2c.readfrom_mem(self.addr, 0xF3, 1)[0] & 0x08:
time.sleep_ms(2)
d = self.i2c.readfrom_mem(self.addr, 0xF7, 6)
adc_p = (d[0] << 12) | (d[1] << 4) | (d[2] >> 4)
adc_t = (d[3] << 12) | (d[4] << 4) | (d[5] >> 4)
v1 = (adc_t / 16384.0 - self.T1 / 1024.0) * self.T2
v2 = (adc_t / 131072.0 - self.T1 / 8192.0) ** 2 * self.T3
t_fine = v1 + v2
v1 = t_fine / 2.0 - 64000.0
v2 = v1 * v1 * self.P6 / 32768.0 + v1 * self.P5 * 2.0
v2 = v2 / 4.0 + self.P4 * 65536.0
v1 = (self.P3 * v1 * v1 / 524288.0 + self.P2 * v1) / 524288.0
v1 = (1.0 + v1 / 32768.0) * self.P1
if v1 == 0:
return t_fine / 5120.0, 0.0
p = (1048576.0 - adc_p - v2 / 4096.0) * 6250.0 / v1
p += (self.P9 * p * p / 2147483648.0 + p * self.P8 / 32768.0 + self.P7) / 16.0
return t_fine / 5120.0, p / 100.0
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100_000)
found = i2c.scan()
addr = 0x76 if 0x76 in found else (0x77 if 0x77 in found else None)
if addr is None:
raise SystemExit("No BMP280 at 0x76/0x77 - found {}".format([hex(a) for a in found]))
bmp = BMP280(i2c, addr)
print("BMP280 at {} (chip ID 0x{:02X}{})".format(hex(addr), bmp.chip, " = BME280, humidity ignored" if bmp.chip == 0x60 else ""))
while True:
t, p = bmp.read()
alt = 44330.0 * (1.0 - (p / SEA_LEVEL_HPA) ** 0.1903)
print("Temp {:.2f} C Pressure {:.2f} hPa Altitude ~{:.0f} m".format(t, p, alt))
time.sleep(2)Expected serial output
BMP280 at 0x76 (chip ID 0x58)
Temp 28.61 C Pressure 1006.42 hPa Altitude ~58 m
Temp 28.62 C Pressure 1006.40 hPa Altitude ~58 mBMP280 at 0x76 (chip ID 0x58) Temp 28.61 C Pressure 1006.42 hPa Altitude ~58 m Temp 28.62 C Pressure 1006.40 hPa Altitude ~58 m
Common errors
OSError: chip ID 0x.. - not a BMP280
Something else answered at 0x76/0x77 — another sensor on the same bus, or a mislabelled module. Unplug the others and check the chip marking.
No BMP280 at 0x76/0x77
CSB is tied LOW (SPI mode), SDA/SCL swapped, or no 3.3 V. GP4 is physical pin 6, GP5 is pin 7.
Pressure reads about 600 hPa or wildly wrong
A counterfeit or damaged sensor. Compare with a phone barometer; replace the module.