Raspberry Pi Pico W BME680 Raumklima Sensor
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
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 WLANwificonnection.disconnect()
--> Trennt die Verbindung mit dem WLANwificonnection.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:
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 |