Pico W and BME280

Hello,

I have a Galactic Unicorn and a BME280 plugged on it.

I’ve modified the “clock.py” example to display both clock and temp/pressure/humidity level.

My program is working well, but after a few minutes, it’s blocked.
The problem seem to come when I read BME280 value, but I don’t understand why.

Could you help me ?

import time
import math
import machine
import network
import ntptime
from galactic import GalacticUnicorn
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
import breakout_bme280 as bme280
from pimoroni_i2c import PimoroniI2C


PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN)
bme = bme280.BreakoutBME280(i2c)
bme.configure(bme280.FILTER_COEFF_OFF, bme280.STANDBY_TIME_1000_MS,
              bme280.OVERSAMPLING_16X, bme280.OVERSAMPLING_2X, bme280.OVERSAMPLING_1X,
              bme280.FORCED_MODE)

try:
    from secrets import WIFI_SSID, WIFI_PASSWORD
    wifi_available = True
except ImportError:
    print("Create secrets.py with your WiFi credentials to get time from NTP")
    wifi_available = False

# create galactic object and graphics surface for drawing
gu = GalacticUnicorn()
graphics = PicoGraphics(DISPLAY)

# create the rtc object
rtc = machine.RTC()

width = GalacticUnicorn.WIDTH
height = GalacticUnicorn.HEIGHT

# set up some pens to use later
WHITE = graphics.create_pen(255, 255, 255)
BLACK = graphics.create_pen(0, 0, 0)

# Start connection
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)


# Wait for connect success or failure
max_wait = 100
while max_wait > 0:
    if wlan.status() < 0 or wlan.status() >= 3:
        break
    max_wait -= 1
    print('waiting for connection...')
    time.sleep(0.2)
    

if max_wait > 0:
    print("Connected")

# function for drawing outlined text
def outline_text(text, x, y):
    graphics.set_pen(BLACK)
    graphics.text(text, x - 1, y - 1, -1, 1)
    graphics.text(text, x, y - 1, -1, 1)
    graphics.text(text, x + 1, y - 1, -1, 1)
    graphics.text(text, x - 1, y, -1, 1)
    graphics.text(text, x + 1, y, -1, 1)
    graphics.text(text, x - 1, y + 1, -1, 1)
    graphics.text(text, x, y + 1, -1, 1)
    graphics.text(text, x + 1, y + 1, -1, 1)

    graphics.set_pen(WHITE)
    graphics.text(text, x, y, -1, 1)


# Connect to wifi and synchronize the RTC time from NTP
def sync_time():
    try:
        ntptime.settime()
        print("Time set")
    except OSError:
        pass


# NTP synchronizes the time to UTC, this allows you to adjust the displayed time
# by one hour increments from UTC by pressing the volume up/down buttons
#
# We use the IRQ method to detect the button presses to avoid incrementing/decrementing
# multiple times when the button is held.
utc_offset = 0

up_button = machine.Pin(GalacticUnicorn.SWITCH_VOLUME_UP, machine.Pin.IN, machine.Pin.PULL_UP)
down_button = machine.Pin(GalacticUnicorn.SWITCH_VOLUME_DOWN, machine.Pin.IN, machine.Pin.PULL_UP)


def adjust_utc_offset(pin):
    global utc_offset
    if pin == up_button:
        utc_offset += 1
    if pin == down_button:
        utc_offset -= 1


up_button.irq(trigger=machine.Pin.IRQ_FALLING, handler=adjust_utc_offset)
down_button.irq(trigger=machine.Pin.IRQ_FALLING, handler=adjust_utc_offset)


year, month, day, wd, hour, minute, second, _ = rtc.datetime()

last_second = second
last_minute = minute
t,p,h=bme.read()
print(t,p,h)

# Check whether the RTC time has changed and if so redraw the display
def redraw_display_if_reqd():
    global year, month, day, wd, hour, minute, second, last_second, last_minute,t,p,h

    year, month, day, wd, hour, minute, second, _ = rtc.datetime()
    if second != last_second:
        #print(second)
        hour += utc_offset
        if hour>23:
            hour-=24

        if hour<0:
            hour+=24;

        graphics.set_pen(BLACK)
        graphics.clear()

        clock = "{:02}:{:02}".format(hour, minute)
        
        # set the font
        graphics.set_font("bitmap8")

        # calculate text position so that it is centred
        w = graphics.measure_text(clock, 1)
        x = int(width / 2 - w / 2 + 1)
        x = 2
        y = 2

        outline_text(clock, x, y)
        
        graphics.set_font("bitmap6")

        if minute != last_minute:
            print("refresh bme")
            t,p,h=bme.read()
            print("refreshed")
            time.sleep(0.5)
            last_minute=minute
          
            
        graphics.set_pen(WHITE)
        if second>=40:
            text=str(int(round(h,0)))+"%"
        elif second>=20:
            text=str(int(round(p/100,0)))+"m"
        else:
            text=str(int(round(t,0)))+"°C"
        
        w = graphics.measure_text(text, 1)
        x=width-w
        y=2        
        graphics.text(text, x, y, -1, 1)
        
        last_second = second
        # update the display
        gu.update(graphics)

gu.set_brightness(0.3)

sync_time()

while True:
    if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
        gu.adjust_brightness(+0.01)

    if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
        gu.adjust_brightness(-0.01)

    if gu.is_pressed(GalacticUnicorn.SWITCH_A):
        sync_time()

    redraw_display_if_reqd()

    time.sleep(0.1)

Try remarking out the
bme.configure line.
May I ask why your using it? I’m using BME68X and bme280 on Pico’s with just the standard

from breakout_bme280 import BreakoutBME280
from breakout_bme68x import BreakoutBME68X

I have added this line to see if it could fix my problem, but it doesn’t change anything.

Also, it help to have a first reading with good values.

OK, that explains that. I just put in a time delay before I display my first reading. I let it take readings for 1 second before displaying the reading.

Just a precision.
If I execute my program through Thonny, it can run for hours.
Il I execute it as “main.py” on the Galactic with only a USB charger, then it crash after a few minutes.

I have code running on a Tufty, and a Pico Lipo with a Display Pack. No issues running them for hours. I’ll have to check to see if its a BME280 or 680. I’m pretty sure one or both have a BME280. I’m just about to head out the door but will post my code when I get back in an hour or so.

1 Like

This my weather code I’m running on a Pimoroni Pico Lipo with a Pimoroni Display Pack V2 and Pimoroni BME280 breakout. I’m using the Pimoroni Pico Lipo uf2 file.

Warning, wall of code to follow. ;)

import utime
import picographics
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2, PEN_RGB332
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2, rotate=0, pen_type=PEN_RGB332)
display.set_backlight(1.0)
display.set_font("bitmap8") 

from breakout_bme280 import BreakoutBME280
from pimoroni_i2c import PimoroniI2C
i2c = PimoroniI2C(sda=(4), scl=(5))
bme = BreakoutBME280(i2c,0x77)

from machine import ADC, Pin
vsys = ADC(29)
charging = Pin(24, Pin.IN)
conversion_factor = 3 * 3.3 / 65535

full_battery = 4.2    
empty_battery = 2.8          

from pimoroni import Button
button_a = Button(12)
button_b = Button(13)
button_x = Button(14)
button_y = Button(15)

from pimoroni import RGBLED
led = RGBLED(6, 7, 8)
led.set_rgb(0, 0, 0) 

tempcolor = display.create_pen(0, 255, 0)  # this colour will get changed in a bit
humidcolor = display.create_pen(0, 255, 0)  # this colour will get changed in a bit
presscolor = display.create_pen(0, 255, 0)  # this colour will get changed in a bit
white = display.create_pen(255, 255, 255)
black = display.create_pen(0, 0, 0)
red = display.create_pen(255, 0, 0)
green = display.create_pen(0, 255, 0)
blue = display.create_pen(0, 0, 255)
yellow = display.create_pen(255, 255, 0)
orange = display.create_pen(255, 140, 0)
grey = display.create_pen(120, 120, 120)
violet = display.create_pen(255, 0, 255)

start_time = utime.time()

display.set_pen(black)
display.clear()

# converts the temperature into a description and pen colour
def describe_temperature(temperature):
    global tempcolor
    if temperature < 0:
        description = "Very Cold"
        tempcolor = blue
    elif 0 <= temperature < 12:
        description = "Cold"
        tempcolor = yellow
    elif 12 <= temperature < 17:
        description = "Cool"
        tempcolor = green
    elif 17 <= temperature < 22:
        description = "Warm"
        tempcolor = green      
    elif 22 <= temperature < 27:
        description = "Hot"
        tempcolor = orange
    elif temperature >= 27:
        description = "Very Hot"
        tempcolor = red
    else:
        description = ""
        tempcolor = black
    return description

# converts humidity into good/bad description and pen color
def describe_humidity(humidity):
    global humidcolor
    if humidity < 30:
        description = "Low - Dry"
        humidcolor = orange
    elif 30 <= humidity <= 60:
        description = "OK"
        humidcolor = green
    elif 60 < humidity < 80:
        description = "High"
        humidcolor = yellow
    elif humidity >= 80:
        description = "Very High"
        humidcolor = red        
    else:
        description = ""
        humidcolor = black        
    return description

# converts pressure into barometer-type description and pen color
def describe_pressure(pressure):
    global presscolor
    if pressure < 982:
        description = "Very Low"
        presscolor = red
    elif 982 <= pressure < 1004:
        description = "Low"
        presscolor = yellow
    elif 1004 <= pressure < 1026:
        description = ""
        presscolor = green
    elif 1026 <= pressure < 1048:
        description = "High"
        presscolor = blue
    elif pressure >= 1048:
        description = "Very High"
        presscolor = orange
    else:
        description = ""
        presscolor = black
    return description



def draw_graph(temp_value, press_value, humid_value):
    scaled_temp = int((temperature * 5) + 160)
    scaled_humid = int((humidity * (320 / 100)) - 10) 
    scaled_press = int(((pressuremb - 960) * 3) - 10)
    display.set_pen(black)
    display.clear()
    
    describe_temperature(temperature)
    display.set_pen(tempcolor)
    display.rectangle(0, 40, (scaled_temp), 19)
    display.circle((scaled_temp), 49, 9)
    display.text("{:0.0f}c".format(temperature), 0, 0, scale=4)
    display.text(describe_temperature(temperature), 150, 0, scale=4)
    display.set_pen(black)
    display.circle((scaled_temp), 49, 5) 
    
    describe_humidity(humidity)
    display.set_pen(humidcolor)
    display.rectangle(0, 110, (scaled_humid), 19)
    display.circle((scaled_humid), 119, 9)
    display.text("{:0.0f}%".format(humidity), 0, 70, scale=4)
    display.text(describe_humidity(humidity), 150, 70, scale=4)
    display.set_pen(black)
    display.circle((scaled_humid), 119, 5)
    
    describe_pressure(pressuremb)
    display.set_pen(presscolor)
    display.rectangle(0, 180, (scaled_press), 19)
    display.circle((scaled_press), 189, 9)
    display.text("{:0.0f}mb".format(pressuremb), 0, 140, scale=4)
    display.text(describe_pressure(pressuremb), 150, 140, scale=4)
    display.set_pen(black)
    display.circle((scaled_press), 189, 5)
 
  
start_time = utime.time()
    
while True:

    time_elapsed = utime.time() - start_time
    
    temperature, pressure, humidity = bme.read()
    pressuremb = pressure / 100      
    draw_graph(temperature, pressure, humidity)
    #print("{:0.2f}c, {:0.2f}mb, {:0.2f}%".format(
    #    temperature, pressuremb, humidity))
    
    voltage = vsys.read_u16() * conversion_factor
    percentage = 100 * ((voltage - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100
        
    if charging.value() == 1:
        display.set_pen(blue)
        display.text("USB Powered", 0, 210, scale=3)
        led.set_rgb(0, 0, 25)
    else:
        display.set_pen(green)
        display.text("Battery", 0, 210, scale=2)
        display.text("{:0.2f}v".format(voltage), 75, 210, scale=2)
        display.text("{:0.0f}%".format(percentage), 150, 210, scale=2)
        led.set_rgb(25, 0, 0) 

    if button_x.is_pressed:
        display.set_backlight(1.0)
    elif button_y.is_pressed:
        display.set_backlight(0.4) 
            
    if time_elapsed < 1:
        temperature, pressure, humidity = bme.read()
        display.set_pen(black)
        display.clear()
        display.update()
        utime.sleep(1)
    else:
        display.update()
        utime.sleep(1)
        display.set_pen(black)
        display.clear()    
        utime.sleep(1)

This is my Tufty code, it also has aBME280 on it.

'''
Pimoroni Tufty 2040
BME280
'''
import utime

import picographics
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332
display = PicoGraphics(display=DISPLAY_TUFTY_2040, rotate=180, pen_type=PEN_RGB332)
display.set_backlight(1.0)
display.set_font("bitmap8") 

from breakout_bme280 import BreakoutBME280
from pimoroni_i2c import PimoroniI2C
i2c = PimoroniI2C(sda=(4), scl=(5))
bme = BreakoutBME280(i2c)

from machine import ADC, Pin
vbat_adc = ADC(29)
vref_adc = ADC(28)
vref_en = Pin(27)
vref_en.init(Pin.OUT)
vref_en.value(0)
usb_power = Pin(24, Pin.IN)    

full_battery = 4.5
empty_battery = 2.8

from pimoroni import Button
button_a = Button(7, invert=False)
button_b = Button(8, invert=False)
button_c = Button(9, invert=False)
button_up = Button(22, invert=False)
button_down = Button(6, invert=False)

tempcolor = display.create_pen(0, 255, 0)  # this colour will get changed in a bit
humidcolor = display.create_pen(0, 255, 0)  # this colour will get changed in a bit
presscolor = display.create_pen(0, 255, 0)  # this colour will get changed in a bit
white = display.create_pen(255, 255, 255)
black = display.create_pen(0, 0, 0)
red = display.create_pen(255, 0, 0)
green = display.create_pen(0, 255, 0)
blue = display.create_pen(0, 0, 255)
yellow = display.create_pen(255, 255, 0)
orange = display.create_pen(255, 140, 0)
grey = display.create_pen(120, 120, 120)
violet = display.create_pen(255, 0, 255)

start_time = utime.time()

display.set_pen(black)
display.clear()

# converts the temperature into a description and pen colour
def describe_temperature(temperature):
    global tempcolor
    if temperature < 0:
        description = "Very Cold"
        tempcolor = blue
    elif 0 <= temperature < 12:
        description = "Cold"
        tempcolor = yellow
    elif 12 <= temperature < 17:
        description = "Cool"
        tempcolor = green
    elif 17 <= temperature < 22:
        description = "Warm"
        tempcolor = green      
    elif 22 <= temperature < 27:
        description = "Hot"
        tempcolor = orange
    elif temperature >= 27:
        description = "Very Hot"
        tempcolor = red
    else:
        description = ""
        tempcolor = black
    return description

# converts humidity into good/bad description and pen color
def describe_humidity(humidity):
    global humidcolor
    if humidity < 30:
        description = "Low - Dry"
        humidcolor = red
    elif 30 <= humidity <= 60:
        description = "OK"
        humidcolor = green
    elif 60 < humidity < 80:
        description = "High"
        humidcolor = yellow
    elif humidity >= 80:
        description = "Very High"
        humidcolor = orange        
    else:
        description = ""
        humidcolor = black        
    return description

# converts pressure into barometer-type description and pen color
def describe_pressure(pressure):
    global presscolor
    if pressure < 982:
        description = "Very Low"
        presscolor = red
    elif 982 <= pressure < 1004:
        description = "Low"
        presscolor = yellow
    elif 1004 <= pressure < 1026:
        description = ""
        presscolor = green
    elif 1026 <= pressure < 1048:
        description = "High"
        presscolor = blue
    elif pressure >= 1048:
        description = "Very High"
        presscolor = orange
    else:
        description = ""
        presscolor = black
    return description


def draw_graph(temp_value, press_value, humid_value):
    scaled_temp = int((temperature * 5) + 160)
    scaled_humid = int((humidity * (320 / 100)) - 10) 
    scaled_press = int(((pressuremb - 960) * 3) - 10)
    display.set_pen(black)
    display.clear()
    
    describe_temperature(temperature)
    display.set_pen(tempcolor)
    display.rectangle(0, 40, (scaled_temp), 19)
    display.circle((scaled_temp), 49, 9)
    display.text("{:0.0f}c".format(temperature), 0, 5, scale=4)
    display.text(describe_temperature(temperature), 150, 5, scale=4)
    display.set_pen(black)
    display.circle((scaled_temp), 49, 5) 
    
    describe_humidity(humidity)
    display.set_pen(humidcolor)
    display.rectangle(0, 110, (scaled_humid), 19)
    display.circle((scaled_humid), 119, 9)
    display.text("{:0.0f}%".format(humidity), 0, 75, scale=4)
    display.text(describe_humidity(humidity), 150, 75, scale=4)
    display.set_pen(black)
    display.circle((scaled_humid), 119, 5)
    
    describe_pressure(pressuremb)
    display.set_pen(presscolor)
    display.rectangle(0, 180, (scaled_press), 19)
    display.circle((scaled_press), 189, 9)
    display.text("{:0.0f}mb".format(pressuremb), 0, 145, scale=4)
    display.text(describe_pressure(pressuremb), 150, 145, scale=4)
    display.set_pen(black)
    display.circle((scaled_press), 189, 5)
        
start_time = utime.time()
    
while True:

    time_elapsed = utime.time() - start_time
    
    temperature, pressure, humidity = bme.read()
    pressuremb = pressure / 100      
    draw_graph(temperature, pressure, humidity)
    
    vref_en.value(1)

    vdd = 1.24 * (65535 / vref_adc.read_u16())
    vbat = (
        (vbat_adc.read_u16() / 65535) * 3 * vdd
    )  # 3 in this is a gain, not rounding of 3.3V
    
    vref_en.value(0)
   
    percentage = 100 * ((vbat - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100
    if percentage < 0:
        percentage = 0    

    if usb_power.value() == 1:
        display.set_pen(blue)
        display.text("USB Powered", 75, 210, scale=3)
    else:
        display.set_pen(green)
        display.text("Battery", 0, 210, scale=2)
        display.text("{:0.2f}v".format(vbat), 75, 210, scale=2)
        display.text("{:0.0f}%".format(percentage), 150, 210, scale=2)

    if button_up.is_pressed:
        display.set_backlight(1.0)
    elif button_down.is_pressed:
        display.set_backlight(0.5) 
            
    if time_elapsed < 1:
        temperature, pressure, humidity = bme.read()
        display.set_pen(black)
        display.clear()
        display.update()
        utime.sleep(1)
    else:
        display.update()
        utime.sleep(1)
        display.set_pen(black)
        display.clear()    
        utime.sleep(1)


Thanks for your time !
Hope than someone with a galactic and a BME could try my own code to see if he face the same problem.

Note that on my side a simple program “read and display value from the BME” work like a charm !

I have a Galactic Unicorn, Pimoroni gave me one of the pre production ones as a thank you for helping out on the forums.
I’ll have a look see if I have a spare BME280 and a QWICC cable kicking around. If I do I’ll give your code a try.

1 Like

Many thanks !
I will also give a look to your example

OK, I have your code up and running on my Galactic Unicorn.
One thing to keep in mind is (as far as I know) when connected via thonny it will sync time from the Host PC. The RP2040’s RTC does anyway. When on WIFI, time is synced via what ever you have set up, if you set something up. I’ve been using RV3028’s on the ones I need accurate time keeping.

On my Unicorn HD I do scrolling text.
Day, Date, Time, temperature, humidity, pressure.
It’s actually scrolling messages in series.
It’s only 16 x16 so that was a better option than a fixed message.

Yes the Pimonori example use ntp to get correct time for the Pico :)

Yeah, the issue with the RP2040 built in RTC is it doesn’t have any battery backup. Kill the power and it loses what time it is. That’s why I use RV3028’s, they have a battery backup and once set will remember the time even if you power down the PICO.

Ran your code as posted above. It ran all night on a Pi power supply without stopping.
I’m going to take the bme.configure part out and see what happens.

Thanks for you trial !
Maybe I should also flash my Galactic with a fresh micropython install ?

Thonny is showing the following
MicroPython 9dfabcd on 2022-11-18; Raspberry Pi Pico W with RP2040
Interpreter is set to MicroPython (RP2040)

I have reflashed mine at least once. I’m pretty sure I used the 1.19.10 GU uf2

I like your use of the buttons. Can’t figure out the graphics positioning part, that’s usually my strong point. It’s only 6AM here so not fully awake yet lol.

I remarked out the bme.configure part and so far so good. It’s been running fine for a couple of hours now.

1 Like

Ran fine all day with the bme.configure code remarked out. Went back to the 80’s super computer demo for now. Taking a break from it for now. Going to try a few things in a couple of days though.