Merge branch 'master' of github.com:pimoroni/enviroplus-python
[EVA-2020-02-2.git] / examples / all-in-one.py
... / ...
CommitLineData
1#!/usr/bin/env python
2
3import time
4import colorsys
5import os
6import sys
7import ST7735
8import ltr559
9
10from bme280 import BME280
11from pms5003 import PMS5003, ReadTimeoutError as pmsReadTimeoutError
12from enviroplus import gas
13from subprocess import PIPE, Popen
14from PIL import Image
15from PIL import ImageDraw
16from PIL import ImageFont
17import logging
18
19logging.basicConfig(
20 format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
21 level=logging.INFO,
22 datefmt='%Y-%m-%d %H:%M:%S')
23
24logging.info("""all-in-one.py - Displays readings from all of Enviro plus' sensors
25
26Press Ctrl+C to exit!
27
28""")
29
30# BME280 temperature/pressure/humidity sensor
31bme280 = BME280()
32
33# PMS5003 particulate sensor
34pms5003 = PMS5003()
35
36# Create ST7735 LCD display class
37st7735 = ST7735.ST7735(
38 port=0,
39 cs=1,
40 dc=9,
41 backlight=12,
42 rotation=270,
43 spi_speed_hz=10000000
44)
45
46# Initialize display
47st7735.begin()
48
49WIDTH = st7735.width
50HEIGHT = st7735.height
51
52# Set up canvas and font
53img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
54draw = ImageDraw.Draw(img)
55path = os.path.dirname(os.path.realpath(__file__))
56font = ImageFont.truetype(path + "/fonts/Asap/Asap-Bold.ttf", 20)
57
58message = ""
59
60# The position of the top bar
61top_pos = 25
62
63
64# Displays data and text on the 0.96" LCD
65def display_text(variable, data, unit):
66 # Maintain length of list
67 values[variable] = values[variable][1:] + [data]
68 # Scale the values for the variable between 0 and 1
69 colours = [(v - min(values[variable]) + 1) / (max(values[variable])
70 - min(values[variable]) + 1) for v in values[variable]]
71 # Format the variable name and value
72 message = "{}: {:.1f} {}".format(variable[:4], data, unit)
73 logging.info(message)
74 draw.rectangle((0, 0, WIDTH, HEIGHT), (255, 255, 255))
75 for i in range(len(colours)):
76 # Convert the values to colours from red to blue
77 colour = (1.0 - colours[i]) * 0.6
78 r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour,
79 1.0, 1.0)]
80 # Draw a 1-pixel wide rectangle of colour
81 draw.rectangle((i, top_pos, i+1, HEIGHT), (r, g, b))
82 # Draw a line graph in black
83 line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos)))\
84 + top_pos
85 draw.rectangle((i, line_y, i+1, line_y+1), (0, 0, 0))
86 # Write the text at the top in black
87 draw.text((0, 0), message, font=font, fill=(0, 0, 0))
88 st7735.display(img)
89
90
91# Get the temperature of the CPU for compensation
92def get_cpu_temperature():
93 process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
94 output, _error = process.communicate()
95 return float(output[output.index('=') + 1:output.rindex("'")])
96
97
98# Tuning factor for compensation. Decrease this number to adjust the
99# temperature down, and increase to adjust up
100factor = 0.8
101
102cpu_temps = [get_cpu_temperature()] * 5
103
104delay = 0.5 # Debounce the proximity tap
105mode = 0 # The starting mode
106last_page = 0
107light = 1
108
109# Create a values dict to store the data
110variables = ["temperature",
111 "pressure",
112 "humidity",
113 "light",
114 "oxidised",
115 "reduced",
116 "nh3",
117 "pm1",
118 "pm25",
119 "pm10"]
120
121values = {}
122
123for v in variables:
124 values[v] = [1] * WIDTH
125
126# The main loop
127try:
128 while True:
129 proximity = ltr559.get_proximity()
130
131 # If the proximity crosses the threshold, toggle the mode
132 if proximity > 1500 and time.time() - last_page > delay:
133 mode += 1
134 mode %= len(variables)
135 last_page = time.time()
136
137 # One mode for each variable
138 if mode == 0:
139 # variable = "temperature"
140 unit = "C"
141 cpu_temp = get_cpu_temperature()
142 # Smooth out with some averaging to decrease jitter
143 cpu_temps = cpu_temps[1:] + [cpu_temp]
144 avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
145 raw_temp = bme280.get_temperature()
146 data = raw_temp - ((avg_cpu_temp - raw_temp) / factor)
147 display_text(variables[mode], data, unit)
148
149 if mode == 1:
150 # variable = "pressure"
151 unit = "hPa"
152 data = bme280.get_pressure()
153 display_text(variables[mode], data, unit)
154
155 if mode == 2:
156 # variable = "humidity"
157 unit = "%"
158 data = bme280.get_humidity()
159 display_text(variables[mode], data, unit)
160
161 if mode == 3:
162 # variable = "light"
163 unit = "Lux"
164 if proximity < 10:
165 data = ltr559.get_lux()
166 else:
167 data = 1
168 display_text(variables[mode], data, unit)
169
170 if mode == 4:
171 # variable = "oxidised"
172 unit = "kO"
173 data = gas.read_all()
174 data = data.oxidising / 1000
175 display_text(variables[mode], data, unit)
176
177 if mode == 5:
178 # variable = "reduced"
179 unit = "kO"
180 data = gas.read_all()
181 data = data.reducing / 1000
182 display_text(variables[mode], data, unit)
183
184 if mode == 6:
185 # variable = "nh3"
186 unit = "kO"
187 data = gas.read_all()
188 data = data.nh3 / 1000
189 display_text(variables[mode], data, unit)
190
191 if mode == 7:
192 # variable = "pm1"
193 unit = "ug/m3"
194 try:
195 data = pms5003.read()
196 except pmsReadTimeoutError:
197 logging.warn("Failed to read PMS5003")
198 else:
199 data = data.pm_ug_per_m3(1.0)
200 display_text(variables[mode], data, unit)
201
202 if mode == 8:
203 # variable = "pm25"
204 unit = "ug/m3"
205 try:
206 data = pms5003.read()
207 except pmsReadTimeoutError:
208 logging.warn("Failed to read PMS5003")
209 else:
210 data = data.pm_ug_per_m3(2.5)
211 display_text(variables[mode], data, unit)
212
213 if mode == 9:
214 # variable = "pm10"
215 unit = "ug/m3"
216 try:
217 data = pms5003.read()
218 except pmsReadTimeoutError:
219 logging.warn("Failed to read PMS5003")
220 else:
221 data = data.pm_ug_per_m3(10)
222 display_text(variables[mode], data, unit)
223
224# Exit cleanly
225except KeyboardInterrupt:
226 sys.exit(0)