Update README for 0.0.2
[EVA-2020-02-2.git] / examples / all-in-one.py
CommitLineData
20442c9a 1#!/usr/bin/env python3
d7b32dab
SM
2
3import time
4import colorsys
5import os
6import sys
7import ST7735
bca04496
PH
8try:
9 # Transitional fix for breaking change in LTR559
10 from ltr559 import LTR559
11 ltr559 = LTR559()
12except ImportError:
13 import ltr559
d7b32dab
SM
14
15from bme280 import BME280
8a7d81b6 16from pms5003 import PMS5003, ReadTimeoutError as pmsReadTimeoutError
d7b32dab 17from enviroplus import gas
9d2c6929 18from subprocess import PIPE, Popen
d7b32dab
SM
19from PIL import Image
20from PIL import ImageDraw
21from PIL import ImageFont
20442c9a 22from fonts.ttf import RobotoMedium as UserFont
10b73e18 23import logging
d7b32dab 24
10b73e18
CM
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
d7b32dab
SM
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)
20442c9a 61font_size = 20
62font = ImageFont.truetype(UserFont, font_size)
d7b32dab
SM
63
64message = ""
65
66# The position of the top bar
67top_pos = 25
68
ec075941 69
d7b32dab
SM
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
ec075941
SM
75 colours = [(v - min(values[variable]) + 1) / (max(values[variable])
76 - min(values[variable]) + 1) for v in values[variable]]
d7b32dab
SM
77 # Format the variable name and value
78 message = "{}: {:.1f} {}".format(variable[:4], data, unit)
10b73e18 79 logging.info(message)
d7b32dab
SM
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
ec075941
SM
84 r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour,
85 1.0, 1.0)]
d7b32dab
SM
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
ec075941
SM
89 line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos)))\
90 + top_pos
d7b32dab
SM
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
ec075941 96
9d2c6929
SM
97# Get the temperature of the CPU for compensation
98def get_cpu_temperature():
e2d010e2 99 process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
9d2c6929
SM
100 output, _error = process.communicate()
101 return float(output[output.index('=') + 1:output.rindex("'")])
102
ec075941 103
9d2c6929
SM
104# Tuning factor for compensation. Decrease this number to adjust the
105# temperature down, and increase to adjust up
20442c9a 106factor = 2.25
9d2c6929 107
27156d25 108cpu_temps = [get_cpu_temperature()] * 5
9d2c6929 109
d7b32dab 110delay = 0.5 # Debounce the proximity tap
2c6a2d72 111mode = 0 # The starting mode
d7b32dab
SM
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:
e8fa1c06 145 # variable = "temperature"
d7b32dab 146 unit = "C"
9d2c6929
SM
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)
e8fa1c06 153 display_text(variables[mode], data, unit)
d7b32dab
SM
154
155 if mode == 1:
e8fa1c06 156 # variable = "pressure"
d7b32dab
SM
157 unit = "hPa"
158 data = bme280.get_pressure()
e8fa1c06 159 display_text(variables[mode], data, unit)
d7b32dab
SM
160
161 if mode == 2:
e8fa1c06 162 # variable = "humidity"
d7b32dab
SM
163 unit = "%"
164 data = bme280.get_humidity()
e8fa1c06 165 display_text(variables[mode], data, unit)
d7b32dab
SM
166
167 if mode == 3:
e8fa1c06 168 # variable = "light"
d7b32dab
SM
169 unit = "Lux"
170 if proximity < 10:
171 data = ltr559.get_lux()
172 else:
173 data = 1
e8fa1c06 174 display_text(variables[mode], data, unit)
d7b32dab
SM
175
176 if mode == 4:
e8fa1c06 177 # variable = "oxidised"
d7b32dab
SM
178 unit = "kO"
179 data = gas.read_all()
180 data = data.oxidising / 1000
e8fa1c06 181 display_text(variables[mode], data, unit)
d7b32dab
SM
182
183 if mode == 5:
e8fa1c06 184 # variable = "reduced"
d7b32dab
SM
185 unit = "kO"
186 data = gas.read_all()
187 data = data.reducing / 1000
e8fa1c06 188 display_text(variables[mode], data, unit)
d7b32dab
SM
189
190 if mode == 6:
e8fa1c06 191 # variable = "nh3"
d7b32dab
SM
192 unit = "kO"
193 data = gas.read_all()
194 data = data.nh3 / 1000
e8fa1c06 195 display_text(variables[mode], data, unit)
d7b32dab
SM
196
197 if mode == 7:
69294f3c 198 # variable = "pm1"
d7b32dab 199 unit = "ug/m3"
e8fa1c06
CM
200 try:
201 data = pms5003.read()
8a7d81b6 202 except pmsReadTimeoutError:
2c6a2d72 203 logging.warn("Failed to read PMS5003")
e8fa1c06 204 else:
426f1cbc 205 data = float(data.pm_ug_per_m3(1.0))
e8fa1c06 206 display_text(variables[mode], data, unit)
d7b32dab
SM
207
208 if mode == 8:
e8fa1c06 209 # variable = "pm25"
d7b32dab 210 unit = "ug/m3"
8a7d81b6
CM
211 try:
212 data = pms5003.read()
213 except pmsReadTimeoutError:
2c6a2d72 214 logging.warn("Failed to read PMS5003")
8a7d81b6 215 else:
426f1cbc 216 data = float(data.pm_ug_per_m3(2.5))
8a7d81b6 217 display_text(variables[mode], data, unit)
d7b32dab
SM
218
219 if mode == 9:
e8fa1c06 220 # variable = "pm10"
f86c4c6d 221 unit = "ug/m3"
8a7d81b6
CM
222 try:
223 data = pms5003.read()
224 except pmsReadTimeoutError:
2c6a2d72 225 logging.warn("Failed to read PMS5003")
8a7d81b6 226 else:
426f1cbc 227 data = float(data.pm_ug_per_m3(10))
8a7d81b6 228 display_text(variables[mode], data, unit)
d7b32dab
SM
229
230# Exit cleanly
231except KeyboardInterrupt:
232 sys.exit(0)