improved logging with timestamps (instead of print)
[EVA-2020-02-2.git] / examples / luftdaten.py
... / ...
CommitLineData
1#!/usr/bin/env python
2
3import requests
4import ST7735
5from bme280 import BME280
6from pms5003 import PMS5003, ReadTimeoutError
7from subprocess import PIPE, Popen, check_output
8from PIL import Image, ImageDraw, ImageFont
9
10try:
11 from smbus2 import SMBus
12except ImportError:
13 from smbus import SMBus
14
15import logging
16
17logging.basicConfig(
18 format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
19 level=logging.INFO,
20 datefmt='%Y-%m-%d %H:%M:%S')
21
22logging.info("""luftdaten.py - Reads temperature, pressure, humidity,
23PM2.5, and PM10 from Enviro plus and sends data to Luftdaten,
24the citizen science air quality project.
25
26Note: you'll need to register with Luftdaten at:
27https://meine.luftdaten.info/ and enter your Raspberry Pi
28serial number that's displayed on the Enviro plus LCD along
29with the other details before the data appears on the
30Luftdaten map.
31
32Press Ctrl+C to exit!
33
34""")
35
36bus = SMBus(1)
37
38# Create BME280 instance
39bme280 = BME280(i2c_dev=bus)
40
41# Create LCD instance
42disp = ST7735.ST7735(
43 port=0,
44 cs=1,
45 dc=9,
46 backlight=12,
47 rotation=270,
48 spi_speed_hz=10000000
49)
50
51# Initialize display
52disp.begin()
53
54# Create PMS5003 instance
55pms5003 = PMS5003()
56
57
58# Read values from BME280 and PMS5003 and return as dict
59def read_values():
60 values = {}
61 cpu_temp = get_cpu_temperature()
62 raw_temp = bme280.get_temperature()
63 comp_temp = raw_temp - ((cpu_temp - raw_temp) / comp_factor)
64 values["temperature"] = "{:.2f}".format(comp_temp)
65 values["pressure"] = "{:.2f}".format(bme280.get_pressure() * 100)
66 values["humidity"] = "{:.2f}".format(bme280.get_humidity())
67 try:
68 pm_values = pms5003.read()
69 values["P2"] = str(pm_values.pm_ug_per_m3(2.5))
70 values["P1"] = str(pm_values.pm_ug_per_m3(10))
71 except ReadTimeoutError:
72 pms5003.reset()
73 pm_values = pms5003.read()
74 values["P2"] = str(pm_values.pm_ug_per_m3(2.5))
75 values["P1"] = str(pm_values.pm_ug_per_m3(10))
76 return values
77
78
79# Get CPU temperature to use for compensation
80def get_cpu_temperature():
81 process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
82 output, _error = process.communicate()
83 return float(output[output.index('=') + 1:output.rindex("'")])
84
85
86# Get Raspberry Pi serial number to use as ID
87def get_serial_number():
88 with open('/proc/cpuinfo', 'r') as f:
89 for line in f:
90 if line[0:6] == 'Serial':
91 return line.split(":")[1].strip()
92
93
94# Check for Wi-Fi connection
95def check_wifi():
96 if check_output(['hostname', '-I']):
97 return True
98 else:
99 return False
100
101
102# Display Raspberry Pi serial and Wi-Fi status on LCD
103def display_status():
104 wifi_status = "connected" if check_wifi() else "disconnected"
105 text_colour = (255, 255, 255)
106 back_colour = (0, 170, 170) if check_wifi() else (85, 15, 15)
107 id = get_serial_number()
108 message = "{}\nWi-Fi: {}".format(id, wifi_status)
109 img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
110 draw = ImageDraw.Draw(img)
111 size_x, size_y = draw.textsize(message, font)
112 x = (WIDTH - size_x) / 2
113 y = (HEIGHT / 2) - (size_y / 2)
114 draw.rectangle((0, 0, 160, 80), back_colour)
115 draw.text((x, y), message, font=font, fill=text_colour)
116 disp.display(img)
117
118
119def send_to_luftdaten(values, id):
120 pm_values = dict(i for i in values.items() if i[0].startswith("P"))
121 temp_values = dict(i for i in values.items() if not i[0].startswith("P"))
122
123 resp_1 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/",
124 json={
125 "software_version": "enviro-plus 0.0.1",
126 "sensordatavalues": [{"value_type": key, "value": val} for
127 key, val in pm_values.items()]
128 },
129 headers={
130 "X-PIN": "1",
131 "X-Sensor": id,
132 "Content-Type": "application/json",
133 "cache-control": "no-cache"
134 }
135 )
136
137 resp_2 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/",
138 json={
139 "software_version": "enviro-plus 0.0.1",
140 "sensordatavalues": [{"value_type": key, "value": val} for
141 key, val in temp_values.items()]
142 },
143 headers={
144 "X-PIN": "11",
145 "X-Sensor": id,
146 "Content-Type": "application/json",
147 "cache-control": "no-cache"
148 }
149 )
150
151 if resp_1.ok and resp_2.ok:
152 return True
153 else:
154 return False
155
156
157# Compensation factor for temperature
158comp_factor = 1.2
159
160# Raspberry Pi ID to send to Luftdaten
161id = "raspi-" + get_serial_number()
162
163# Width and height to calculate text position
164WIDTH = disp.width
165HEIGHT = disp.height
166
167# Text settings
168font_size = 16
169font = ImageFont.truetype("fonts/Asap/Asap-Bold.ttf", font_size)
170
171# Display Raspberry Pi serial and Wi-Fi status
172logging.info("Raspberry Pi serial: {}".format(get_serial_number()))
173logging.info("Wi-Fi: {}\n".format("connected" if check_wifi() else "disconnected"))
174
175# Main loop to read data, display, and send to Luftdaten
176while True:
177 try:
178 values = read_values()
179 logging.info(values)
180 resp = send_to_luftdaten(values, id)
181 logging.info("Response: {}\n".format("ok" if resp else "failed"))
182 display_status()
183 except Exception as e:
184 logging.info(e)