Bme680 and mqtt

Greetings,
I am trying to get a bme680 sensor to publish its readings to my homeassistant instance, using mqtt.
After much grinding of teeth, and pulling at what little hair I have remaining, I’ve got the following code so far:

#!/usr/bin/env python3
"""
Run mqtt broker on localhost: sudo apt-get install mosquitto mosquitto-clients

Example run: python3 mqtt-all.py --broker 192.168.1.164 --topic enviro --username xxx --password xxxx
"""
import argparse
import time
import ssl
import bme680
import json
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish

try:
    from smbus2 import SMBus
except ImportError:
    from smbus import SMBus

# set up mqtt
DEFAULT_MQTT_BROKER_IP = "<-REMOVED FOR FORUM POST->"
DEFAULT_MQTT_BROKER_PORT = 1883
DEFAULT_MQTT_TOPIC = "envirobme680"
DEFAULT_READ_INTERVAL = 5
DEFAULT_TLS_MODE = False
DEFAULT_USERNAME = "<-REMOVED FOR FORUM POST->"
DEFAULT_PASSWORD = "<-REMOVED FOR FORUM POST->"

# mqtt callbacks
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("connected OK")
    else:
        print("Bad connection Returned code=", rc)

def on_publish(client, userdata, mid):
    print("mid: " + str(mid))

# Get CPU temperature to use for compensation
def get_cpu_temperature():
    process = Popen(
        ["vcgencmd", "measure_temp"], stdout=PIPE, universal_newlines=True
    )
    output, _error = process.communicate()
    return float(output[output.index("=") + 1:output.rindex("'")])

# Get Raspberry Pi serial number to use as ID
def get_serial_number():
    with open("/proc/cpuinfo", "r") as f:
        for line in f:
            if line[0:6] == "Serial":
                return line.split(":")[1].strip()

def main():
    parser = argparse.ArgumentParser(
        description="Publish enviroplus values over mqtt"
    )
    parser.add_argument(
        "--broker",
        default=DEFAULT_MQTT_BROKER_IP,
        type=str,
        help="mqtt broker IP",
    )
    parser.add_argument(
        "--port",
        default=DEFAULT_MQTT_BROKER_PORT,
        type=int,
        help="mqtt broker port",
    )
    parser.add_argument(
        "--topic", default=DEFAULT_MQTT_TOPIC, type=str, help="mqtt topic"
    )
    parser.add_argument(
        "--interval",
        default=DEFAULT_READ_INTERVAL,
        type=int,
        help="the read interval in seconds",
    )
    parser.add_argument(
        "--tls",
        default=DEFAULT_TLS_MODE,
        action='store_true',
        help="enable TLS"
    )
    parser.add_argument(
        "--username",
        default=DEFAULT_USERNAME,
        type=str,
        help="mqtt username"
    )
    parser.add_argument(
        "--password",
        default=DEFAULT_PASSWORD,
        type=str,
        help="mqtt password"
    )
    args = parser.parse_args()

    # Raspberry Pi ID
    device_serial_number = get_serial_number()
    device_id = "raspi-" + device_serial_number

    print(
        f"""mqtt-all.py - Reads Enviro plus data and sends over mqtt.

    broker: {args.broker}
    client_id: {device_id}
    port: {args.port}
    topic: {args.topic}
    tls: {args.tls}
    username: {args.username}
    password: {args.password}

    Press Ctrl+C to exit!

    """
    )

    mqtt_client = mqtt.Client(client_id=device_id)
    if args.username and args.password:
        mqtt_client.username_pw_set(args.username, args.password)
    mqtt_client.on_connect = on_connect
    mqtt_client.on_publish = on_publish

    if args.tls is True:
        mqtt_client.tls_set(tls_version=ssl.PROTOCOL_TLSv1_2)

    if args.username is not None:
        mqtt_client.username_pw_set(args.username, password=args.password)

    mqtt_client.connect(args.broker, port=args.port)

    bus = SMBus(1)

# set up bme680 instance

    sensor = bme680.BME680()

    sensor.set_humidity_oversample(bme680.OS_2X)
    sensor.set_pressure_oversample(bme680.OS_4X)
    sensor.set_temperature_oversample(bme680.OS_8X)
    sensor.set_filter(bme680.FILTER_SIZE_3)

    sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
    sensor.set_gas_heater_temperature(320)
    sensor.set_gas_heater_duration(150)
    sensor.select_gas_heater_profile(0)
    
    # Main loop to read data, display, and send over mqtt
    mqtt_client.loop_start()
    
    while True:
        try:  
            if sensor.get_sensor_data():
                output = "T:{0:.1f} C, P:{1:.1f} hPa, H:{2:.1f} %".format(sensor.data.temperature / 2.33, sensor.data.pressure, sensor.data.humidity)
            if sensor.data.heat_stable:
                print(" {0}, Gas:{1:.1f} Ohms".format(output, sensor.data.gas_resistance))

            else:
                print(output)
                mqtt_client.publish(args.topic, json.dumps(output))
                mqtt_client.publish(args.topic)
                display_status(disp, args.broker)
                time.sleep(args.interval)
        except Exception as e:
            print(e)
        time.sleep(1)

if __name__ == "__main__":
    main()

Which does kind of do something interesting:

>>> %Run Enviro-mqtt-example-bme680-modded-v1.1.py
mqtt-all.py - Reads Enviro plus data and sends over mqtt.

    broker: "<-REMOVED FOR FORUM POST->"
    client_id: "<-REMOVED FOR FORUM POST->"
    port: 1883
    topic: envirobme680
    tls: False
    username: "<-REMOVED FOR FORUM POST->"
    password: "<-REMOVED FOR FORUM POST->"

    Press Ctrl+C to exit!

    
connected OK
local variable 'output' referenced before assignment
T:10.4 C, P:1021.6 hPa, H:59.2 %
mid: 1
mid: 2
 T:10.4 C, P:1021.6 hPa, H:59.2 %, Gas:28219.7 Ohms
 T:10.4 C, P:1021.6 hPa, H:59.1 %, Gas:27636.2 Ohms
 T:10.5 C, P:1021.6 hPa, H:59.1 %, Gas:31961.8 Ohms
 T:10.5 C, P:1021.6 hPa, H:59.0 %, Gas:35656.6 Ohms
 T:10.5 C, P:1021.6 hPa, H:59.0 %, Gas:39136.8 Ohms

The readings are only received at the mqtt broker once. here is a snippet of whats being received at the other end:

Listen to a topic
 
Listening to
envirobme680
 
Message 1 received on envirobme680 at 12:04:
QoS: 0 - Retain: false
Message 0 received on envirobme680 at 12:04:
"T:10.8 C, P:1021.5 hPa, H:57.5 %"
QoS: 0 - Retain: false

No further messages are received. Only the first couple.

Is anyone able to assist me in getting this working, please? I’m a bit outside my comfort zone, and still very much a pythonic newbie!

Thanks for any help , I am genuinely very grateful.

Shameless humble request for any possible assistance here, please. I’ve not managed to progress.
@hel @gadgetoid forgiveness please for the tagging.

Pretty sure I ran into a similar issue with at some point, with only the first message being received by the broker. I seem to remember that for me this was caused by authentication issues - Home Assistant’s MQTT plugin doesn’t support anonymous logins any more (though some older tutorials say that it does!) so I needed to set up a username and password in the broker configuration (I’m using the Mosquitto addon) and then include them in the MQTT integration and the Python script running on the Pi.

This is the script I’m currently running to post data from Weather HAT into HA - you might find it to be a bit simpler than the Enviro+ example? You’d need to swap the stuff that reads the sensors on Weather HAT for commands for reading from your BME680.

The dict1 etc business means the script sets up the sensors at the Home Assistant end for you - you don’t need to set them up in configuration.yaml, they should just appear (hopefully!)

1 Like

I might be reading the code wrong - I’m not a Pythonite - but it looks like the mqtt publish only gets called if sensor.data.heat_stable is false (i.e. it’s on the other side of the else)?

Ooh yes, missed that! It also looks like the indents are a little messed up when compared to the BME680 example - the if sensor.data.heat_stable: and the else: should be indented so they’re nested within the previous if statement.

if sensor.get_sensor_data():
    output = "T:{0:.1f} C, P:{1:.1f} hPa, H:{2:.1f} %".format(
        sensor.data.temperature / 2.33, 
        sensor.data.pressure, 
        sensor.data.humidity)
    
    if sensor.data.heat_stable:
        print(" {0}, Gas:{1:.1f} Ohms".format(
            output, 
            sensor.data.gas_resistance))    
    else:
        print(output)

# and then do your MQTT stuff               
mqtt_client.publish(args.topic, json.dumps(output))
mqtt_client.publish(args.topic)
display_status(disp, args.broker)
time.sleep(args.interval)

Python indents, they can always spoil your day :D

And this is why I’m not a Pythonite :-)

Any language that uses whitespace semantically needs to be burned with fire.

2 Likes

Wow, thank you both. I’m knee deep in stuff at the moment. But, will give this a try just as soon as I can.

I see what you mean about the indenting and the heater status.

Also, thanks for the gist link, will have a dive into that also.

Thanks again, eternally grateful. Arr, yarr, etc.

1 Like