Pi Pico based Weather Station Project

Wall of code to follow. I had to get creative to use the two display packs with just the one frame buffer. There isn’t enough memory space for two frame buffers.
What I do is write to the buffer display 1 only, update the display, then clear the buffer. Then write to it for display 2 only, update the display, then clear the buffer. wash, rinse and repeat.

import time
import st7789

from machine import ADC, Pin
from pimoroni_i2c import PimoroniI2C
from breakout_bme280 import BreakoutBME280
from breakout_bme68x import BreakoutBME68X
from breakout_ltr559 import BreakoutLTR559
from breakout_rtc import BreakoutRTC

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

full_battery = 4.2
empty_battery = 2.8

frame_buffer = bytearray(240 * 320 * 2)
display1 = st7789.ST7789(width=240, height=320, buffer=frame_buffer, slot=0)
display2 = st7789.ST7789(width=240, height=320, buffer=frame_buffer, slot=1)
display1.set_backlight(1.0)
display2.set_backlight(1.0)

i2c = PimoroniI2C(sda=(4), scl=(5))

bme_out = BreakoutBME68X(i2c)
bme_in = BreakoutBME280(i2c,0x77)

ltr = BreakoutLTR559(i2c)

rtc = BreakoutRTC(i2c)
rtc.set_backup_switchover_mode(1)
rtc.set_24_hour()
rtc.update_time()

min_temp_in = None
max_temp_in = None
min_temp_out = None
max_temp_out = None
min_press_out = None
max_press_out = None

start_time = time.time()

rtc.enable_periodic_update_interrupt(True)

while True:
    
    if rtc.read_periodic_update_interrupt_flag():
        rtc.clear_periodic_update_interrupt_flag()

        if rtc.update_time():
            rtc_date = rtc.string_date()
            rtc_time = rtc.string_time()
    
    time_elapsed = time.time() - start_time
        
    # read the sensors
    temp_in, press_in, hum_in = bme_in.read()
    temp_out, press_out, hum_out, gas_resistance, status, gas_index, meas_index = bme_out.read() 
            
    # convert pressure to mb
    pressuremb = press_out / 100
    
    # header
    display1.set_pen(255, 255, 255)
    display1.text("indoor", 15, 0, 240, 2)
    display1.text("outdoor", 140, 0, 240, 2)
    display1.text("temperature", 25, 20, 240, 3)
    display1.text("min", 95, 78, 240, 3)
    display1.text("max", 92, 100, 240, 3)
    display1.text("humidity", 50, 130, 240, 3)
    display1.text("pressure", 50, 200, 240, 3)
        
    # indoor temperature
    temp_in = round(temp_in)
    
    if temp_in < 0:
        display1.set_pen(255, 255, 255)
    elif 0 <= temp_in < 12:
        display1.set_pen(0, 0, 255)
    elif 12 <= temp_in < 17:
        display1.set_pen(255, 255, 0)
    elif 17 <= temp_in < 25:
        display1.set_pen(0, 255, 0)
    elif 25 <= temp_in < 30:
        display1.set_pen(255, 140, 0)
    elif temp_in >= 30:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    display1.text('{:.0f}'.format(temp_in) + '`c', 15, 45, 240, 4)

    # outdoor temperature
    temp_out = round(temp_out)
    if temp_out < 0:
        display1.set_pen(255, 255, 255)
    elif 0 <= temp_out < 12:
        display1.set_pen(0, 0, 255)
    elif 12 <= temp_out < 17:
        display1.set_pen(255, 255, 0)
    elif 17 <= temp_out < 25:
        display1.set_pen(0, 255, 0)
    elif 25 <= temp_out < 30:
        display1.set_pen(255, 140, 0)
    elif temp_out >= 30:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    display1.text('{:.0f}'.format(temp_out) + '`c', 160, 45, 240, 4)

    # indoor min max temperature readings
    if time_elapsed > 5:
        if min_temp_in is not None and max_temp_in is not None:
            if temp_in < min_temp_in:
                min_temp_in = int(temp_in)
            elif temp_in > max_temp_in:
                max_temp_in = int(temp_in)
        else:
            min_temp_in = int(temp_in)
            max_temp_in = int(temp_in)
            
    if min_temp_in is not None and max_temp_in is not None:
        min_string_in = ('{:.0f}'.format(min_temp_in))
        max_string_in = ('{:.0f}'.format(max_temp_in))
    else:
        min_string_in = ""
        max_string_in = ""
        
    if min_temp_in is not None and max_temp_in is not None:
        if min_temp_in < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= min_temp_in < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= min_temp_in < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= min_temp_in < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= min_temp_in < 30: # hot
            display1.set_pen(255, 140, 0)
        elif min_temp_in >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display.set_pen(0, 0, 0)
            
        display1.text(min_string_in, 25, 78, 240, 3)
        
        if max_temp_in < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= max_temp_in < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= max_temp_in < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= max_temp_in < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= max_temp_in < 30: # hot
            display1.set_pen(255, 140, 0)
        elif max_temp_in >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display1.set_pen(0, 0, 0)
            
        display1.text(max_string_in, 25, 100, 240, 3)  

    # outdoor min max temperature readings
    if time_elapsed > 5:
        if min_temp_out is not None and max_temp_out is not None:
            if temp_out < min_temp_out:
                min_temp_out = int(temp_out)
            elif temp_out > max_temp_out:
                max_temp_out = int(temp_out)
        else:
            min_temp_out = int(temp_out)
            max_temp_out = int(temp_out)
            
    if min_temp_out is not None and max_temp_out is not None:
        min_string_out = ('{:.0f}'.format(min_temp_out))
        max_string_out = ('{:.0f}'.format(max_temp_out))
    else:
        min_string_out = ""
        max_string_out = ""
        
    if min_temp_out is not None and max_temp_out is not None:
        if min_temp_out < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= min_temp_out < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= min_temp_out < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= min_temp_out < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= min_temp_out < 30: # hot
            display1.set_pen(255, 140, 0)
        elif min_temp_out >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display1.set_pen(0, 0, 0)
            
        display1.text(min_string_out, 180, 78, 240, 3)
        
        #max_temp_out = round(max_temp_out)
        
        if max_temp_out < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= max_temp_out < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= max_temp_out < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= max_temp_out < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= max_temp_out < 30: # hot
            display1.set_pen(255, 140, 0)
        elif max_temp_out >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display1.set_pen(0, 0, 0)
            
        display1.text(max_string_out, 180, 100, 240, 3)

    # indoor humidity
    
    if hum_in < 30:
        display1.set_pen(255, 140, 0)
    elif 30 <= hum_in < 61:
        display1.set_pen(0, 255, 0)
    elif 61 <= hum_in < 81:
        display1.set_pen(255, 255, 0)
    elif hum_in >= 81:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    #display1.text((int(hum_in)), 20, 80, 240, 3)
    display1.text('{:.0f}'.format(hum_in) + '%', 10, 160, 240, 4)
  
    # outdoor humidity
    
    if hum_out < 30:
        display1.set_pen(255, 140, 0)
    elif 30 <= hum_out < 61:
        display1.set_pen(0, 255, 0)
    elif 61 <= hum_out < 81:
        display1.set_pen(255, 255, 0)
    elif hum_out >= 81:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    #display1.text((int(hum_in)), 20, 80, 240, 3)
    display1.text('{:.0f}'.format(hum_out) + '%', 160, 160, 240, 4)
    
    # outddor pressure reading on display 2    
    if pressuremb < 982:
        display1.set_pen(255, 0, 0)
        display1.text('{:.0f}'.format(pressuremb) + 'mb', 50, 230, 240, 4)
        display1.text("very low", 30, 265, 240, 4)
    elif 982 <= pressuremb < 1004:
        display1.set_pen(255, 255, 0)
        display1.text('{:.0f}'.format(pressuremb) + 'mb', 50, 230, 240, 4)
        display1.text("low", 80, 265, 240, 4)
    elif 1004 <= pressuremb < 1026:
        display1.set_pen(0, 255, 0)
        display1.text('{:.0f}'.format(pressuremb) + 'mb', 55, 230, 240, 4)
        display1.text("unsetled", 30, 265, 240, 4)
    elif 1026 <= pressuremb < 1048:
        display1.set_pen(0, 0, 255)
        display1.text('{:.0f}'.format(pressuremb) + 'mb', 50, 230, 240, 4)
        display1.text("high", 35, 265, 240, 4)
    elif pressuremb >= 1048:
        display1.set_pen(255, 140, 0)
        display1.text('{:.0f}'.format(pressuremb) + 'mb', 50, 230, 240, 4)
        display1.text("very high", 20, 265, 240, 4)
    else:
        display1.set_pen(0, 0, 0)
        display1.text('{:.0f}'.format(pressuremb) + 'mb', 50, 230, 240, 4)
        display1.text("", 25, 265, 240, 4)    

    # battery state
    voltage = vsys.read_u16() * conversion_factor
    percentage = 100 * ((voltage - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100.00
    
    if charging.value() == 1:         # if it's plugged into USB power...
        display1.set_pen(0, 255, 0)
        display1.text("Battery", 5, 300, 240, 3)
        #display1.text("ON", 180, 280, 240, 3)
        display1.text('{:.0f}%'.format(percentage), 155, 300, 240, 3)
        #display1.text('{:.1f}v'.format(voltage), 155, 300, 240, 3)
        #display1.text("MAINS", 155, 300, 240, 3)
        display1.set_backlight(1.0)
        display2.set_backlight(1.0)
    else:                             # if not, display the battery stats
        display1.set_pen(255, 255, 0)
        display1.text("Battery", 5, 300, 240, 3)
        #display1.text("ON", 180, 280, 240, 3)        
        display1.text('{:.0f}%'.format(percentage), 155, 300, 240, 3)
        #display1.text("Batt", 155, 300, 240, 3)
        display1.set_backlight(0.5)
        display2.set_backlight(0.5)
        #display2.set_led(125,0,0)
        
    # time to update display 1
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.clear()
    
    # header
    display2.set_pen(255, 255, 255)
    display2.text("rain fall", 50, 130, 240, 3)
    display2.text("wind", 80, 200, 240, 3)
    display2.text("@", 100, 230, 240, 4)

                                         
    display2.set_pen(255, 255, 255)
    
    hours = rtc.get_hours()
    minutes = rtc.get_minutes()
    display2.text(rtc.string_date(), 70, 30, 240, 2)
    #display2.text(f"{hours:02}:{minutes:02}", 190, 0, 320, 3)
    
    if hours <12:
        display2.text(f"{hours:2}:{minutes:02}:am", 50, 0, 240, 4)
    elif hours == 12:
        display2.text(f"{hours:2}:{minutes:02}:pm", 60, 0, 240, 4)
    elif hours >12:
        hours = hours - 12
        display2.text(f"{hours:2}:{minutes:02}:pm", 60, 0, 240, 4)
          
    reading = ltr.get_reading()
    light = reading[BreakoutLTR559.LUX]
    
    display2.set_pen(0, 255, 0)
    display2.text("sun", 10, 50, 240, 4)
    
    #Convert light level in lux to descriptive value.
    
    if light < 50:
        display2.text("dark", 120, 50, 240, 4)
    elif 50 <= light < 100:
        display2.text("dim", 110, 50, 240, 4)
    elif 100 <= light < 500:
        display2.text("light", 110, 50, 240, 4)
    elif light >= 500:
        display2.text("bright", 110, 50, 240, 4)
        
    # UV Index
    display2.set_pen(0, 255, 0)
    display2.text("UV:", 15, 80, 240, 4)
    display2.text("??", 140, 80, 240, 4)
            
        
    # percipitaion
     
    display2.set_pen(0, 255, 0)
    display2.text("??" + 'mm', 10, 160, 240, 3)
    display2.text("??" + 'mm/h', 120, 160, 240, 3)
    
    # wind

    display2.set_pen(0, 255, 0)
    display2.text("??", 50, 230, 240, 4)
    display2.text("??", 150, 230, 240, 4)
    display2.text("??", 100, 265, 240, 4)




#    display2.set_pen(255, 255, 255)
#    display2.text("wind:", 0, 190, 240, 3)
#    display2.text("NE", 80, 190, 240, 3)
    
    # wind direction
#    display2.set_pen(255, 255, 255)
#    display2.text("36 km/h", 130, 190, 240, 3)
        

    # battery state
    voltage = vsys.read_u16() * conversion_factor
    percentage = 100 * ((voltage - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100.00
    
    if charging.value() == 1:         # if it's plugged into USB power...
        display2.set_pen(0, 255, 0)
        display2.text("Battery", 5, 300, 240, 3)
        #display2.text("ON", 180, 280, 240, 3)
        #display2.text('{:.1f}v'.format(voltage), 155, 300, 240, 3)
        display2.text('{:.0f}%'.format(percentage), 155, 300, 240, 3)
        #display2.text("MAINS", 155, 300, 240, 3)
        
    else:                             # if not, display the battery stats
        display2.set_pen(255, 255, 0)
        display2.text("Battery", 5, 300, 240, 3)
        #display2.text("ON", 180, 280, 240, 3)        
        display2.text('{:.0f}%'.format(percentage), 155, 300, 240, 3)
        #display2.text("Batt", 155, 300, 240, 3)
                    
    # update display 2
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.clear()
    
    time.sleep(1.0)