Raspberry Pi Pico W BME680 Raumklima Sensor

Aus Laub-Home Wiki

Mit dem Raspberry Pi Pico oder Raspberry Pi Pico w ist es möglich den BME680 Raumklima Sensor von Bosch auszulesen. Dieses Setup kann man zum Beispiel nutzen, um dauerhaft Temperatur, Luftfeuchtigkeit, Luftdruck und CO2 in einem Raum zu messen. Es ist möglich den Pico mit einem Battery Pack zu nutzen, was ihn dann somit sehr flexibel der Örtlichkeiten macht. Verbindet man den Pico W mit einem Wlan ist es auch möglich die Werte zum Beispiel via MQTT an das Smart Home senden.

Seid ihr auf der Suche nach einer Anleitung den BME680 Sensor an einem Raspberry Pi zum laufen zu bekommen, schaut hier weiter:

Voraussetzungen

Wenn ihr noch keine Erfahrung mit dem Raspberry Pi Pico gesammelt habt, dann schaut am besten erst einmal hier rein:

Anschluss des Sensors am GPIO

Raspberry Pi Pico Pinbelegung
Raspberry Pi Pico W Pinbelegung

Wir schließen den Sensor wie folgt an:

Raspberry Pi Pico BME680
Pin 36 3V3 (OUT) VCC
Pin 28 GND GND
Pin 27 GP21(I2C0 SCL) SCL
Pin 26 GP20(I2C0 SDA) SDA

Auslesen der Daten

Mit dem folgenden MicroPython Scripten kann man die Werte des BME680 Sensors auslesen:

Das "Treiber" Script müsst ihr vorher auf jeden Fall auf den Pico übertragen, am besten legt es unter /lib ab. Ihr könnt es hier herunterladen:

lib/bme680.py

als nächstes könnt ihr mit dem folgenden Script einen ersten Test vagen, ob der Sensor alle Werte ausliefert:

bme680_sample.py

# -*- coding: utf-8 -*-
"""BME680 Sensor 
reads Temperature, Humidity, Pressure and Gas of a BME680 sensor
you need to download first bme680.py from:
* https://raw.githubusercontent.com/alaub81/rpi_pico_scripts/main/lib/bme680.py
and upload it to the lib or root folder of your pico
"""

from bme680 import *
from machine import I2C, Pin
from time import sleep

# Initialize I2C-Pins
i2c_sda = Pin(20)
i2c_scl = Pin(21)

# change this to match the location's pressure (hPa) at sea level
sealevelpressure = 1013.25

# Initialize BME680 Sensor
bme680 = BME680_I2C(I2C(0, sda=i2c_sda,scl=i2c_scl))

# You will usually have to add an offset to account for the temperature of
# the sensor. This is usually around 5 degrees but varies by use. Use a
# separate temperature sensor to calibrate this one.
temperature_offset = -2

# set sealevel if it is conffigured
if 'sealevelpressure' in locals():
    bme680.sea_level_pressure = sealevelpressure

# degree symbol decleration
degreecels = '\u00B0' + "C"

while True:
    print("\nTemperature: %0.1f " % (bme680.temperature + temperature_offset) + degreecels)
    print("Gas: %d ohm" % bme680.gas)
    print("Humidity: %0.1f %%" % bme680.humidity)
    print("Pressure: %0.3f hPa" % bme680.pressure)
    print("Altitude = %0.2f meters" % bme680.altitude)
    sleep(3)

Die Konsolen Ausgabe sieht dann wie folgt aus.

>>> Running bme680_sample.py
 

Temperature: 24.1 °C
Gas: 10300 ohm
Humidity: 60.1 %
Pressure: 1009.070 hPa
Altitude = 34.89 meters

Temperature: 24.9 °C
Gas: 26214 ohm
Humidity: 59.0 %
Pressure: 1009.074 hPa
Altitude = 34.84 meters

Das Script könnt ihr auch hier herunterladen:

Senden der Sensorwerte via MQTT

Möchte man die Sensor Werte dann an einen MQTT Broker schicken, muss man zunächst den Pico W mit dem WLAN Verbinden und dann die ausgelesenen Daten mit umqtt an den MQTT Broker schicken. All dies macht das folgende Script. Die MQTT Werte werden nach der Homie MQTT Convention gesendet. Als Voraussetzung zur Nutzung des Skriptes (bme680homiemqtt.py) braucht ihr eine config.py mit allen Variablen zur Konfiguration und die wificonnection.py die für die WLAN Verbindung zuständig ist. Diese beiden Dateien, müssen auch immer auf den Pico mit übertragen werden. Auch wenn ihr das bme680homiemqtt.py in eurer IDE testen möchtet, greift dieses auf die auf dem Pico liegenden config.py und wificonnection.py zu. Bitte auch falls noch nicht geschehen, die "Treiber" Library bme680.py nach lib uploaden.

Ihr findet alle Scripte auch unter GitHub:

Installation umqtt.simple2 Library

Da die Library umqtt.simple2 nicht standardmäßig in der Pico MicroPython Version enthalten ist, müssen wir diese zu erst manuell installieren. Dafür müsst ihr euch, am besten mit eurer IDE an das MicroPython REPL verbinden.

Dort muss man sich zuerst mit dem WLAN verbinden. Danach folgendes auf der Konsole eingeben:

import upip
upip.install("micropython-umqtt.simple2")

config.py -- Konfigurationsdatei

In dieser Datei könnt ihr die Variablen definieren, die von der wificonnection.py und bme680homiemqtt.py verwendet werden.

config.py

# -*- coding: utf-8 -*-
"""Configuration File
This file is where you keep secret settings, passwords, and tokens!
If you put them in the code you risk committing that info or sharing it
you need to upload that file also on the Pico,
even if you are just testing your scripts.
"""

### Board Configuration ###
## Status LED ##
# LED can show you the status of the running scripts
# e.g. Wifi Connection Status Code
ledstatus = True

## LightSleep Mode ##
# Using LightSleep mode of Pico W to save power (True) just a Loop when "False"
usinglightsleep = True

## Wifi ##
# Wifi Connection Settings
wifissid = "<YourSSID>"
wifipassword = "<YOourWifiPassword>"
wificountrycode = "<CountryCode>" # "DE" for example
# Wifi Access Point Settings
wifiapssid = "<SSID>"
wifiappassword = "<WifiPassword>"


### MQTT Broker ###
mqttbroker = "<FQDN / IP of MQTTBroker>"
mqttport = 1883 # 8883 for TLS
mqttusername = "<MQTT-Username>"
mqttpassword = "<MQTT-Password"
mqttclientid = "<UniqueMQTTClientID>" # "pico-w-001" for example
# if you like to generate a unique ID:
#import ubinascii
#mqttclientid = ubinascii.hexlify(machine.unique_id())
mqttssl = False # True for TLS connection
mqttqos = 0 # only 0 and 1 is supported
mqttretainmessage = True # True or False
# Homie Configuration / Naming
homieclientid = "<UniqueHomieClientID>" # "pico-w-001-bme680" for example
homieclientname = "<Name your Homie Device>" # "Pico W 001 BME680 Sensor" for example
homienodes="bme680" # for example "bme680"
# how often should be a publish to MQTT (in Seconds)
publishtime = 300


### Sensor Configurations ###
# At which value humidity alarm will be fired (x in %)
humidityalarm = 70

## BME680 Sensor ##
# Initialize BME680 I2C-Pins
i2c_sda = 20
i2c_scl = 21
# change this to match the location's pressure (hPa) at sea level
sealevelpressure = 1013.25
# You will usually have to add an offset to account for the temperature of
# the sensor. This is usually around 5 degrees but varies by use. Use a
# separate temperature sensor to calibrate this one.
temperature_offset = -2

wificonnection.py -- Wlan Management Datei

Mit Hilfe dieser Datei, verbindet sich die bme680homiemqtt.py mit dem Wifi. Und killt die Verbindung wieder bevor der Pico schlafen geht. Die Konfiguration zieht sie sich aus der config.py. Es gibt die folgenden Funktionen:

  • wificonnection.connect() --> Verbindet mit dem WLAN
  • wificonnection.disconnect() --> Trennt die Verbindung mit dem WLAN
  • wificonnection.status() --> Gibt den aktuellen Status der Verbindung aus (True / False)

wificonnection.py

# -*- coding: utf-8 -*-
"""Wifi Connection Module
Connecting Wifi, disconnecting Wifi and Wifi status
you'll need config.py to configure SSID, Password and CountryCode
* wificonnection.connect()
* wificonnection.disconnect()
* wificonnection.status()
"""

import config
import machine
from time import sleep
import network
import rp2

# wlan declearation
wlan = network.WLAN(network.STA_IF)
# led declaration
if config.ledstatus:
    led = machine.Pin('LED', machine.Pin.OUT)


def connect():
    # Setting Country
    rp2.country(config.wificountrycode)

    # activate wifi
    wlan.active(True)
    wlan.connect(config.wifissid, config.wifipassword)

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

    # Handle connection error
    if wlan.status() != 3:
        raise RuntimeError('network connection failed')
    else:
        # LED blinking
        if config.ledstatus:
            for i in range(wlan.status()):
                led.on()
                sleep(.1)
                led.off()
                sleep(.1)
        print('connected')
        status = wlan.ifconfig()
        print('ip = ' + status[0])


def disconnect():
    wlan.disconnect()
    wlan.active(False)
    wlan.deinit()
    print("Disconnected: " + str(wlan.status()))
    

def status():
    print("Connected: " + str(wlan.isconnected()))

bme680homiemqtt.py -- Hauptscript

Dieses Script holt sich die Konfigurationswerte aus der config.py und nutzt die bme680.py als Treiber Library für den Sensor. Dann passieren die folgenden Dinge:

  • Verbindung mit dem Wlan wificonnection.connect()
  • Verbindung zum MQTT Broker und Erstellung der Homie Convention Struktur
  • Auslesen der BME680 Sensor Werte
  • Senden von Temperatur und Luftfeuchtigkeit an den MQTT Broker
  • UsingLightSleep - False / True
  • False: Entweder eine Loop des Lesend des Sensors und verschicken der Daten an den MQTT Broker
  • True: Oder trennen von MQTT und WLAN und schlafen legen bis zum nächsten publizieren.

bme680homiemqtt.py

# -*- coding: utf-8 -*-
"""BME680 Homie MQTT
reads data (Temperature, Humidity, Pressure, Gas, Altitude) of BME680 sensor and
sends values to a MQTT Broker in Homie MQTT Convention format
You are able to set a value, when humidity alarm is fired.
"""

import config
import wificonnection
from umqtt.simple2 import MQTTClient
from time import sleep
from bme680 import *
from machine import I2C, Pin
import machine


#led declaration
if config.ledstatus:
    led = Pin('LED', Pin.OUT)
#degree symbol decleration
degreecels = '\u00B0' + "C"

# Functions
def publish(topic, payload):
    client.publish("homie/" + config.homieclientid + "/" + topic,
                   payload, retain=config.mqttretainmessage, qos=config.mqttqos)


def mqttconnect():
    global client
    client = MQTTClient(config.mqttclientid, config.mqttbroker, port=config.mqttport, user=config.mqttusername,
                            password=config.mqttpassword, keepalive=10, ssl=config.mqttssl,
                            ssl_params={})
    client.set_last_will("homie/" + config.homieclientid + "/$state",
                            "lost", retain=config.mqttretainmessage, qos=config.mqttqos)
    client.connect()
    # homie client config
    publish("$state", "init")
    publish("$homie", "4.0")
    publish("$name", config.homieclientname)
    publish("$nodes", config.homienodes)
    # homie node config
    publish(config.homienodes + "/$name", "BME680 Sensor")
    publish(config.homienodes + "/$properties", "temperature,humidity,humidityalarm,pressure,gas,altitude")
    publish(config.homienodes + "/temperature/$name", "Temperature")
    publish(config.homienodes + "/temperature/$unit", degreecels.encode('utf8'))
    publish(config.homienodes + "/temperature/$datatype", "float")
    publish(config.homienodes + "/temperature/$settable", "false")
    publish(config.homienodes + "/humidity/$name", "Humidity")
    publish(config.homienodes + "/humidity/$unit", "%")
    publish(config.homienodes + "/humidity/$datatype", "float")
    publish(config.homienodes + "/humidity/$settable", "false")
    publish(config.homienodes + "/humidityalarm/$name", "Humidity Alarm")
    publish(config.homienodes + "/humidityalarm/$datatype", "boolean")
    publish(config.homienodes + "/humidityalarm/$settable", "false")
    publish(config.homienodes + "/pressure/$name", "Pressure")
    publish(config.homienodes + "/pressure/$unit", "hPa")
    publish(config.homienodes + "/pressure/$datatype", "float")
    publish(config.homienodes + "/pressure/$settable", "false")
    publish(config.homienodes + "/gas/$name","Gas")
    publish(config.homienodes + "/gas/$unit","ohm")
    publish(config.homienodes + "/gas/$datatype","integer")
    publish(config.homienodes + "/gas/$settable","false")
    publish(config.homienodes + "/altitude/$name","Altitude")
    publish(config.homienodes + "/altitude/$unit","m")
    publish(config.homienodes + "/altitude/$datatype","float")
    publish(config.homienodes + "/altitude/$settable","false")
    # homie state ready
    publish("$state", "ready")


def sensorpublish():
    publish(config.homienodes + "/temperature", "{:.1f}".format(temperature))
    publish(config.homienodes + "/humidity", "{:.1f}".format(humidity))
    publish(config.homienodes + "/pressure", "{:.1f}".format(pressure))
    publish(config.homienodes + "/gas", "{:.1f}".format(gas))
    publish(config.homienodes + "/altitude", "{:.1f}".format(altitude))
    if humidity >= config.humidityalarm:
        publish(config.homienodes + "/humidityalarm", "true")
    else:
        publish(config.homienodes + "/humidityalarm", "false")
    

def bme680sensor():
    global temperature
    global humidity
    global pressure
    global gas
    global altitude
    # Initialize BME680 Sensor
    bme680 = BME680_I2C(I2C(0, sda=Pin(config.i2c_sda),scl=Pin(config.i2c_scl)))
    # set sealevel if it is conffigured
    if 'config.sealevelpressure' in locals():
        bme680.sea_level_pressure = config.sealevelpressure
    temperature = (bme680.temperature + config.temperature_offset)
    humidity = bme680.humidity
    pressure = bme680.pressure
    gas = bme680.gas
    altitude = bme680.altitude
 

# do the things
try:
    # connect to wifi
    wificonnection.connect()
    # connect to mqtt broker and initialize homie convention
    mqttconnect()
    while True:
        if config.ledstatus:
            led.value(True)
        bme680sensor()
        print('Temperature = ', temperature, degreecels)
        print('Humidty = ', humidity, '%')
        print('Pressure = ', pressure, 'hPa')
        print('Gas = ', gas, 'ohm')
        print('Altitude = ', altitude, 'm', '\n')       
        sensorpublish()
        sleep(.5)
        if config.usinglightsleep:
            publish("$state", "sleeping")
            sleep(.5)
            client.disconnect()
            sleep(.5)
            wificonnection.disconnect()
            print("done...")
            #sleep(.5)
            if config.ledstatus:
                led.value(False)
                machine.lightsleep((config.publishtime)*1000-13100)
            else:
                machine.lightsleep((config.publishtime)*1000-10600)
            break
        else:
            print("just a break for %s seconds" % config.publishtime)
            if config.ledstatus:
                led.value(False)
                sleep(config.publishtime-15)
            else:
                sleep(config.publishtime-10)
except RuntimeError as error:
    print(error.args[0])
    pass
except OSError as e:
    print(e.args[0])
    pass
machine.reset()

main.py -- Autostart

Möchte man, das das bme680homiemqtt.py Script autark auf dem Pico läuft, dann muss man entweder dieses als main.py umbenennen und auf den Pico übertragen, oder aber der bessere Weg, wir erstellen eine main.py und starten über diese unser bme680homiemqtt.py Script. Der Vorteil dieses Scripts hier, ist, das man mittels überbrücken von einem GPIO Pin (hier GPIO19) zu GND wieder in den programmierbaren Modus des Pico kommt:

main.py

# -*- coding: utf-8 -*-
"""Main Script
to prevent always blank flashing the pico
GPIO(19) is used to detect, if the system will break
instead of running the MQTT script.
Just use a jumper cable to connect GND and GPIO(19)
or use a switch on these two pins.
* on exit you'll see led 5 times flashing
* on entering the python script led will flash 1 time
"""


import machine
import config
from time import sleep

# declare EXIT Pin GPIO(19)
sw = machine.Pin(19,machine.Pin.IN,machine.Pin.PULL_UP)
# declare LED variable
if config.ledstatus:
    led = machine.Pin('LED', machine.Pin.OUT)

# Check if GPIO19 is activated
if  sw.value():
    if config.ledstatus:
        # LED blink on Startup
        led.value(True)
        sleep(.3)
        led.value(False)
    import bme680homiemqtt.py
else:
    blink = 5
    for i in range(blink):
        led.value(True)
        sleep(.1)
        led.value(False)
        sleep(.1)
    sys.exit()

Stromverbrauch

Wenn man den LightSleep Mode verwendet, verbraucht der Pico keinen für mich messbaren Strom. Beim Ausführen des Skriptes selbst liegen folgenden Maximal Werte an:

Raspberry Pi Pico W
Volt 5,13 V
Ampere 0,06 A
Watt 0,30 W
Einmalige Laufzeit des Skriptes in Sekunden Mit Status LED 13,1 s
Ohne Status LED 10,6 s

Quellen