Get BME280 readings in openHABian with MQTT
Bosch’s BME280 is a popular environmental sensor that can measure temperature, humidity and pressure. The purpose of this how-to is to explain how to configure a OpenHABian system running in a Paspberry Pi to get the sensor’s readings as [OpenHAB Items](Items | openHAB).
I got a general idea of what needed to be done from this discussion. I got help about the specific steps from Claude AI. My experience in Linux systems was useful. I thought it would be helpful to put it in straightforward how-to that anybody could follow.
The set-up used in this how-to is as follows:
- Raspberry Pi 4b, 4Gbytes
- Linux OpenHABian 6.12.25
- OpenHAB 5.0.2
- BME280 module board (Pimoroni’s)
Connect the BME280 to the Raspberry Pi
There are several tiny electronic boards that contain the sensor and allow direct connection to the Raspberry’s GPIO. It’s my understanding that most boards are compatible with either Adafruit’s or Pimoroni’s boards. These are the only two types that will be covered in this how-to. You will find which type you have in the process below.
The boards have at least four pins, labeled in different ways. You only need to connect four. You should find the specific connection mapping for your board. In my case, I connected it as follows:
| BME280 board | GPIO Pin | GPIO Function |
|---|---|---|
| VCC | 1 | 3v3 Power |
| GND | 9 | Ground |
| SCL | 5 | GPIO 3 (I2C1 SCL) |
| SDA | 3 | GPIO 2 (I2C1 SDA) |
You can use other GPIO pins for 3v3 power and ground. For example, I have seen schemas connecting GND to pin 6. In my case, that pin is taken by the power supply of the touch display.
Enable I2C
Start the Rasbperry configuration:
sudo raspi-config
Navigate to Interface Options > I2C and answer Yes to enable the I2C interface.
Run this to check if the BME280 board is detected:
sudo i2cdetect -y 1
You should see a number “76” or “77”, depending on the type of you board:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --#
“76” means that my board is Pimoroni-compatible. If you see “77”, yours is Adafruit-compatible.
Get readings with a Python script
The system’s default Python environment won’t let you install the modules needed to communicate with the card, so you need to create a virtual environment. Let’s create one in the $HOME directory, in a subdirectory named mypyenv. You may want to use a hidden directory (starting with a dot) like .mypyenv:
cd $HOME
python -m venv mypyenv
Now you need to install the modules. You have two options, install modules for Adafruit or Pimoroni. You can use either with any of the board types, but I suppose it makes more sense to use the one that matches your board. I’m going to show you examples for both, but you only need one of them.
Adafruit
Install the Python modules to communicate with an Adafruit BME280 board:
$HOME/mypyenv/bin/pip3 install adafruit-circuitpython-bme280
Write a script to check that you can get readings from the board. Let’s call it bme280_test.py and place it in the $HOME directory:
import time
import board
import adafruit_bme280.advanced as adafruit_bme280
# --- Initialize sensor ---
i2c = board.I2C()
bme = adafruit_bme280.Adafruit_BME280_I2C(i2c)
# --- Print readings ---
while True:
temperature = bme.temperature
humidity = bme.humidity
pressure = bme.pressure / 100.0
print(f"Temp: {temperature:.2f} °C | Humidity: {humidity:.2f} % | Pressure: {pressure:.2f} hPa")
time.sleep(2)
Run the script:
$HOME/mypyenv/bin/python3 $HOME/bme280_test.py
You should get something like:
Temp: 22.28 °C | Humidity: 45.26 % | Pressure: 10.18 hPa
Temp: 22.27 °C | Humidity: 45.07 % | Pressure: 10.18 hPa
Temp: 22.27 °C | Humidity: 45.04 % | Pressure: 10.18 hPa
I don’t know if all boards return the same units. If you don’t like them, you can convert them in the script. In the script above, the pressure reading is divided by 100 to convert Pascals to Hectopascals.
I actually get the following because my board is Pimoroni’s:
ValueError: No I2C device at address: 0x77
I just need to change line number 7 in the script to make it work:
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x76)
Pimoroni
Install the Python modules to communicate with a Pimoroni BME280 board:
$HOME/mypyenv/bin/pip3 install pimoroni-bme280
The bme280_test.py script should be as follows:
import time
from smbus2 import SMBus
from bme280 import BME280
# Initialize sensor
bus = SMBus(1)
bme = BME280(i2c_dev=bus)
# Print readings
while True:
temperature = bme.get_temperature()
humidity = bme.get_humidity()
pressure = bme.get_pressure() / 100.0
print(f"Temp: {temperature:.2f} °C | Humidity: {humidity:.2f} % | Pressure:
{pressure:.2f} hPa")
time.sleep(2)
I don’t have an Adafruit board, so I cannot test how to make Pimoroni’s modules work with such board.
The rest of this how-to will be based on Pimoroni’s modules. Change the imports and the sensor initialization if you want to use Adafruit’s.
Send the readings through the MQTT broker
The expected way to send these readings to OpenHAB is through a MQTT broker, so let’s do that. Run the following command to install Mosquitto:
sudo openhabian-config
Navigate to Optional Components > Mosquitto and select Continue. It will ask you for a MQTT-User and password. We will later need to write these in a file in plain text, so don’t use any of your secret passwords. Create a dedicated user and password for this purpose.
You may not need to do this the first time; but just in case, restart the Mosquitto service to apply the changes:
sudo systemctl restart mosquitto
You need to install the [Paho MQTT Client](client module — Eclipse paho-mqtt documentation) to be able to connect to the MQTT broker in Python:
$HOME/mypyenv/bin/pip3 install paho-mqtt
Let’s write the final Python script to read the BME280 sensor and publish the readings in the MQTT broker in intervals of time.
In the example below, all the configuration parameters are defined as variables at the beginning of the script. You don’t have to bother with the rest of the script. Here is where you have put the MQTT user and password you entered before. You may choose any MQTT topics and time interval you like.
You probably want to have this script running in the background all the time, so I recommend you to put it in a hidden directory to avoid accidental deletion. For the sake of simplicity of this tutorial, we will put the script in a file named bme280_mqtt.py in the $HOME directory:
import time
import signal
import sys
from smbus2 import SMBus
from bme280 import BME280
import paho.mqtt.client as mqtt
# --- Configuration ---
MQTT_HOST = "localhost"
MQTT_PORT = 1883
MQTT_USER = "YOUR_MQTT_USER"
MQTT_PASSWORD = "YOUR_MQTT_PASSWORD"
MQTT_TOPIC_TEMP = "bme280/temperature"
MQTT_TOPIC_HUM = "bme280/humidity"
MQTT_TOPIC_PRESS = "bme280/pressure"
INTERVAL = 10 # seconds
# --- Handle graceful termination
running = True
def handle_signal(sig, frame):
global running
running = False
signal.signal(signal.SIGTERM, handle_signal)
signal.signal(signal.SIGINT, handle_signal)
# --- Initialize sensor ---
bus = SMBus(1)
bme = BME280(i2c_dev=bus)
# --- Initialize MQTT client ---
client = mqtt.Client(
protocol=mqtt.MQTTv311,
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
)
client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
client.connect(MQTT_HOST, MQTT_PORT, 60)
client.loop_start()
# --- Publish loop ---
try:
while running:
temperature = bme.get_temperature()
humidity = bme.get_humidity()
pressure = bme.get_pressure() / 100.0
client.publish(MQTT_TOPIC_TEMP, f"{temperature:.2f}")
client.publish(MQTT_TOPIC_HUM, f"{humidity:.2f}")
client.publish(MQTT_TOPIC_PRESS, f"{pressure:.2f}")
time.sleep(INTERVAL)
# --- Termination
finally:
client.loop_stop()
client.disconnect()
bus.close()
Run the script:
$HOME/mypyenv/bin/python3 $HOME/bme280_mqtt.py
The script runs in an infinite loop until you interrupt it with CTRL+C. When you do, it will take a few seconds to close the connections before stopping.
Keep the script running and open another terminal window to test it. In the new terminal, run the following command to print the readings published in the MQTT topics:
mosquitto_sub -h localhost -t "bme280/#" -v -u YOUR_MQTT_USER -P YOUR_MQTT_PASSWORD
If you get lines like this every ten seconds, everything is working as expected:
bme280/temperature 21.39
bme280/humidity 46.16
bme280/pressure 10.25
Run the script in a systemd service
You probably want to have the script running in the background from the moment the system starts. For that, you need to create a systemd service. You need to put a new service file in the /etc/systemd/system. Let’s name it bme280mqtt.service:
[Unit]
Description=BME280 MQTT Publisher
After=mosquitto.service
[Service]
User=openhabian
WorkingDirectory=/home/openhabian
ExecStart=/home/openhabian/mypyenv/bin/python3 /home/openhabian/bme280_mqtt.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Here we cannot use environment variables like $HOME, so you need to make all the values explicit. This service file assumes the following:
-
The Python virtual environment is in
/home/openhabian/mypyenv. -
The script is in
/home/openhabian/bme280_mqtt.py.
Otherwise, change the values accordingly.
You need to let the system know about the new service:
sudo systemctl daemon-reload
Enable the service:
sudo systemctl enable bme280mqtt.service
And start it:
sudo systemctl start bme280mqtt.service
Make sure you are not running the script with the python3 command. You should still get the readings with the same mosquitto_sub command you used before. For the final test, restart the system:
sudo reboot
If you still get the readings with mosquitto_sub command without doing anything else, you have achieved the goal of this section and the trickiest part of this how-to.
Get BME280’s readings as Items in OpenHAB
The rest of this how-to concerts OpenHAB configuration. I’m going to assume you are experienced with it and there is plenty of documentation in the website, so I will be brief. I’m a newbie in this area, so it’s likely that something in the configuration below is wrong or not optimal. Let me know and I will fix it.
First you need to install the MQTT Binding add-on. With it, create a Thing with three Channels. This is an example of a working configuration for what we have done so far:
UID: mqtt:broker:debc5cb380
label: Local MQTT
thingTypeUID: mqtt:broker
configuration:
lwtQos: 0
publickeypin: true
clientid: a7b3289f-187f-455e-8179-eb81f5e79e4e
keepAlive: 60
hostnameValidated: true
birthRetain: true
secure: false
certificatepin: true
shutdownRetain: true
password: YOUR_MQTT_PASSWORD
protocol: TCP
qos: 0
reconnectTime: 60000
port: 1883
mqttVersion: V3
host: localhost
lwtRetain: true
enableDiscovery: true
username: YOUR_MQTT_USER
channels:
- id: BME280Temperature
channelTypeUID: mqtt:publishTrigger
label: Temperature
description: ""
configuration:
stateTopic: bme280/temperature
- id: BME280Humidity
channelTypeUID: mqtt:publishTrigger
label: Room humidity
description: ""
configuration:
stateTopic: bme280/humidity
- id: BME280Pressure
channelTypeUID: mqtt:publishTrigger
label: Room pressure
description: ""
configuration:
stateTopic: bme280/pressure
And finally, create an Item for each of the Channels. My pitfall here was to figure out that the type must be String, not Number. For example, for temperature:
label: Room temperature
type: String
icon: temperature
groupNames: []
tags:
- Measurement
- Temperature
That’s it. I hope this is useful. Let me know how I can improve this how-to, I appreciate your feedback.