Remove .travis.yml
[EVA-2020-02-2.git] / examples / all-in-one.py
... / ...
CommitLineData
1#!/usr/bin/env python3
2
3import time
4import colorsys
5import sys
6import ST7735
7try:
8 # Transitional fix for breaking change in LTR559
9 from ltr559 import LTR559
10 ltr559 = LTR559()
11except ImportError:
12 import ltr559
13
14from bme280 import BME280
15from pms5003 import PMS5003, ReadTimeoutError as pmsReadTimeoutError
16from enviroplus import gas
17from subprocess import PIPE, Popen
18from PIL import Image
19from PIL import ImageDraw
20from PIL import ImageFont
21from fonts.ttf import RobotoMedium as UserFont
22import logging
23
24logging.basicConfig(
25 format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
26 level=logging.INFO,
27 datefmt='%Y-%m-%d %H:%M:%S')
28
29logging.info("""all-in-one.py - Displays readings from all of Enviro plus' sensors
30
31Press Ctrl+C to exit!
32
33""")
34
35# BME280 temperature/pressure/humidity sensor
36bme280 = BME280()
37
38# PMS5003 particulate sensor
39pms5003 = PMS5003()
40
41# Create ST7735 LCD display class
42st7735 = 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
52st7735.begin()
53
54WIDTH = st7735.width
55HEIGHT = st7735.height
56
57# Set up canvas and font
58img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
59draw = ImageDraw.Draw(img)
60font_size = 20
61font = ImageFont.truetype(UserFont, font_size)
62
63message = ""
64
65# The position of the top bar
66top_pos = 25
67
68
69# Displays data and text on the 0.96" LCD
70def 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
96def 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
104factor = 2.25
105
106cpu_temps = [get_cpu_temperature()] * 5
107
108delay = 0.5 # Debounce the proximity tap
109mode = 0 # The starting mode
110last_page = 0
111light = 1
112
113# Create a values dict to store the data
114variables = ["temperature",
115 "pressure",
116 "humidity",
117 "light",
118 "oxidised",
119 "reduced",
120 "nh3",
121 "pm1",
122 "pm25",
123 "pm10"]
124
125values = {}
126
127for v in variables:
128 values[v] = [1] * WIDTH
129
130# The main loop
131try:
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
229except KeyboardInterrupt:
230 sys.exit(0)