Skip to content

Commit 527495f

Browse files
authored
Merge pull request #39 from Kostadin/combined_mode_example
Added a new combined mode example
2 parents bca0449 + 71dc296 commit 527495f

File tree

1 file changed

+345
-0
lines changed

1 file changed

+345
-0
lines changed

examples/combined.py

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
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+
smallfont = ImageFont.truetype(path + "/fonts/Asap/Asap-Bold.ttf", 10)
63+
x_offset = 2
64+
y_offset = 2
65+
66+
message = ""
67+
68+
# The position of the top bar
69+
top_pos = 25
70+
71+
# Create a values dict to store the data
72+
variables = ["temperature",
73+
"pressure",
74+
"humidity",
75+
"light",
76+
"oxidised",
77+
"reduced",
78+
"nh3",
79+
"pm1",
80+
"pm25",
81+
"pm10"]
82+
83+
units = ["C",
84+
"hPa",
85+
"%",
86+
"Lux",
87+
"kO",
88+
"kO",
89+
"kO",
90+
"ug/m3",
91+
"ug/m3",
92+
"ug/m3"]
93+
94+
# Define your own warning limits
95+
# The limits definition follows the order of the variables array
96+
# Example limits explanation for temperature:
97+
# [4,18,28,35] means
98+
# [-273.15 .. 4] -> Dangerously Low
99+
# (4 .. 18] -> Low
100+
# (18 .. 28] -> Normal
101+
# (28 .. 35] -> High
102+
# (35 .. MAX] -> Dangerously High
103+
# DISCLAIMER: The limits provided here are just examples and come
104+
# with NO WARRANTY. The authors of this example code claim
105+
# NO RESPONSIBILITY if reliance on the following values or this
106+
# code in general leads to ANY DAMAGES or DEATH.
107+
limits = [[4,18,28,35],
108+
[250,650,1013.25,1015],
109+
[20,30,60,70],
110+
[-1,-1,30000,100000],
111+
[-1,-1,40,50],
112+
[-1,-1,450,550],
113+
[-1,-1,200,300],
114+
[-1,-1,50,100],
115+
[-1,-1,50,100],
116+
[-1,-1,50,100]]
117+
118+
# RGB palette for values on the combined screen
119+
palette = [(0,0,255), # Dangerously Low
120+
(0,255,255), # Low
121+
(0,255,0), # Normal
122+
(255,255,0), # High
123+
(255,0,0)] # Dangerously High
124+
125+
values = {}
126+
127+
128+
# Displays data and text on the 0.96" LCD
129+
def display_text(variable, data, unit):
130+
# Maintain length of list
131+
values[variable] = values[variable][1:] + [data]
132+
# Scale the values for the variable between 0 and 1
133+
colours = [(v - min(values[variable]) + 1) / (max(values[variable])
134+
- min(values[variable]) + 1) for v in values[variable]]
135+
# Format the variable name and value
136+
message = "{}: {:.1f} {}".format(variable[:4], data, unit)
137+
logging.info(message)
138+
draw.rectangle((0, 0, WIDTH, HEIGHT), (255, 255, 255))
139+
for i in range(len(colours)):
140+
# Convert the values to colours from red to blue
141+
colour = (1.0 - colours[i]) * 0.6
142+
r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour,
143+
1.0, 1.0)]
144+
# Draw a 1-pixel wide rectangle of colour
145+
draw.rectangle((i, top_pos, i+1, HEIGHT), (r, g, b))
146+
# Draw a line graph in black
147+
line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos)))\
148+
+ top_pos
149+
draw.rectangle((i, line_y, i+1, line_y+1), (0, 0, 0))
150+
# Write the text at the top in black
151+
draw.text((0, 0), message, font=font, fill=(0, 0, 0))
152+
st7735.display(img)
153+
154+
# Saves the data to be used in the graphs later and prints to the log
155+
def save_data(idx, data):
156+
variable = variables[idx]
157+
# Maintain length of list
158+
values[variable] = values[variable][1:] + [data]
159+
unit = units[idx]
160+
message = "{}: {:.1f} {}".format(variable[:4], data, unit)
161+
logging.info(message)
162+
163+
164+
# Displays all the text on the 0.96" LCD
165+
def display_everything():
166+
draw.rectangle((0, 0, WIDTH, HEIGHT), (0, 0, 0))
167+
column_count = 2
168+
row_count = (len(variables)/column_count)
169+
for i in xrange(len(variables)):
170+
variable = variables[i]
171+
data_value = values[variable][-1]
172+
unit = units[i]
173+
x = x_offset + ((WIDTH/column_count) * (i / row_count))
174+
y = y_offset + ((HEIGHT/row_count) * (i % row_count))
175+
message = "{}: {:.1f} {}".format(variable[:4], data_value, unit)
176+
lim = limits[i]
177+
rgb = palette[0]
178+
for j in xrange(len(lim)):
179+
if data_value > lim[j]:
180+
rgb = palette[j+1]
181+
draw.text((x, y), message, font=smallfont, fill=rgb)
182+
st7735.display(img)
183+
184+
185+
186+
# Get the temperature of the CPU for compensation
187+
def get_cpu_temperature():
188+
process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
189+
output, _error = process.communicate()
190+
return float(output[output.index('=') + 1:output.rindex("'")])
191+
192+
193+
# Tuning factor for compensation. Decrease this number to adjust the
194+
# temperature down, and increase to adjust up
195+
factor = 1.95
196+
197+
cpu_temps = [get_cpu_temperature()] * 5
198+
199+
delay = 0.5 # Debounce the proximity tap
200+
mode = 10 # The starting mode
201+
last_page = 0
202+
light = 1
203+
204+
for v in variables:
205+
values[v] = [1] * WIDTH
206+
207+
# The main loop
208+
try:
209+
while True:
210+
proximity = ltr559.get_proximity()
211+
212+
# If the proximity crosses the threshold, toggle the mode
213+
if proximity > 1500 and time.time() - last_page > delay:
214+
mode += 1
215+
mode %= (len(variables)+1)
216+
last_page = time.time()
217+
218+
# One mode for each variable
219+
if mode == 0:
220+
# variable = "temperature"
221+
unit = "C"
222+
cpu_temp = get_cpu_temperature()
223+
# Smooth out with some averaging to decrease jitter
224+
cpu_temps = cpu_temps[1:] + [cpu_temp]
225+
avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
226+
raw_temp = bme280.get_temperature()
227+
data = raw_temp - ((avg_cpu_temp - raw_temp) / factor)
228+
display_text(variables[mode], data, unit)
229+
230+
if mode == 1:
231+
# variable = "pressure"
232+
unit = "hPa"
233+
data = bme280.get_pressure()
234+
display_text(variables[mode], data, unit)
235+
236+
if mode == 2:
237+
# variable = "humidity"
238+
unit = "%"
239+
data = bme280.get_humidity()
240+
display_text(variables[mode], data, unit)
241+
242+
if mode == 3:
243+
# variable = "light"
244+
unit = "Lux"
245+
if proximity < 10:
246+
data = ltr559.get_lux()
247+
else:
248+
data = 1
249+
display_text(variables[mode], data, unit)
250+
251+
if mode == 4:
252+
# variable = "oxidised"
253+
unit = "kO"
254+
data = gas.read_all()
255+
data = data.oxidising / 1000
256+
display_text(variables[mode], data, unit)
257+
258+
if mode == 5:
259+
# variable = "reduced"
260+
unit = "kO"
261+
data = gas.read_all()
262+
data = data.reducing / 1000
263+
display_text(variables[mode], data, unit)
264+
265+
if mode == 6:
266+
# variable = "nh3"
267+
unit = "kO"
268+
data = gas.read_all()
269+
data = data.nh3 / 1000
270+
display_text(variables[mode], data, unit)
271+
272+
if mode == 7:
273+
# variable = "pm1"
274+
unit = "ug/m3"
275+
try:
276+
data = pms5003.read()
277+
except pmsReadTimeoutError:
278+
logging.warn("Failed to read PMS5003")
279+
else:
280+
data = float(data.pm_ug_per_m3(1.0))
281+
display_text(variables[mode], data, unit)
282+
283+
if mode == 8:
284+
# variable = "pm25"
285+
unit = "ug/m3"
286+
try:
287+
data = pms5003.read()
288+
except pmsReadTimeoutError:
289+
logging.warn("Failed to read PMS5003")
290+
else:
291+
data = float(data.pm_ug_per_m3(2.5))
292+
display_text(variables[mode], data, unit)
293+
294+
if mode == 9:
295+
# variable = "pm10"
296+
unit = "ug/m3"
297+
try:
298+
data = pms5003.read()
299+
except pmsReadTimeoutError:
300+
logging.warn("Failed to read PMS5003")
301+
else:
302+
data = float(data.pm_ug_per_m3(10))
303+
display_text(variables[mode], data, unit)
304+
if mode == 10:
305+
# Everything on one screen
306+
cpu_temp = get_cpu_temperature()
307+
# Smooth out with some averaging to decrease jitter
308+
cpu_temps = cpu_temps[1:] + [cpu_temp]
309+
avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
310+
raw_temp = bme280.get_temperature()
311+
raw_data = raw_temp - ((avg_cpu_temp - raw_temp) / factor)
312+
save_data(0, raw_data)
313+
display_everything()
314+
raw_data = bme280.get_pressure()
315+
save_data(1, raw_data)
316+
display_everything()
317+
raw_data = bme280.get_humidity()
318+
save_data(2, raw_data)
319+
if proximity < 10:
320+
raw_data = ltr559.get_lux()
321+
else:
322+
raw_data = 1
323+
save_data(3, raw_data)
324+
display_everything()
325+
gas_data = gas.read_all()
326+
save_data(4, gas_data.oxidising / 1000)
327+
save_data(5, gas_data.reducing / 1000)
328+
save_data(6, gas_data.nh3 / 1000)
329+
display_everything()
330+
pms_data = None
331+
try:
332+
pms_data = pms5003.read()
333+
except pmsReadTimeoutError:
334+
logging.warn("Failed to read PMS5003")
335+
else:
336+
save_data(7, float(pms_data.pm_ug_per_m3(1.0)))
337+
save_data(8, float(pms_data.pm_ug_per_m3(2.5)))
338+
save_data(9, float(pms_data.pm_ug_per_m3(10)))
339+
display_everything()
340+
341+
342+
343+
# Exit cleanly
344+
except KeyboardInterrupt:
345+
sys.exit(0)

0 commit comments

Comments
 (0)