Commit | Line | Data |
---|---|---|
d7b32dab SM |
1 | #!/usr/bin/env python |
2 | ||
3 | import time | |
4 | import colorsys | |
5 | import os | |
6 | import sys | |
7 | import ST7735 | |
8 | import ltr559 | |
9 | ||
10 | from bme280 import BME280 | |
8a7d81b6 | 11 | from pms5003 import PMS5003, ReadTimeoutError as pmsReadTimeoutError |
d7b32dab | 12 | from enviroplus import gas |
9d2c6929 | 13 | from subprocess import PIPE, Popen |
d7b32dab SM |
14 | from PIL import Image |
15 | from PIL import ImageDraw | |
16 | from PIL import ImageFont | |
17 | ||
18 | print("""all-in-one.py - Displays readings from all of Enviro plus' sensors | |
19 | ||
20 | Press Ctrl+C to exit! | |
21 | ||
22 | """) | |
23 | ||
24 | # BME280 temperature/pressure/humidity sensor | |
25 | bme280 = BME280() | |
26 | ||
27 | # PMS5003 particulate sensor | |
28 | pms5003 = PMS5003() | |
29 | ||
30 | # Create ST7735 LCD display class | |
31 | st7735 = ST7735.ST7735( | |
32 | port=0, | |
33 | cs=1, | |
34 | dc=9, | |
35 | backlight=12, | |
36 | rotation=270, | |
37 | spi_speed_hz=10000000 | |
38 | ) | |
39 | ||
40 | # Initialize display | |
41 | st7735.begin() | |
42 | ||
43 | WIDTH = st7735.width | |
44 | HEIGHT = st7735.height | |
45 | ||
46 | # Set up canvas and font | |
47 | img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0)) | |
48 | draw = ImageDraw.Draw(img) | |
49 | path = os.path.dirname(os.path.realpath(__file__)) | |
50 | font = ImageFont.truetype(path + "/fonts/Asap/Asap-Bold.ttf", 20) | |
51 | ||
52 | message = "" | |
53 | ||
54 | # The position of the top bar | |
55 | top_pos = 25 | |
56 | ||
ec075941 | 57 | |
d7b32dab SM |
58 | # Displays data and text on the 0.96" LCD |
59 | def display_text(variable, data, unit): | |
60 | # Maintain length of list | |
61 | values[variable] = values[variable][1:] + [data] | |
62 | # Scale the values for the variable between 0 and 1 | |
ec075941 SM |
63 | colours = [(v - min(values[variable]) + 1) / (max(values[variable]) |
64 | - min(values[variable]) + 1) for v in values[variable]] | |
d7b32dab SM |
65 | # Format the variable name and value |
66 | message = "{}: {:.1f} {}".format(variable[:4], data, unit) | |
67 | print(message) | |
68 | draw.rectangle((0, 0, WIDTH, HEIGHT), (255, 255, 255)) | |
69 | for i in range(len(colours)): | |
70 | # Convert the values to colours from red to blue | |
71 | colour = (1.0 - colours[i]) * 0.6 | |
ec075941 SM |
72 | r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, |
73 | 1.0, 1.0)] | |
d7b32dab SM |
74 | # Draw a 1-pixel wide rectangle of colour |
75 | draw.rectangle((i, top_pos, i+1, HEIGHT), (r, g, b)) | |
76 | # Draw a line graph in black | |
ec075941 SM |
77 | line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos)))\ |
78 | + top_pos | |
d7b32dab SM |
79 | draw.rectangle((i, line_y, i+1, line_y+1), (0, 0, 0)) |
80 | # Write the text at the top in black | |
81 | draw.text((0, 0), message, font=font, fill=(0, 0, 0)) | |
82 | st7735.display(img) | |
83 | ||
ec075941 | 84 | |
9d2c6929 SM |
85 | # Get the temperature of the CPU for compensation |
86 | def get_cpu_temperature(): | |
e2d010e2 | 87 | process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True) |
9d2c6929 SM |
88 | output, _error = process.communicate() |
89 | return float(output[output.index('=') + 1:output.rindex("'")]) | |
90 | ||
ec075941 | 91 | |
9d2c6929 SM |
92 | # Tuning factor for compensation. Decrease this number to adjust the |
93 | # temperature down, and increase to adjust up | |
94 | factor = 0.8 | |
95 | ||
27156d25 | 96 | cpu_temps = [get_cpu_temperature()] * 5 |
9d2c6929 | 97 | |
d7b32dab SM |
98 | delay = 0.5 # Debounce the proximity tap |
99 | mode = 0 # The starting mode | |
100 | last_page = 0 | |
101 | light = 1 | |
102 | ||
103 | # Create a values dict to store the data | |
104 | variables = ["temperature", | |
105 | "pressure", | |
106 | "humidity", | |
107 | "light", | |
108 | "oxidised", | |
109 | "reduced", | |
110 | "nh3", | |
111 | "pm1", | |
112 | "pm25", | |
113 | "pm10"] | |
114 | ||
115 | values = {} | |
116 | ||
117 | for v in variables: | |
118 | values[v] = [1] * WIDTH | |
119 | ||
120 | # The main loop | |
121 | try: | |
122 | while True: | |
123 | proximity = ltr559.get_proximity() | |
124 | ||
125 | # If the proximity crosses the threshold, toggle the mode | |
126 | if proximity > 1500 and time.time() - last_page > delay: | |
127 | mode += 1 | |
128 | mode %= len(variables) | |
129 | last_page = time.time() | |
130 | ||
131 | # One mode for each variable | |
132 | if mode == 0: | |
e8fa1c06 | 133 | # variable = "temperature" |
d7b32dab | 134 | unit = "C" |
9d2c6929 SM |
135 | cpu_temp = get_cpu_temperature() |
136 | # Smooth out with some averaging to decrease jitter | |
137 | cpu_temps = cpu_temps[1:] + [cpu_temp] | |
138 | avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps)) | |
139 | raw_temp = bme280.get_temperature() | |
140 | data = raw_temp - ((avg_cpu_temp - raw_temp) / factor) | |
e8fa1c06 | 141 | display_text(variables[mode], data, unit) |
d7b32dab SM |
142 | |
143 | if mode == 1: | |
e8fa1c06 | 144 | # variable = "pressure" |
d7b32dab SM |
145 | unit = "hPa" |
146 | data = bme280.get_pressure() | |
e8fa1c06 | 147 | display_text(variables[mode], data, unit) |
d7b32dab SM |
148 | |
149 | if mode == 2: | |
e8fa1c06 | 150 | # variable = "humidity" |
d7b32dab SM |
151 | unit = "%" |
152 | data = bme280.get_humidity() | |
e8fa1c06 | 153 | display_text(variables[mode], data, unit) |
d7b32dab SM |
154 | |
155 | if mode == 3: | |
e8fa1c06 | 156 | # variable = "light" |
d7b32dab SM |
157 | unit = "Lux" |
158 | if proximity < 10: | |
159 | data = ltr559.get_lux() | |
160 | else: | |
161 | data = 1 | |
e8fa1c06 | 162 | display_text(variables[mode], data, unit) |
d7b32dab SM |
163 | |
164 | if mode == 4: | |
e8fa1c06 | 165 | # variable = "oxidised" |
d7b32dab SM |
166 | unit = "kO" |
167 | data = gas.read_all() | |
168 | data = data.oxidising / 1000 | |
e8fa1c06 | 169 | display_text(variables[mode], data, unit) |
d7b32dab SM |
170 | |
171 | if mode == 5: | |
e8fa1c06 | 172 | # variable = "reduced" |
d7b32dab SM |
173 | unit = "kO" |
174 | data = gas.read_all() | |
175 | data = data.reducing / 1000 | |
e8fa1c06 | 176 | display_text(variables[mode], data, unit) |
d7b32dab SM |
177 | |
178 | if mode == 6: | |
e8fa1c06 | 179 | # variable = "nh3" |
d7b32dab SM |
180 | unit = "kO" |
181 | data = gas.read_all() | |
182 | data = data.nh3 / 1000 | |
e8fa1c06 | 183 | display_text(variables[mode], data, unit) |
d7b32dab SM |
184 | |
185 | if mode == 7: | |
69294f3c | 186 | # variable = "pm1" |
d7b32dab | 187 | unit = "ug/m3" |
e8fa1c06 CM |
188 | try: |
189 | data = pms5003.read() | |
8a7d81b6 | 190 | except pmsReadTimeoutError: |
e8fa1c06 CM |
191 | pass |
192 | else: | |
193 | data = data.pm_ug_per_m3(1.0) | |
194 | display_text(variables[mode], data, unit) | |
d7b32dab SM |
195 | |
196 | if mode == 8: | |
e8fa1c06 | 197 | # variable = "pm25" |
d7b32dab | 198 | unit = "ug/m3" |
8a7d81b6 CM |
199 | try: |
200 | data = pms5003.read() | |
201 | except pmsReadTimeoutError: | |
202 | pass | |
203 | else: | |
204 | data = data.pm_ug_per_m3(2.5) | |
205 | display_text(variables[mode], data, unit) | |
d7b32dab SM |
206 | |
207 | if mode == 9: | |
e8fa1c06 | 208 | # variable = "pm10" |
f86c4c6d | 209 | unit = "ug/m3" |
8a7d81b6 CM |
210 | try: |
211 | data = pms5003.read() | |
212 | except pmsReadTimeoutError: | |
213 | pass | |
214 | else: | |
215 | data = data.pm_ug_per_m3(10) | |
216 | display_text(variables[mode], data, unit) | |
d7b32dab SM |
217 | |
218 | # Exit cleanly | |
219 | except KeyboardInterrupt: | |
220 | sys.exit(0) |