From: Robin Date: Thu, 18 Jun 2020 08:35:06 +0000 (+0100) Subject: Adds mqtt example (#68) X-Git-Url: https://zdv2.bktei.com/gitweb/EVA-2020-02-2.git/commitdiff_plain/f5335ba6681d5268d306ab438571bb369d682c29 Adds mqtt example (#68) Adds mqtt example by @robmarkcole - see also: https://github.com/robmarkcole/rpi-enviro-mqtt --- diff --git a/examples/mqtt-all.py b/examples/mqtt-all.py new file mode 100755 index 0000000..26f46bb --- /dev/null +++ b/examples/mqtt-all.py @@ -0,0 +1,212 @@ +""" +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 +""" +#!/usr/bin/env python3 + +import argparse +import ST7735 +import time +from bme280 import BME280 +from pms5003 import PMS5003, ReadTimeoutError +from enviroplus import gas + +try: + # Transitional fix for breaking change in LTR559 + from ltr559 import LTR559 + + ltr559 = LTR559() +except ImportError: + import ltr559 + +from subprocess import PIPE, Popen, check_output +from PIL import Image, ImageDraw, ImageFont +from fonts.ttf import RobotoMedium as UserFont +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 + + +DEFAULT_MQTT_BROKER_IP = "localhost" +DEFAULT_MQTT_BROKER_PORT = 1883 +DEFAULT_MQTT_TOPIC = "enviroplus" + +# mqtt callbacks +def on_connect(client, userdata, flags, rc): + print(f"CONNACK received with code {rc}") + if rc == 0: + print("connected OK") + else: + print("Bad connection Returned code=", rc) + + +def on_publish(client, userdata, mid): + print("mid: " + str(mid)) + + +# Read values from BME280 and PMS5003 and return as dict +def read_values(bme280, pms5003): + # Compensation factor for temperature + comp_factor = 2.25 + + values = {} + cpu_temp = get_cpu_temperature() + raw_temp = bme280.get_temperature() # float + comp_temp = raw_temp - ((cpu_temp - raw_temp) / comp_factor) + values["temperature"] = int(comp_temp) + values["pressure"] = round( + int(bme280.get_pressure() * 100), -1 + ) # round to nearest 10 + values["humidity"] = int(bme280.get_humidity()) + try: + pm_values = pms5003.read() # int + values["pm1"] = pm_values.pm_ug_per_m3(1) + values["pm25"] = pm_values.pm_ug_per_m3(2.5) + values["pm10"] = pm_values.pm_ug_per_m3(10) + except ReadTimeoutError: + pms5003.reset() + pm_values = pms5003.read() + values["pm1"] = pm_values.pm_ug_per_m3(1) + values["pm25"] = pm_values.pm_ug_per_m3(2.5) + values["pm10"] = pm_values.pm_ug_per_m3(10) + data = gas.read_all() + values["oxidised"] = int(data.oxidising / 1000) + values["reduced"] = int(data.reducing / 1000) + values["nh3"] = int(data.nh3 / 1000) + values["lux"] = int(ltr559.get_lux()) + return values + + +# 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() + + +# Check for Wi-Fi connection +def check_wifi(): + if check_output(["hostname", "-I"]): + return True + else: + return False + + +# Display Raspberry Pi serial and Wi-Fi status on LCD +def display_status(disp, mqtt_broker): + # Width and height to calculate text position + WIDTH = disp.width + HEIGHT = disp.height + # Text settings + font_size = 16 + font = ImageFont.truetype(UserFont, font_size) + + wifi_status = "connected" if check_wifi() else "disconnected" + text_colour = (255, 255, 255) + back_colour = (0, 170, 170) if check_wifi() else (85, 15, 15) + id = get_serial_number() + message = "{}\nWi-Fi: {}\nmqtt-broker: {}".format(id, wifi_status, mqtt_broker) + img = Image.new("RGB", (WIDTH, HEIGHT), color=(0, 0, 0)) + draw = ImageDraw.Draw(img) + size_x, size_y = draw.textsize(message, font) + x = (WIDTH - size_x) / 2 + y = (HEIGHT / 2) - (size_y / 2) + draw.rectangle((0, 0, 160, 80), back_colour) + draw.text((x, y), message, font=font, fill=text_colour) + disp.display(img) + + +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" + ) + args = parser.parse_args() + + print( + """mqtt-all.py - Reads temperature, pressure, humidity, + PM2.5, and PM10 from Enviro plus and sends data over mqtt. + + broker: {} + port: {} + topic: {} + + Press Ctrl+C to exit! + + """.format( + args.broker, args.port, args.topic + ) + ) + + mqtt_client = mqtt.Client() + mqtt_client.on_connect = on_connect + mqtt_client.on_publish = on_publish + mqtt_client.connect(args.broker, port=args.port) + + bus = SMBus(1) + + # Create BME280 instance + bme280 = BME280(i2c_dev=bus) + + # Create LCD instance + disp = ST7735.ST7735( + port=0, cs=1, dc=9, backlight=12, rotation=270, spi_speed_hz=10000000 + ) + + # Initialize display + disp.begin() + + # Create PMS5003 instance + pms5003 = PMS5003() + + # Raspberry Pi ID + device_serial_number = get_serial_number() + id = "raspi-" + device_serial_number + + # Display Raspberry Pi serial and Wi-Fi status + print("Raspberry Pi serial: {}".format(get_serial_number())) + print("Wi-Fi: {}\n".format("connected" if check_wifi() else "disconnected")) + print("MQTT broker IP: {}".format(args.broker)) + + time_since_update = 0 + update_time = time.time() + + # Main loop to read data, display, and send over mqtt + mqtt_client.loop_start() + while True: + try: + time_since_update = time.time() - update_time + values = read_values(bme280, pms5003) + values["serial"] = device_serial_number + print(values) + mqtt_client.publish(args.topic, json.dumps(values)) + if time_since_update > 145: + update_time = time.time() + display_status(disp, args.broker) + except Exception as e: + print(e) + + +if __name__ == "__main__": + main() diff --git a/library/setup.cfg b/library/setup.cfg index d2909c1..c59250c 100644 --- a/library/setup.cfg +++ b/library/setup.cfg @@ -38,6 +38,7 @@ install_requires = astral pytz sounddevice + paho-mqtt [flake8] exclude =