Adding all in one weather and light display example
[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
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
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)
60path = os.path.dirname(os.path.realpath(__file__))
61font = ImageFont.truetype(path + "/fonts/Asap/Asap-Bold.ttf", 20)
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 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
97def 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
105factor = 0.8
106
107cpu_temps = [get_cpu_temperature()] * 5
108
109delay = 0.5 # Debounce the proximity tap
110mode = 0 # The starting mode
111last_page = 0
112light = 1
113
114# Create a values dict to store the data
115variables = ["temperature",
116 "pressure",
117 "humidity",
118 "light",
119 "oxidised",
120 "reduced",
121 "nh3",
122 "pm1",
123 "pm25",
124 "pm10"]
125
126values = {}
127
128for v in variables:
129 values[v] = [1] * WIDTH
130
131# The main loop
132try:
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
230except KeyboardInterrupt:
231 sys.exit(0)