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