Commit | Line | Data |
---|---|---|
20442c9a | 1 | #!/usr/bin/env python3 |
d7b32dab SM |
2 | |
3 | import time | |
4 | import colorsys | |
d7b32dab SM |
5 | import sys |
6 | import ST7735 | |
bca04496 PH |
7 | try: |
8 | # Transitional fix for breaking change in LTR559 | |
9 | from ltr559 import LTR559 | |
10 | ltr559 = LTR559() | |
11 | except ImportError: | |
12 | import ltr559 | |
d7b32dab SM |
13 | |
14 | from bme280 import BME280 | |
8a7d81b6 | 15 | from pms5003 import PMS5003, ReadTimeoutError as pmsReadTimeoutError |
d7b32dab | 16 | from enviroplus import gas |
9d2c6929 | 17 | from subprocess import PIPE, Popen |
d7b32dab SM |
18 | from PIL import Image |
19 | from PIL import ImageDraw | |
20 | from PIL import ImageFont | |
20442c9a | 21 | from fonts.ttf import RobotoMedium as UserFont |
10b73e18 | 22 | import logging |
d7b32dab | 23 | |
10b73e18 CM |
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 | |
d7b32dab SM |
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) | |
20442c9a | 60 | font_size = 20 |
61 | font = ImageFont.truetype(UserFont, font_size) | |
d7b32dab SM |
62 | |
63 | message = "" | |
64 | ||
65 | # The position of the top bar | |
66 | top_pos = 25 | |
67 | ||
ec075941 | 68 | |
d7b32dab SM |
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 | |
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 |
96 | def 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 | 104 | factor = 2.25 |
9d2c6929 | 105 | |
27156d25 | 106 | cpu_temps = [get_cpu_temperature()] * 5 |
9d2c6929 | 107 | |
d7b32dab | 108 | delay = 0.5 # Debounce the proximity tap |
2c6a2d72 | 109 | mode = 0 # The starting mode |
d7b32dab SM |
110 | last_page = 0 |
111 | light = 1 | |
112 | ||
113 | # Create a values dict to store the data | |
114 | variables = ["temperature", | |
115 | "pressure", | |
116 | "humidity", | |
117 | "light", | |
118 | "oxidised", | |
119 | "reduced", | |
120 | "nh3", | |
121 | "pm1", | |
122 | "pm25", | |
123 | "pm10"] | |
124 | ||
125 | values = {} | |
126 | ||
127 | for v in variables: | |
128 | values[v] = [1] * WIDTH | |
129 | ||
130 | # The main loop | |
131 | try: | |
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: |
6a89566f | 201 | logging.warning("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: | |
6a89566f | 212 | logging.warning("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: | |
6a89566f | 223 | logging.warning("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 | |
229 | except KeyboardInterrupt: | |
230 | sys.exit(0) |