Adding all in one weather and light display example
[EVA-2020-02-2.git] / examples / all-in-one.py
1 #!/usr/bin/env python
2
3 import time
4 import colorsys
5 import os
6 import sys
7 import ST7735
8 try:
9 # Transitional fix for breaking change in LTR559
10 from ltr559 import LTR559
11 ltr559 = LTR559()
12 except ImportError:
13 import ltr559
14
15 from bme280 import BME280
16 from pms5003 import PMS5003, ReadTimeoutError as pmsReadTimeoutError
17 from enviroplus import gas
18 from subprocess import PIPE, Popen
19 from PIL import Image
20 from PIL import ImageDraw
21 from PIL import ImageFont
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 path = os.path.dirname(os.path.realpath(__file__))
61 font = ImageFont.truetype(path + "/fonts/Asap/Asap-Bold.ttf", 20)
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 colours = [(v - min(values[variable]) + 1) / (max(values[variable])
75 - min(values[variable]) + 1) for v in values[variable]]
76 # Format the variable name and value
77 message = "{}: {:.1f} {}".format(variable[:4], data, unit)
78 logging.info(message)
79 draw.rectangle((0, 0, WIDTH, HEIGHT), (255, 255, 255))
80 for i in range(len(colours)):
81 # Convert the values to colours from red to blue
82 colour = (1.0 - colours[i]) * 0.6
83 r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour,
84 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)))\
89 + top_pos
90 draw.rectangle((i, line_y, i+1, line_y+1), (0, 0, 0))
91 # Write the text at the top in black
92 draw.text((0, 0), message, font=font, fill=(0, 0, 0))
93 st7735.display(img)
94
95
96 # Get the temperature of the CPU for compensation
97 def get_cpu_temperature():
98 process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
99 output, _error = process.communicate()
100 return float(output[output.index('=') + 1:output.rindex("'")])
101
102
103 # Tuning factor for compensation. Decrease this number to adjust the
104 # temperature down, and increase to adjust up
105 factor = 0.8
106
107 cpu_temps = [get_cpu_temperature()] * 5
108
109 delay = 0.5 # Debounce the proximity tap
110 mode = 0 # The starting mode
111 last_page = 0
112 light = 1
113
114 # Create a values dict to store the data
115 variables = ["temperature",
116 "pressure",
117 "humidity",
118 "light",
119 "oxidised",
120 "reduced",
121 "nh3",
122 "pm1",
123 "pm25",
124 "pm10"]
125
126 values = {}
127
128 for v in variables:
129 values[v] = [1] * WIDTH
130
131 # The main loop
132 try:
133 while True:
134 proximity = ltr559.get_proximity()
135
136 # If the proximity crosses the threshold, toggle the mode
137 if proximity > 1500 and time.time() - last_page > delay:
138 mode += 1
139 mode %= len(variables)
140 last_page = time.time()
141
142 # One mode for each variable
143 if mode == 0:
144 # variable = "temperature"
145 unit = "C"
146 cpu_temp = get_cpu_temperature()
147 # Smooth out with some averaging to decrease jitter
148 cpu_temps = cpu_temps[1:] + [cpu_temp]
149 avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
150 raw_temp = bme280.get_temperature()
151 data = raw_temp - ((avg_cpu_temp - raw_temp) / factor)
152 display_text(variables[mode], data, unit)
153
154 if mode == 1:
155 # variable = "pressure"
156 unit = "hPa"
157 data = bme280.get_pressure()
158 display_text(variables[mode], data, unit)
159
160 if mode == 2:
161 # variable = "humidity"
162 unit = "%"
163 data = bme280.get_humidity()
164 display_text(variables[mode], data, unit)
165
166 if mode == 3:
167 # variable = "light"
168 unit = "Lux"
169 if proximity < 10:
170 data = ltr559.get_lux()
171 else:
172 data = 1
173 display_text(variables[mode], data, unit)
174
175 if mode == 4:
176 # variable = "oxidised"
177 unit = "kO"
178 data = gas.read_all()
179 data = data.oxidising / 1000
180 display_text(variables[mode], data, unit)
181
182 if mode == 5:
183 # variable = "reduced"
184 unit = "kO"
185 data = gas.read_all()
186 data = data.reducing / 1000
187 display_text(variables[mode], data, unit)
188
189 if mode == 6:
190 # variable = "nh3"
191 unit = "kO"
192 data = gas.read_all()
193 data = data.nh3 / 1000
194 display_text(variables[mode], data, unit)
195
196 if mode == 7:
197 # variable = "pm1"
198 unit = "ug/m3"
199 try:
200 data = pms5003.read()
201 except pmsReadTimeoutError:
202 logging.warn("Failed to read PMS5003")
203 else:
204 data = float(data.pm_ug_per_m3(1.0))
205 display_text(variables[mode], data, unit)
206
207 if mode == 8:
208 # variable = "pm25"
209 unit = "ug/m3"
210 try:
211 data = pms5003.read()
212 except pmsReadTimeoutError:
213 logging.warn("Failed to read PMS5003")
214 else:
215 data = float(data.pm_ug_per_m3(2.5))
216 display_text(variables[mode], data, unit)
217
218 if mode == 9:
219 # variable = "pm10"
220 unit = "ug/m3"
221 try:
222 data = pms5003.read()
223 except pmsReadTimeoutError:
224 logging.warn("Failed to read PMS5003")
225 else:
226 data = float(data.pm_ug_per_m3(10))
227 display_text(variables[mode], data, unit)
228
229 # Exit cleanly
230 except KeyboardInterrupt:
231 sys.exit(0)