BMP280 / BME280 uPython

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: BMP280 / BME280 uPython

Post by Rissy » Sat Aug 27, 2022 5:56 pm

danjperron wrote:
Fri Aug 26, 2022 12:32 pm
I didn't have any problem with the BME280 sensor but I installed that version


https://github.com/SebastianRoll/mpy_bme280_esp8266


I just copy the bme280.py into the pico lib folder.


This is an example. I didn't test this exact code but it should work since I extracted it from my working solar garden station.
https://github.com/danjperron/PicoWSolar

Be aware that some BME280 aren't at 0x77 address. Check yours using i2c.scan()

Code: Select all

import machine
import bme280    
bme_PinSDA=4
bme_PinSCL=5
bme_i2c_address = 0x77

i2c=machine.I2C(0,sda=machine.Pin(bme_PinSDA), scl=machine.Pin(bme_PinSCL), freq=100000)    #initializing the I2C method 
bme = bme280.BME280(i2c=i2c,address=bme_i2c_address)


def readBme():
    try:
        t, p, h = bme.read_compensated_data()
        t, p, h = bme.read_compensated_data()
        return (t/100.0 , p/25600.0, h/1024.0)
    except OSError as e:
        pass
    return None    

t, p , h = readBme()
print("Temperature :", t)
print("Humidity:", h)
print("Pressure:", p)
Hello danjperron,

I was looking at your code, and i went further and had a look at your code at your PicoWSolar github page. As long as it was ok for me to do so, I took a copy of your overall code, which included the wifi connection programming etc too. This looks most different to mine which I have running with an SHT30 sensor. So i was interested to try yours for my switch over from RP4B to PicoW for my TWO bme280 sensros in my loft.

After I took your code, I've attempted to edit it somewhat to account for the fact i have two bme280 sensors,not just one.

I'm confused at how to account for two sensors with the way your code is laid out. Honestly, I think i'd have trouble even with my own code at understanding how to read two sensors at once. I'm not even doing that within one script with my RP4B. I have two individual scripts running there, although they both look at the same serial link. Now that i'm writing this, I'm wondering if I have to do the same in a Pico W too?

I've included how I'm doing my serial link to my ONE SHT30 device in my first Pico W here:

Code: Select all

#Necessary library imports for this code to function
from secrets import SSID
from secrets import PASSWORD
from secrets import IP
from secrets import IP_mask
from secrets import IP_gateway
from secrets import IP_DNS
from secrets import MQTTSERVE
from umqtt.simple import MQTTClient
import time
import network
import ubinascii
import socket
from machine import SoftI2C, Pin
import sht30
import uasyncio as asyncio

#Define the interface of the SHT30 Temp/Humidity sensor with the Pico W
i2c=SoftI2C(sda=Pin(4), scl=Pin(5), freq=50000)
sht=sht30.SHT30(i2c=i2c, i2c_address=68)
sensor = sht.measure()
temperature, humidity = sensor

#Define the onboard Pico Temperature sensor and necessary conversion factor
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)
 
#Active the onboard Pico W Wi-Fi and connect to home network defined through "secrets.py"
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
#print(wlan.scan())
wlan.connect(SSID, PASSWORD)

#Configure this Pico W for a static IP address on router using the following
UseStaticIP = True
IP = IP
IP_mask = IP_mask
IP_gateway = IP_gateway
IP_DNS = IP_DNS

#Read the MAC address from the Pico W itself
mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
print("MAC address for this Pico W:", mac)
# Wait for connect or fail
max_wait = 10
while max_wait > 0:
 if wlan.status() < 0 or wlan.status() >= 3:
  break
  max_wait -= 1
  print('waiting for connection...')
  time.sleep(1)

# Handle connection error
if wlan.status() != 3:
 raise RuntimeError('network connection attempt failed')
else:
 print("Pico W Wi-Fi is connected to: ",SSID)
 status = wlan.ifconfig()
 print( "ip address for this Pico W: " + status[0] )
 print()
 print("Channel: ", wlan.config('channel'))
 print("SSID: ", wlan.config('essid'))
 print("TXPOWER: ", wlan.config('txpower'))

wlan.config(pm = 0xa11140)

#Show some onboard LED flashing to show that attempting to connect to the Wi-Fi has been completed
time.sleep(5)
led = machine.Pin("LED", machine.Pin.OUT)
#led = Pin(15, Pin.OUT)
led.off()
time.sleep(2)
led.toggle()
time.sleep(0.2)
led.toggle()
time.sleep(1)
led.toggle()
time.sleep(0.2)
led.toggle()
time.sleep(1)
led.toggle()
time.sleep(0.2)
led.toggle()
time.sleep(1)
led.toggle()
time.sleep(2)

#Setup MQTT server, local client ID (not really required),
#and published data transmissions hierarchy
mqtt_server = MQTTSERVE
client_id = b'PICOW'

topic_pub1 = b'OS_Temp'
topic_pub2 = b'OS_Hum'
topic_pub3 = b'Pico_Temp'

#For the above topics, use "PicoW/+"
#at the subscriber side to receive all
#three messages at once or use the full
#titles individually i.e PicoW/OS_Temp for
#just the outside temperature variable

def mqtt_connect():
    client = MQTTClient(client_id, mqtt_server, keepalive=3600)
    client.connect()
    print('Connected to %s MQTT Broker'%(mqtt_server))
    return client

def reconnect():
    print('Failed to connect to the MQTT Broker. Reconnecting...')
    time.sleep(5)
    machine.reset()

try:
    client = mqtt_connect()
except OSError as e:
    reconnect()
    
#The main script of repeativeness for reading in from the connected sensor
#and taking in onboard temperature readings
while True: #do stuff here

    led.on()
    time.sleep(0.5)
    
    sensor = sht.measure()
    temperature, humidity = sensor
    
    reading = sensor_temp.read_u16() * conversion_factor 
    picotemp = 27 - (reading - 0.706)/0.001721
    
    #make readings 1 decimal place
    tpm1 = round(temperature,1)
    tpm2 = round(humidity,1)
    tpm3 = round(picotemp,1)
    
    #convert float reading values into bytes ready for MQTT transmission
    tpm4 = f"{tpm1}".encode()
    tpm5 = f"{tpm2}".encode()
    tpm6 = f"{tpm3}".encode()
    
    topic_msg1 = tpm4
    topic_msg2 = tpm5
    topic_msg3 = tpm6
    
    #Message displayed to the screen
    print()
    print('SHT Temperature: {:3.1f}ºC'.format(tpm1))
    print('SHT Humidity: {:3.1f}%'.format(tpm2))
    print('Pico W Temperature: {:3.1f}ºC'.format(tpm3))
    
    #Equivalent transmission of displayed readings above
    #(reverse order to match above order on subscriber receipt)
    client.publish(topic_pub3, topic_msg3)
    client.publish(topic_pub2, topic_msg2)
    client.publish(topic_pub1, topic_msg1)
    
    led.off()
  
    time.sleep(10)
And then this is what I have so far, based on your code but this is far from working. I just need some pointers in order to attempt to continue from here now:

Code: Select all

#Necessary library imports for this code to function
from secrets2 import SSID
from secrets2 import PASSWORD
from secrets2 import IP
from secrets2 import IP_mask
from secrets2 import IP_gateway
from secrets2 import IP_DNS
from secrets2 import MQTTSERVE
#from umqtt.simple import MQTTClient
import time
import network
import ubinascii
#import socket
#from machine import SoftI2C, Pin
from machine import Pin
import machine
import rp2
import sys
import bme280

wifi_essid = SSID
wifi_password = PASSWORD

# define IP,mask and gateway if IP is static
UseStaticIP = True
IP = IP
IP_mask = IP_mask
IP_gateway = IP_gateway
IP_DNS = IP_DNS
IP_MQTT_BROKER = MQTTSERVE

UnitID = "LoftPicoW"

#do we want to use watchdog to reset in case
WatchDog_Enable = True

#sensors definition

#bme280 sensors
bme_Enable = False
bme_PinSDA=4
bme_PinSCL=5
bme0_i2c_address = 0x77
bme0_temperature_topic = "bme0T"
bme0_pressure_topic = "bme0P"
bme0_humidity_topic = "bme0H"
bme1_i2c_address = 0x76
bme1_temperature_topic = "bme1T"
bme1_pressure_topic = "bme1P"
bme1_humidity_topic = "bme1H"

# stop switch  (Press to quit on reset if file is main.py)
#switchPin = 19
#ok let's set the Pins
#sw = machine.Pin(switchPin,machine.Pin.IN,machine.Pin.PULL_UP)

time.sleep_ms(10)

#On boot do we want to stop main() if yes exit
#if  sw.value() == 0:
#    sys.exit()


#### WATCH DOG 
# set maximum wachtdog 8.3s  ( 2**24 -1)/2
wdt = machine.WDT(timeout=8300000)

# define lightsleep with watch dog limits
# because we implemented wachtdog we need to
# maximize the light sleep to be lower than 8 second
# the watch dog is 24 bits at 1us clock  by a count of 2
# then the maximum is (2 power 24 -1)/2 which is ~8.3 sec
#  we will use 5 seconds step
def wd_lightsleep(value):
    if WatchDog_Enable:
        while value >0:
            if value >= 5000:
                machine.lightsleep(5000)
                value = value - 5000
            else:
                machine.lightsleep(value)
                value=0
            wdt.feed()
    else:
         machine.lightsleep(value)


#ok let's set the Pins
#i2c
# in bme280 i2c address was 0x76 put adafruit need to be 0x77

if bme_Enable and (address = 0x77):  # I don't know how to read from two sensors at once, so this is just a note so you can see what I'm trying to do
    i2c=machine.I2C(0,sda=machine.Pin(bme_PinSDA), scl=machine.Pin(bme_PinSCL), freq=1000)    #initializing the I2C method 
    bme0 = bme280.BME280(i2c=i2c,address=bme0_i2c_address)

if bme_Enable and (address = 0x76):  # I don't know how to read from two sensors at once, so this is just a note  so you can see what I'm trying to do
    i2c=machine.I2C(0,sda=machine.Pin(bme_PinSDA), scl=machine.Pin(bme_PinSCL), freq=1000)    #initializing the I2C method 
    bme1 = bme280.BME280(i2c=i2c,address=bme1_i2c_address)

#Led
led = machine.Pin('LED', machine.Pin.OUT)


#cpu temperature declaration
cpu_temp = machine.ADC(machine.ADC.CORE_TEMP)
conversion_factor = 3.3 / (65535)

def readCpuTemperature():
  reading = cpu_temp.read_u16() * conversion_factor
  return 27 - (reading - 0.706)/0.001721
    
def readBme():
    try:
        t, p, h = bme.read_compensated_data()
        t, p, h = bme.read_compensated_data()
        return (t/100.0 , p/25600.0, h/1024.0)
    except OSError as e:
        pass
    return None    

#network declaration
# Set country to avoid possible errors / https://randomnerdtutorials.com/micropython-mqtt-esp32-esp8266/
rp2.country('GB')
wlan=None
client=None

def stopWifi():
    global client
    global wlan
    if not(client==None):
        client.disconnect()
        client=None
    if not(wlan==None):
        wlan.disconnect()
        wlan.active(False)
        wlan.deinit()
        wlan=None
    time.sleep_ms(100)

def setWifi():
    global wlan
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    # See the MAC address in the wireless chip OTP
    mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
    print("MAC address for this Pico W:", mac)
    # Load logimin data from different file for safety reasons
    #set static IP
    # (IP,mask,gateway,dns)
    if UseStaticIP:
        wlan.ifconfig((IP, IP_mask,IP_gateway,IP_DNS))
    wlan.connect(wifi_essid,wifi_password)
    #set 10 seconds for time out delay
    wlandelay =  time.ticks_ms() + 12000
    while time.ticks_ms() < wlandelay:
      if wlan.isconnected():
          if wlan.status() <0  or wlan.status() >=3 :
              break
      machine.idle()
      if WatchDog_Enable:
          wdt.feed()

    # Handle connection error
    # Error meanings
    # 0  Link Down
    # 1  Link Join
    # 2  Link NoIp
    # 3  Link Up
    # -1 Link Fail
    # -2 Link NoNet
    # -3 Link BadAuth

    if wlan.status() != 3:
        return False
    return True


def connectWifi():
    for i in range(3):
        print("connect Client",i)
        if setWifi()==False:
            stopWifi()
            continue
        else:
            return True
    #unable to connect after 3 tentatives
    return False
    
###MQTT  Topic Setup ###

def connectMQTT():
  client = MQTTClient(UnitID,IP_MQTT_BROKER)
  client.connect()
  return client


def publish(topic,value,idx=None):
  global UnitID
  if WatchDog_Enable:
      wdt.feed()
  print(topic,value)
  pub_msg = "%5.3f" % value
  print(topic,"  ",pub_msg)
  if idx==None:
      client.publish(UnitID+"/"+topic, pub_msg)
  else:
      client.publish(UnitID+"/"+topic+"_"+str(idx), pub_msg)
  print("publish Done")



def getSensorsAndPublish():
  global vsys
  
  if bme_Enable:
      v = readBme()

      publish(bme0_temperature_topic,v[0])
      publish(bme0_pressure_topic,v[1])
      publish(bme0_humidity_topic,v[2])
      publish(bme1_temperature_topic,v[3])
      publish(bme1_pressure_topic,v[4])
      publish(bme1_humidity_topic,v[5])
      publish('TempCPU',readCpuTemperature())

#ok main process

try:  
    while True:
        PowerAnalogPin(True)
        try:
            if not connectWifi():
                print("wifi connect Failed restart")
                time.sleep_ms(500)
                machine.reset()
            client = connectMQTT()
        except OSError as e:
            print("WIFI OR MQTT Failed restart")
            time.sleep_ms(500)
            machine.reset()
        led.value(True)
        time.sleep_ms(100)
        led.value(False)
        getSensorsAndPublish()
        stopWifi()
        time.sleep_ms(50)
        wd_lightsleep(9000)  #9 sec cycle
        
except OSError as e:
    print("OS ERROR restart")
    time.sleep_ms(500)
    machine.reset()
Can anyone give me some assistance with correcting this code where I need to to make it work?

beetle
Posts: 51
Joined: Sat Oct 16, 2021 11:35 am

Re: BMP280 / BME280 uPython

Post by beetle » Sat Aug 27, 2022 7:01 pm

Can anyone give me some assistance with correcting this code where I need to to make it work?
Have a peep at this:
https://learn.adafruit.com/working-with ... 2c-devices

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: BMP280 / BME280 uPython

Post by Rissy » Sun Aug 28, 2022 7:48 am

beetle wrote:
Sat Aug 27, 2022 7:01 pm
Can anyone give me some assistance with correcting this code where I need to to make it work?
Have a peep at this:
https://learn.adafruit.com/working-with ... 2c-devices
Ah right, so I basically already had it then. I just wrongly tried to separate the two address assignment statements with separate if conditions unnecessarily.

I'm afraid to say that danjperron's code is too complicated for me to follow. I just don't have a good enough handle on functions in my head yet (I'm sorry, I'm still new to this).

So i've tried to make a bme280 equivalent of my previous SHT30 code instead.

Can you take a look and tell me if you think this is correct...(by that i mean work)?

Code: Select all

#Necessary library imports for this code to function
from secrets import SSID
from secrets import PASSWORD
from secrets import IP
from secrets import IP_mask
from secrets import IP_gateway
from secrets import IP_DNS
from secrets import MQTTSERVE
from umqtt.simple import MQTTClient
import time
import network
import ubinascii
from machine import SoftI2C, Pin
import bme280

#bme280 sensor addresses
bme0_i2c_address = 0x77
bme1_i2c_address = 0x76

#Define the interface of the SHT30 Temp/Humidity sensor with the Pico W
i2c=SoftI2C(sda=Pin(4), scl=Pin(5), freq=50000) #default frequency is 400,000 for short cables.  For longer cables, use reduced frequency value
bme0 = bme280.BME280(i2c=i2c, address=bme0_i2c_address)
bme1 = bme280.BME280(i2c=i2c, address=bme1_i2c_address)

#Define the onboard Pico Temperature sensor and necessary conversion factor
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)
 
#Active the onboard Pico W Wi-Fi and connect to home network defined through "secrets.py"
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
#print(wlan.scan())
wlan.connect(SSID, PASSWORD)

#Configure this Pico W for a static IP address on router using the following
UseStaticIP = True
IP = IP
IP_mask = IP_mask
IP_gateway = IP_gateway
IP_DNS = IP_DNS

#Read the MAC address from the Pico W itself
mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
print("MAC address for this Pico W:", mac)

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

# Handle connection error
if wlan.status() != 3:
 raise RuntimeError('network connection attempt failed')
else:
 print("Pico W Wi-Fi is connected to: ",SSID)
 status = wlan.ifconfig()
 print( "ip address for this Pico W: " + status[0] )
 print()
 print("Channel: ", wlan.config('channel'))
 print("SSID: ", wlan.config('essid'))
 print("TXPOWER: ", wlan.config('txpower'))

#Configure a high power wi-fi connection
wlan.config(pm = 0xa11140)

#Show some onboard LED flashing to show that attempting to connect to the Wi-Fi has been completed
time.sleep(5)
led = machine.Pin("LED", machine.Pin.OUT)
#led = Pin(15, Pin.OUT)
led.off()
time.sleep(2)
led.toggle()
time.sleep(0.2)
led.toggle()
time.sleep(1)
led.toggle()
time.sleep(0.2)
led.toggle()
time.sleep(1)
led.toggle()
time.sleep(0.2)
led.toggle()
time.sleep(1)
led.toggle()
time.sleep(2)

#Setup MQTT server, local client ID (not really required),
#and published data transmissions hierarchy
mqtt_server = MQTTSERVE
client_id = b'PICOW2'
bme0_temperature_topic = b'PicoW2/bme0T'
bme0_pressure_topic = b'PicoW2/bme0P'
bme0_humidity_topic = b'PicoW2/bme0H'
bme1_temperature_topic = b'PicoW2/bme1T'
bme1_pressure_topic = b'PicoW2/bme1P'
bme1_humidity_topic = b'PicoW2/bme1H'
pico2_temp_topic = b'PicoW2/PicoTemp'

#For the above topics, use "PicoW2/+"
#at the subscriber side to receive all
#three messages at once or use the full
#titles individually i.e PicoW2/bme0T for
#just the sensor 0 temperature variable

def mqtt_connect():
    client = MQTTClient(client_id, mqtt_server, keepalive=3600)
    client.connect()
    print('Connected to %s MQTT Broker'%(mqtt_server))
    return client

def reconnect():
    print('Failed to connect to the MQTT Broker. Reconnecting...')
    time.sleep(5)
    machine.reset()

try:
    client = mqtt_connect()
except OSError as e:
    reconnect()
    
#The main script of repeativeness for reading in from the connected sensor
#and taking in onboard temperature readings
while True: #do stuff here

    led.on()
    time.sleep(0.5)

    #read in values direct from sensor
    t0, p0, h0 = bme0.read_compensated_data()
    t1, p1, h1 = bme1.read_compensated_data()

    #adjust sensor readings with some maths
    at0 = t0/100.0
    ap0 = p0/25600.0
    ah0 = h0/1024.0
    at1 = t1/100.0
    ap1 = p1/25600.0
    ah1 = h1/1024.0)

    #read the onboard temperature of the Pico itself
    reading = sensor_temp.read_u16() * conversion_factor 
    pico2temp = 27 - (reading - 0.706)/0.001721
    ap2t = round(pico2temp,1)

    #convert float reading values into bytes ready for MQTT transmission
    tpm1 = f"{at0}".encode()
    tpm2 = f"{ap0}".encode()
    tpm3 = f"{ah0}".encode()
    tpm4 = f"{at1}".encode()
    tpm5 = f"{ap1}".encode()
    tpm6 = f"{ah1}".encode()
    tpm7 = f"{ap2t}".encode()

    topic_msg1 = tpm1
    topic_msg2 = tpm2
    topic_msg3 = tpm3
    topic_msg4 = tpm4
    topic_msg5 = tpm5
    topic_msg6 = tpm6
    topic_msg7 = tpm7

    #Message displayed to the screen
    print()
    print('BME0 Temperature: {:3.1f}ºC'.format(at0))
    print('BME0 Humidity: {:3.1f}%'.format(ah0))
    print('BME0 Pressure: {:3.1f}%'.format(ap0))
    print('BME1 Temperature: {:3.1f}ºC'.format(at1))
    print('BME1 Humidity: {:3.1f}%'.format(ah1))
    print('BME1 Pressure: {:3.1f}%'.format(ap1))
    print('Pico W Temperature: {:3.1f}ºC'.format(ap2t))
    
    #Equivalent transmission of displayed readings above
    #(reverse order to match above order on subscriber receipt)
    client.publish(pico2_temp_topic, topic_msg7)
    client.publish(bme1_humidity_topic, topic_msg6)
    client.publish(bme1_pressure_topic, topic_msg5)
    client.publish(bme1_temperature_topic, topic_msg4)
    client.publish(bme0_humidity_topic, topic_msg3)
    client.publish(bme0_pressure_topic, topic_msg2)
    client.publish(bme0_temperature_topic, topic_msg1)
    
    led.off()
  
    time.sleep(10)

beetle
Posts: 51
Joined: Sat Oct 16, 2021 11:35 am

Re: BMP280 / BME280 uPython

Post by beetle » Sun Aug 28, 2022 11:41 am

Rissy wrote:
Sun Aug 28, 2022 7:48 am

Can you take a look and tell me if you think this is correct...(by that i mean work)?

It may work, too hard to say without actually running it.

Some quick observations are:
for your led blinking code consider:

Code: Select all

def flash_led(num_flashes, on_time, off_time,):
    for _ in range(num_flashes):
        led.on()
        time.sleep(on_time)
        led.off()
        time.sleep(off_time)
                 
flash_led(10,0.02,1)       
 


and instead of all those variable assignments - e.g
bme0_temperature_topic = b'PicoW2/bme0T'
tpm1 = f"{at0}".encode()
topic_msg1 = tpm1


and then publishing your mqtt message as
client.publish(bme0_temperature_topic, topic_msg1)

How about just doing the following
client.publish("PicoW2/bme0T", str(at0))

I suggest you just run your code and if you get errors try to see if you can fix them. Its often just a missing bracket or colon.

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: BMP280 / BME280 uPython

Post by Rissy » Sun Aug 28, 2022 3:36 pm

beetle wrote:
Sun Aug 28, 2022 11:41 am
Rissy wrote:
Sun Aug 28, 2022 7:48 am

Can you take a look and tell me if you think this is correct...(by that i mean work)?

It may work, too hard to say without actually running it.

Some quick observations are:
for your led blinking code consider:

Code: Select all

def flash_led(num_flashes, on_time, off_time,):
    for _ in range(num_flashes):
        led.on()
        time.sleep(on_time)
        led.off()
        time.sleep(off_time)
                 
flash_led(10,0.02,1)       
 


and instead of all those variable assignments - e.g
bme0_temperature_topic = b'PicoW2/bme0T'
tpm1 = f"{at0}".encode()
topic_msg1 = tpm1


and then publishing your mqtt message as
client.publish(bme0_temperature_topic, topic_msg1)

How about just doing the following
client.publish("PicoW2/bme0T", str(at0))

I suggest you just run your code and if you get errors try to see if you can fix them. Its often just a missing bracket or colon.
I've not run this code yet, because I'm now waiting for yet another two bme280 sensors to come through the post to allow me to prototype with so that I don't need to upset my existing setup while i continue to get things wrong etc.

It took me 7hrs yesterday just to set up my SHT30 outside and get that working! I was literally pulling my hair out. I still don't have a good reason why it worked with my laptop even before putting in a frequency value for it to finally work with an ordinary usb power supply!?

It's funny you should suggest a definition for flashing LEDs here because after i posted this morning, I had a play at trying to get some definitions of functions of my own working.

I came up with a separate file just dedicated to flashing the LED. One def (0) gets called by my boot.py and the other def (1) gets called by my main.py (this is the one that you've done your own of above).

This is what I came up with:

defflash.py

Code: Select all

import time
import machine
from machine import Pin
#led=Pin(13,Pin.OUT)        #create LED object from pin13,Set Pin13 to output
led = machine.Pin("LED", machine.Pin.OUT)

def ledflash0():
    count = 0
    while count < 5:
        led.off()
        led.toggle()
        time.sleep(1)
        led.toggle()
        time.sleep(1)
        count = count + 1
        time.sleep(0.3)

#time1value = 0.2
#time2value = 1
#time3value = 1.5
#time4value = 2

def ledflash1(time1, time2, time3, time4):
    #pass #use this to leave the function empty
    led.off()#Set led turn off
    time.sleep(time4)
    led.toggle()#Set led turn on
    time.sleep(time1)
    led.toggle()#Set led turn off
    time.sleep(time2)
    led.toggle()#Set led turn on
    time.sleep(time1)
    led.toggle()#Set led turn off
    time.sleep(time2)
    led.toggle()#Set led turn on
    time.sleep(time1)
    led.toggle()#Set led turn off
    time.sleep(time2)
    led.toggle()#Set led turn on
    time.sleep(time3)
    led.toggle()#Set led turn off
    
#ledflash(time1value, time2value, time3value, time4value)
#led.toggle()
boot.py

Code: Select all

import machine
import time
from defflash import ledflash0

ledflash0()
part of main.py which calls defflash

Code: Select all

from defflash import ledflash1

ledflash1(time1value, time2value, time3value, time4value)
See I wouldn't have done your example of this:

Code: Select all

def flash_led(num_flashes, on_time, off_time,):
    for _ in range(num_flashes):
        led.on()
        time.sleep(on_time)
        led.off()
        time.sleep(off_time)
                 
flash_led(10,0.02,1)       
basically because i don't know about the "_" and the "in range" commands. What does "_" do? and when can you randomly just shout out "in range" in the code? It's all of these intricacies which I don't have in my head, which stops me from writing code like this, or understanding other people's code. Of course i can look it up, and I am doing that with a lot, but it's time consuming and I'm being pulled to do other things, especially now that I have a working system (they think its all done now :D )

beetle
Posts: 51
Joined: Sat Oct 16, 2021 11:35 am

Re: BMP280 / BME280 uPython

Post by beetle » Sun Aug 28, 2022 6:24 pm

Rissy wrote:
Sun Aug 28, 2022 3:36 pm
basically because i don't know about the "_" and the "in range" commands. What does "_" do? and when can you randomly just shout out "in range" in the code? It's all of these intricacies which I don't have in my head, which stops me from writing code like this, or understanding other people's code. Of course i can look it up, and I am doing that with a lot, but it's time consuming and I'm being pulled to do other things, especially now that I have a working system (they think its all done now :D )
Loops, flow control and conditional statements are fundamental to any programming language. You must understand and have the ability to realise how loops are used to repeat chunks of code to proceed. There are 2 looping statements in python - the for loop and the while loop.

Regarding the '_' in my for loop, that could have been written as 'for num in range(10)' but 'num', 'x', 'something' or indeed '_' are just variable names to hold the current number in the range. As I was not actually using the variable for anything in this instance I used '_' to indicate to me that I was deliberately not using this value. Instead of a for loop I could have equally as well used a while loop. e.g.

Code: Select all

def flash_led(num_flashes, on_time, off_time,):
    flashes = 0
    while flashes < num_flashes:
        led.on()
        time.sleep(on_time)                 
        led.off()
        time.sleep(off_time)
        flashes += 1        
        
flash_led(10,0.02,1)   
The important bit ls that you recognise that you need to create a loop for a block of code and to loop until the reason for repeating the code is no longer valid (an if still True sort of condition ;) )

In the example I put the code in a function. This means that you could use that function again somewhere else in your code to do some more flashing. However the loop in this case was the important thing, not the fact that the code was in a function. But if you repeatedly write the same bit of code in your program its a sure sign you need a function to contain that block of code. The function can then be repeatedly called. Keep your code DRY. Do not Repeat Yourself. :D . Eventually you could keep some of your much used functions into a separate python script and import that module into other programs you create. That flash_led function may be used in many of your future programs, and you would not have to write it again. Just import it and call it.

When I gave you a link to the cory videos in a previous thread, I also gave a link to a very good beginners tutorial which I give again.
https://thepythoncodingbook.com
I think that your 7 hours could have been spent going through that doc and loops are very well covered in that doc. Its a good, but relatively concise, read that is very suited to beginners in both python and programming in general.

I actually think you have come a long way, and understand your main drive was to get your humidity and temperature sensors working ASAP. But now, as you do have a working version albeit a bit wonky, doing just a few more hours on productively learning python programming fundamentals will prove very worthwhile. You will soon be adding buttons, buzzers, speakers, and gawd now what to your expanding project, maybe 8-)

danjperron
Posts: 51
Joined: Thu Dec 27, 2018 11:38 pm
Location: Québec, Canada

Re: BMP280 / BME280 uPython

Post by danjperron » Sun Aug 28, 2022 7:12 pm

Code: Select all

After I took your code, I've attempted to edit it somewhat to account for the fact i have two bme280 sensors,not just one.
Ok two bme sensor ???? I tried to figure why two. Are you settings on different pressure box or you are just interrested to humidity and temperature.

Two bme sensor hsould be simple to implement just have to create another bme object.

If you are using the same i2c you just need have the second bme to be on different i2c address.


Code: Select all

i2c=machine.I2C(0,sda=machine.Pin(bme_PinSDA), scl=machine.Pin(bme_PinSCL), freq=100000)    #initializing the I2C method 
 bme = bme280.BME280(i2c=i2c,address=bme_i2c_address)
 bme2 = bme280.BME280(i2c=i2c,address=bme2_i2c_address)
and modify the read bme to implement which bme you want

Code: Select all

def readBme(bme_sensor):
    try:
        t, p, h = bme_sensor.read_compensated_data()
        t, p, h = bme_sensor.read_compensated_data()
        return (t/100.0 , p/25600.0, h/1024.0)
    except OSError as e:
        pass
    return None    
then you publish them

Code: Select all

#read first bme 
 v = readBme(bme)
 #publish it
publish(bme_temperature_topic,v[0])
publish(bme_pressure_topic,v[1])
publish(bme_humidity_topic,v[2])
#and do the second bme
 v = readBme(bme2)
 #publish it
publish(bme2_temperature_topic,v[0])
publish(bme2_pressure_topic,v[1])
publish(bme2_humidity_topic,v[2])
Maybe the best thing will be to ceate a function that read and publish the specific sensor.

Really strange idea to have two bme280 on the same pico. Why paying so much for a sensor with air pressure.

danjperron
Posts: 51
Joined: Thu Dec 27, 2018 11:38 pm
Location: Québec, Canada

Re: BMP280 / BME280 uPython

Post by danjperron » Sun Aug 28, 2022 7:36 pm

B.T.W.


Step 1 . Does my simple script works ?
Step 2. If you follow my solar pico , does it works with a simple bme sensors. If it doesn't what was the problem.

Rissy
Posts: 116
Joined: Sun Aug 14, 2022 8:15 am

Re: BMP280 / BME280 uPython

Post by Rissy » Thu Sep 01, 2022 2:57 pm

I'm back!

My BMP280 and my TWO additional BME280 sensors literally arrived about 2hrs ago and I couldn't wait to test them out on my second PicoW.

The code i'd put together for my Pico W was basically 99% good. A little bit of a tweek for decimal places and that was it. I spent hours the other day using just the CPU temp on the Pico W to test the additional MQTT link during setting up my second RP4B much like what I already had on my first RP4B which is still stuck up in my loft. Everything is looking good, although i'm going down the rabbit hole with wanting to improve the way i'm doing things before I do the switch over.

Among other things, I'm wanting to set up what I believe I need to get some nice graphics going for my readings on my RP4B. Namely, telegraf to influxdb and then into grafana. I started looking into this yesterday with no more progress possibilities with my Pico until the sensors arrived. This telegraf to influxdb to grafana stuff is SO complicated. My head hurt after several hours trying to figure out what I needed to do after already spending hours trying to understand what I needed to install, and how to do that. Even the youtube videos and guides on the internet dont go dumb enough for me to fully follow or get my head around. so if anyone has any offer of assistance there, I'd be most appreciative.

Anyhoo. the reason for me coming online today was to really ask the following:

On my RP4B up in the loft, I had to change the __init__.py file because there is a line of code there which you need to change according to how you want your bme280 devices to be read and perform based on the expected environment conditions you wish to measure at.

This line looks like the following (original hashed out and my replacement):

Code: Select all

#def setup(self, mode='normal', temperature_oversampling=16, pressure_oversampling=16, humidity_oversampling=16, temperature_standby=500):
def setup(self, mode='normal', temperature_oversampling=2, pressure_oversampling=16, humidity_oversampling=1, temperature_standby=500):
Because now I'm wishing to read from my BME280s via a PicoW I2C and MQTT these readings to my RP4B rather than read them in directly on the RP4B I2C link, I was looking for the same privilege settings on the BME280 driver file I'm using, but this isn't clear to me. Can someone advise me how to set this file up like I have the RP4B set up as described above?

This is the driver file i'm using in my Pico W:

Code: Select all

# Authors: Paul Cunnane 2016, Peter Dahlebrg 2016
#
# This module borrows from the Adafruit BME280 Python library. Original
# Copyright notices are reproduced below.
#
# Those libraries were written for the Raspberry Pi. This modification is
# intended for the MicroPython and esp8266 boards.
#
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Based on the BMP280 driver with BME280 changes provided by
# David J Taylor, Edinburgh (www.satsignal.eu)
#
# Based on Adafruit_I2C.py created by Kevin Townsend.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import time
from ustruct import unpack, unpack_from
from array import array

# BME280 default address.
BME280_I2CADDR = 0x76

# Operating Modes
BME280_OSAMPLE_1 = 1
BME280_OSAMPLE_2 = 2
BME280_OSAMPLE_4 = 3
BME280_OSAMPLE_8 = 4
BME280_OSAMPLE_16 = 5

BME280_REGISTER_CONTROL_HUM = 0xF2
BME280_REGISTER_CONTROL = 0xF4


class BME280:

    def __init__(self,
                 mode=BME280_OSAMPLE_1,
                 address=BME280_I2CADDR,
                 i2c=None,
                 **kwargs):
        # Check that mode is valid.
        if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
                        BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
            raise ValueError(
                'Unexpected mode value {0}. Set mode to one of '
                'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or '
                'BME280_ULTRAHIGHRES'.format(mode))
        self._mode = mode
        self.address = address
        if i2c is None:
            raise ValueError('An I2C object is required.')
        self.i2c = i2c

        # load calibration data
        dig_88_a1 = self.i2c.readfrom_mem(self.address, 0x88, 26)
        dig_e1_e7 = self.i2c.readfrom_mem(self.address, 0xE1, 7)
        self.dig_T1, self.dig_T2, self.dig_T3, self.dig_P1, \
            self.dig_P2, self.dig_P3, self.dig_P4, self.dig_P5, \
            self.dig_P6, self.dig_P7, self.dig_P8, self.dig_P9, \
            _, self.dig_H1 = unpack("<HhhHhhhhhhhhBB", dig_88_a1)

        self.dig_H2, self.dig_H3 = unpack("<hB", dig_e1_e7)
        e4_sign = unpack_from("<b", dig_e1_e7, 3)[0]
        self.dig_H4 = (e4_sign << 4) | (dig_e1_e7[4] & 0xF)

        e6_sign = unpack_from("<b", dig_e1_e7, 5)[0]
        self.dig_H5 = (e6_sign << 4) | (dig_e1_e7[4] >> 4)

        self.dig_H6 = unpack_from("<b", dig_e1_e7, 6)[0]

        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
                             bytearray([0x3F]))
        self.t_fine = 0

        # temporary data holders which stay allocated
        self._l1_barray = bytearray(1)
        self._l8_barray = bytearray(8)
        self._l3_resultarray = array("i", [0, 0, 0])

    def read_raw_data(self, result):
        """ Reads the raw (uncompensated) data from the sensor.
            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order
            Returns:
                None
        """

        self._l1_barray[0] = self._mode
        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL_HUM,
                             self._l1_barray)
        self._l1_barray[0] = self._mode << 5 | self._mode << 2 | 1
        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
                             self._l1_barray)

        sleep_time = 1250 + 2300 * (1 << self._mode)
        sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
        sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
        time.sleep_us(sleep_time)  # Wait the required time

        # burst readout from 0xF7 to 0xFE, recommended by datasheet
        self.i2c.readfrom_mem_into(self.address, 0xF7, self._l8_barray)
        readout = self._l8_barray
        # pressure(0xF7): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_press = ((readout[0] << 16) | (readout[1] << 8) | readout[2]) >> 4
        # temperature(0xFA): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_temp = ((readout[3] << 16) | (readout[4] << 8) | readout[5]) >> 4
        # humidity(0xFD): (msb << 8) | lsb
        raw_hum = (readout[6] << 8) | readout[7]

        result[0] = raw_temp
        result[1] = raw_press
        result[2] = raw_hum

    def read_compensated_data(self, result=None):
        """ Reads the data from the sensor and returns the compensated data.
            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order. You may use
                this to read out the sensor without allocating heap memory
            Returns:
                array with temperature, pressure, humidity. Will be the one from
                the result parameter if not None
        """
        self.read_raw_data(self._l3_resultarray)
        raw_temp, raw_press, raw_hum = self._l3_resultarray
        # temperature
        var1 = ((raw_temp >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11)
        var2 = (((((raw_temp >> 4) - self.dig_T1) *
                  ((raw_temp >> 4) - self.dig_T1)) >> 12) * self.dig_T3) >> 14
        self.t_fine = var1 + var2
        temp = (self.t_fine * 5 + 128) >> 8

        # pressure
        var1 = self.t_fine - 128000
        var2 = var1 * var1 * self.dig_P6
        var2 = var2 + ((var1 * self.dig_P5) << 17)
        var2 = var2 + (self.dig_P4 << 35)
        var1 = (((var1 * var1 * self.dig_P3) >> 8) +
                ((var1 * self.dig_P2) << 12))
        var1 = (((1 << 47) + var1) * self.dig_P1) >> 33
        if var1 == 0:
            pressure = 0
        else:
            p = 1048576 - raw_press
            p = (((p << 31) - var2) * 3125) // var1
            var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
            var2 = (self.dig_P8 * p) >> 19
            pressure = ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)

        # humidity
        h = self.t_fine - 76800
        h = (((((raw_hum << 14) - (self.dig_H4 << 20) -
                (self.dig_H5 * h)) + 16384)
              >> 15) * (((((((h * self.dig_H6) >> 10) *
                            (((h * self.dig_H3) >> 11) + 32768)) >> 10) +
                          2097152) * self.dig_H2 + 8192) >> 14))
        h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4)
        h = 0 if h < 0 else h
        h = 419430400 if h > 419430400 else h
        humidity = h >> 12

        if result:
            result[0] = temp
            result[1] = pressure
            result[2] = humidity
            return result

        return array("i", (temp, pressure, humidity))

    @property
    def values(self):
        """ human readable values """

        t, p, h = self.read_compensated_data()

        p = p // 256
        pi = p // 100
        pd = p - pi * 100

        hi = h // 1024
        hd = h * 100 // 1024 - hi * 100
        return ("{}C".format(t / 100), "{}.{:02d}hPa".format(pi, pd),
                "{}.{:02d}%".format(hi, hd))
The other quick question I have about this driver file is purely for my own education interest:

Near the top you'll see:

Code: Select all

# BME280 default address.
BME280_I2CADDR = 0x76
But in my main.py code, I'm able to read both potential addresses, as guided by you guys the other week like this:

Code: Select all

#bme280 sensor addresses
bme0_i2c_address = 0x77
bme1_i2c_address = 0x76

#Define the interface of the SHT30 Temp/Humidity sensor with the Pico W
i2c=SoftI2C(sda=Pin(4), scl=Pin(5), freq=50000) #default frequency is 400,000 for short cables.  For longer cables, use reduced frequency value
bme0 = bme280.BME280(i2c=i2c, address=bme0_i2c_address)
bme1 = bme280.BME280(i2c=i2c, address=bme1_i2c_address)
How can I do this if in the driver file, there appears to be a fixed address and its set only to 0x76?! I didn't understand even though it clearly works as I have it....?!

danjperron
Posts: 51
Joined: Thu Dec 27, 2018 11:38 pm
Location: Québec, Canada

Re: BMP280 / BME280 uPython

Post by danjperron » Thu Sep 01, 2022 5:14 pm

How can I do this if in the driver file, there appears to be a fixed address and its set only to 0x76?! I didn't understand even though it clearly works as I have it....?!
OH! this is a python behavior

if you check the driver you got

Code: Select all

# BME280 default address.
BME280_I2CADDR = 0x76

class BME280:

    def __init__(self,
                 mode=BME280_OSAMPLE_1,
                 address=BME280_I2CADDR,
                 i2c=None,
                 **kwargs):
this mean that if you don't declare address when you create the object it will use BME280_I2CADDR which is 0x76 in the library.
But if you specify address=X it will use X.

Post Reply