breadboard
Example project · Sensors

Pico 2 I2C weather station

A real RP2350 boots the pinned official MicroPython v1.29.0 image (the default Arm build, on its Cortex-M33 cores), scans a BME280 and OLED on I2C0 (GP4/GP5), then continuously reads, displays, and prints the live weather values. Both devices use direct machine.I2C operations; change the sensor or disconnect SDA to see the real bus respond.

The Pico 2 I2C weather station circuit as rendered by the simulator

What's on the bench

  • 2× Resistor
  • Battery
  • BME280
  • OLED 128×64
  • Pi Pico 2

How it's wired

  • Battery · pos→Pi Pico 2 · vsys
  • Battery · neg→Pi Pico 2 · gnd
  • Pi Pico 2 · 3v3→BME280 · vcc
  • Pi Pico 2 · gnd→BME280 · gnd
  • Pi Pico 2 · gp4→BME280 · sda
  • Pi Pico 2 · gp5→BME280 · scl
  • Pi Pico 2 · 3v3→Resistor · a
  • Resistor · b→BME280 · sda
  • Pi Pico 2 · 3v3→Resistor · a
  • Resistor · b→BME280 · scl
  • Pi Pico 2 · 3v3→OLED 128×64 · vcc
  • Pi Pico 2 · gnd→OLED 128×64 · gnd
  • BME280 · sda→OLED 128×64 · sda
  • BME280 · scl→OLED 128×64 · scl

The code

This MicroPython script runs on the emulated board every boot; edit it in the Code tab.

from machine import I2C, Pin
from time import sleep_ms
import framebuf
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
oled_buf = bytearray(1024)
oled = framebuf.FrameBuffer(oled_buf, 128, 64, framebuf.MONO_VLSB)
def oled_init():
    for c in b'\xae\xd5\x80\xa8\x3f\xd3\x00\x40\x8d\x14\x20\x00\xa1\xc8\xda\x12\x81\xcf\xd9\xf1\xdb\x40\xa4\xa6\xaf':
        i2c.writeto(0x3c, bytes([0, c]))
def oled_show():
    i2c.writeto(0x3c, b'\x00\x21\x00\x7f\x22\x00\x07')
    i2c.writeto(0x3c, b'\x40' + oled_buf)
def u16(b, n): return b[n] | (b[n + 1] << 8)
def s16(v): return v - 65536 if v & 0x8000 else v
def s12(v): return v - 4096 if v & 0x800 else v
def s8(v): return v - 256 if v & 0x80 else v
def fixed(v): return ('-' if v < 0 else '') + '%d.%02d' % (abs(v) // 100, abs(v) % 100)
configured = False
last_display = None
while True:
    try:
        if not configured:
            scan = i2c.scan()
            chip = i2c.readfrom_mem(0x76, 0xD0, 1)[0]
            i2c.writeto_mem(0x76, 0xF2, b'\x01')
            i2c.writeto_mem(0x76, 0xF4, b'\x27')
            ctrl = i2c.readfrom_mem(0x76, 0xF4, 1)[0]
            cal = i2c.readfrom_mem(0x76, 0x88, 26)
            hcal = i2c.readfrom_mem(0x76, 0xE1, 7)
            t1, t2, t3 = u16(cal, 0), s16(u16(cal, 2)), s16(u16(cal, 4))
            p1 = u16(cal, 6)
            p2, p3, p4 = s16(u16(cal, 8)), s16(u16(cal, 10)), s16(u16(cal, 12))
            p5, p6, p7 = s16(u16(cal, 14)), s16(u16(cal, 16)), s16(u16(cal, 18))
            p8, p9 = s16(u16(cal, 20)), s16(u16(cal, 22))
            h1, h2, h3 = cal[25], s16(u16(hcal, 0)), hcal[2]
            h4 = s12((hcal[3] << 4) | (hcal[4] & 15))
            h5, h6 = s12((hcal[5] << 4) | (hcal[4] >> 4)), s8(hcal[6])
            configured = 0x3c in scan and 0x76 in scan and chip == 0x60 and ctrl == 0x27
            if configured:
                oled_init()
        if configured:
            raw = i2c.readfrom_mem(0x76, 0xF7, 8)
            adc_p = (raw[0] << 12) | (raw[1] << 4) | (raw[2] >> 4)
            adc_t = (raw[3] << 12) | (raw[4] << 4) | (raw[5] >> 4)
            adc_h = (raw[6] << 8) | raw[7]
            v1 = (((adc_t >> 3) - (t1 << 1)) * t2) >> 11
            v2 = (((((adc_t >> 4) - t1) * ((adc_t >> 4) - t1)) >> 12) * t3) >> 14
            fine = v1 + v2
            temp = (fine * 5 + 128) >> 8
            pv1 = fine - 128000
            pv2 = pv1 * pv1 * p6 + (pv1 * p5 << 17) + (p4 << 35)
            pv1 = (((1 << 47) + (((pv1 * pv1 * p3) >> 8) + (pv1 * p2 << 12))) * p1) >> 33
            pressure = 0
            if pv1:
                pressure = (((1048576 - adc_p) << 31) - pv2) * 3125 // pv1
                pv1 = (p9 * (pressure >> 13) * (pressure >> 13)) >> 25
                pv2 = (p8 * pressure) >> 19
                pressure = ((pressure + pv1 + pv2) >> 8) + (p7 << 4)
            hx = fine - 76800
            ha = ((adc_h << 14) - (h4 << 20) - h5 * hx + 16384) >> 15
            hb1 = (((hx * h6) >> 10) * (((hx * h3) >> 11) + 32768)) >> 10
            hb = ((hb1 + 2097152) * h2 + 8192) >> 14
            humidity = ha * hb
            humidity -= (((humidity >> 15) * (humidity >> 15) >> 7) * h1) >> 4
            humidity = max(0, min(419430400, humidity)) >> 12
            humidity = (humidity * 100 + 512) >> 10
            pressure = (pressure + 128) >> 8
            temp_s, humidity_s, pressure_s = fixed(temp), fixed(humidity), fixed(pressure)
            display_values = (temp_s, humidity_s, pressure_s)
            if display_values != last_display:
                oled.fill(0)
                oled.text('PICO 2 WEATHER', 0, 0)
                oled.text('T  ' + temp_s + ' C', 0, 16)
                oled.text('RH ' + humidity_s + ' %', 0, 32)
                oled.text('P  ' + pressure_s + ' hPa', 0, 48)
                oled_show()
                last_display = display_values
            print('P2-I2C-OK scan=%s chip=0x%02x ctrl=0x%02x temp=%sC humidity=%s%% pressure=%shPa' % (scan, chip, ctrl, temp_s, humidity_s, pressure_s))
    except OSError:
        configured = False
        last_display = None
        print('P2-I2C-FAIL OSError')
    sleep_ms(1000)

Related projects