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