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