# AHT20 (0x38) + BMP280 (0x77) on Raspberry Pi Pico, I2C0 (GP8 SDA, GP9 SCL)
from machine import Pin, I2C
from time import sleep
# ---------- I2C ----------
i2c = I2C(0, sda=Pin(8), scl=Pin(9), freq=100000)
AHT_ADDR = 0x38
BMP_ADDR = 0x77 # your chip id 0x58 confirmed BMP280 at 0x77
# --------- AHT20 (T + RH) ----------
def aht20_init():
try:
i2c.writeto(AHT_ADDR, b'\xBA') # soft reset
sleep(0.02)
except OSError:
pass
i2c.writeto(AHT_ADDR, b'\xBE\x08\x00') # init/calibrate
sleep(0.02)
def aht20_read():
i2c.writeto(AHT_ADDR, b'\xAC\x33\x00') # trigger
# wait until not busy
for _ in range(60):
status = i2c.readfrom(AHT_ADDR, 1)[0]
if (status & 0x80) == 0:
break
sleep(0.005)
raw = i2c.readfrom(AHT_ADDR, 6)
hum = ((raw[1]<<12) | (raw[2]<<4) | (raw[3]>>4)) / 1048576.0 * 100.0
tmp = (((raw[3]&0x0F)<<16) | (raw[4]<<8) | raw[5]) / 1048576.0 * 200.0 - 50.0
return tmp, hum
# --------- BMP280 (Pressure + Temp) ----------
# Minimal driver (forced mode, x1 oversampling)
def _u16(b): return b[0] | (b[1] << 8)
def _s16(b):
v = _u16(b)
return v - 65536 if v & 0x8000 else v
# Read calibration constants
cal = i2c.readfrom_mem(BMP_ADDR, 0x88, 24)
dig_T1 = _u16(cal[0:2])
dig_T2 = _s16(cal[2:4])
dig_T3 = _s16(cal[4:6])
dig_P1 = _u16(cal[6:8])
dig_P2 = _s16(cal[8:10])
dig_P3 = _s16(cal[10:12])
dig_P4 = _s16(cal[12:14])
dig_P5 = _s16(cal[14:16])
dig_P6 = _s16(cal[16:18])
dig_P7 = _s16(cal[18:20])
dig_P8 = _s16(cal[20:22])
dig_P9 = _s16(cal[22:24])
# Configure: ctrl_meas (temp x1, press x1, sleep), config (standby/filter default)
# We'll use "forced" mode per read.
i2c.writeto_mem(BMP_ADDR, 0xF4, b'\x27') # osrs_t=1, osrs_p=1, mode=3 (normal)
i2c.writeto_mem(BMP_ADDR, 0xF5, b'\x00') # config default
t_fine = 0
def bmp280_read():
global t_fine
# Force a measurement (write mode to forced would be 0x25; normal 0x27 is okay too)
# Read raw data: 0xF7..0xFC: press_msb, press_lsb, press_xlsb, temp_msb, temp_lsb, temp_xlsb
data = i2c.readfrom_mem(BMP_ADDR, 0xF7, 6)
adc_p = ((data[0] << 16) | (data[1] << 8) | data[2]) >> 4
adc_t = ((data[3] << 16) | (data[4] << 8) | data[5]) >> 4
# Temperature compensation (datasheet)
var1 = ((adc_t / 16384.0) - (dig_T1 / 1024.0)) * dig_T2
var2 = (((adc_t / 131072.0) - (dig_T1 / 8192.0)) ** 2) * dig_T3
t_fine = int(var1 + var2)
T = (var1 + var2) / 5120.0
# Pressure compensation (datasheet)
var1p = t_fine / 2.0 - 64000.0
var2p = var1p * var1p * dig_P6 / 32768.0
var2p = var2p + var1p * dig_P5 * 2.0
var2p = var2p / 4.0 + dig_P4 * 65536.0
var1p = (dig_P3 * var1p * var1p / 524288.0 + dig_P2 * var1p) / 524288.0
var1p = (1.0 + var1p / 32768.0) * dig_P1
if var1p == 0:
return T, 0.0 # avoid division by zero
P = 1048576.0 - adc_p
P = (P - var2p / 4096.0) * 6250.0 / var1p
var1p = dig_P9 * P * P / 2147483648.0
var2p = P * dig_P8 / 32768.0
P = P + (var1p + var2p + dig_P7) / 16.0
P_hPa = P / 100.0
return T, P_hPa
# ----- Altitude from pressure -----
P0_SEA_LEVEL = 1013.25 # set this to your current sea-level pressure for best accuracy
def altitude_from_pressure(p_hPa, p0=P0_SEA_LEVEL):
return 44330.0 * (1.0 - (p_hPa / p0) ** (1/5.255))
# ---------- Demo ----------
aht20_init()
while True:
try:
Taht, RH = aht20_read()
Tbmp, P = bmp280_read()
alt = altitude_from_pressure(P, P0_SEA_LEVEL)
print("AHT20 -> T = %.2f °C RH = %.1f %%" % (Taht, RH))
print("BMP280 -> T = %.2f °C P = %.2f hPa Alt ≈ %.1f m (P0=%.2f hPa)" %
(Tbmp, P, alt, P0_SEA_LEVEL))
print()
except OSError as e:
print("Read error:", e)
sleep(1)
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter