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