Skip to content

Adds mqtt example #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 18, 2020
212 changes: 212 additions & 0 deletions examples/mqtt-all.py
Original file line number Diff line number Diff line change
@@ -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()
1 change: 1 addition & 0 deletions library/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ install_requires =
astral
pytz
sounddevice
paho-mqtt

[flake8]
exclude =
Expand Down