9 # Transitional fix for breaking change in LTR559
10 from ltr559
import LTR559
15 from bme280
import BME280
16 from pms5003
import PMS5003
, ReadTimeoutError
as pmsReadTimeoutError
17 from enviroplus
import gas
18 from subprocess
import PIPE
, Popen
20 from PIL
import ImageDraw
21 from PIL
import ImageFont
25 format
='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
27 datefmt
='%Y-%m-%d %H:%M:%S')
29 logging
.info("""all-in-one.py - Displays readings from all of Enviro plus' sensors
35 # BME280 temperature/pressure/humidity sensor
38 # PMS5003 particulate sensor
41 # Create ST7735 LCD display class
42 st7735
= ST7735
.ST7735(
55 HEIGHT
= st7735
.height
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)
68 # The position of the top bar
71 # Create a values dict to store the data
72 variables
= ["temperature",
94 # Define your own warning limits
95 # The limits definition follows the order of the variables array
96 # Example limits explanation for temperature:
98 # [-273.15 .. 4] -> Dangerously Low
100 # (18 .. 28] -> Normal
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],
110 [-1,-1,30000,100000],
118 # RGB palette for values on the combined screen
119 palette
= [(0,0,255), # Dangerously Low
123 (255,0,0)] # Dangerously High
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
,
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
)))\
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))
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
]
160 message
= "{}: {:.1f} {}".format(variable
[:4], data
, unit
)
161 logging
.info(message
)
164 # Displays all the text on the 0.96" LCD
165 def display_everything():
166 draw
.rectangle((0, 0, WIDTH
, HEIGHT
), (0, 0, 0))
168 row_count
= (len(variables
)/column_count
)
169 for i
in xrange(len(variables
)):
170 variable
= variables
[i
]
171 data_value
= values
[variable
][-1]
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
)
178 for j
in xrange(len(lim
)):
179 if data_value
> lim
[j
]:
181 draw
.text((x
, y
), message
, font
=smallfont
, fill
=rgb
)
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("'")])
193 # Tuning factor for compensation. Decrease this number to adjust the
194 # temperature down, and increase to adjust up
197 cpu_temps
= [get_cpu_temperature()] * 5
199 delay
= 0.5 # Debounce the proximity tap
200 mode
= 10 # The starting mode
205 values
[v
] = [1] * WIDTH
210 proximity
= ltr559
.get_proximity()
212 # If the proximity crosses the threshold, toggle the mode
213 if proximity
> 1500 and time
.time() - last_page
> delay
:
215 mode
%= (len(variables
)+1)
216 last_page
= time
.time()
218 # One mode for each variable
220 # variable = "temperature"
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
)
231 # variable = "pressure"
233 data
= bme280
.get_pressure()
234 display_text(variables
[mode
], data
, unit
)
237 # variable = "humidity"
239 data
= bme280
.get_humidity()
240 display_text(variables
[mode
], data
, unit
)
246 data
= ltr559
.get_lux()
249 display_text(variables
[mode
], data
, unit
)
252 # variable = "oxidised"
254 data
= gas
.read_all()
255 data
= data
.oxidising
/ 1000
256 display_text(variables
[mode
], data
, unit
)
259 # variable = "reduced"
261 data
= gas
.read_all()
262 data
= data
.reducing
/ 1000
263 display_text(variables
[mode
], data
, unit
)
268 data
= gas
.read_all()
269 data
= data
.nh3
/ 1000
270 display_text(variables
[mode
], data
, unit
)
276 data
= pms5003
.read()
277 except pmsReadTimeoutError
:
278 logging
.warn("Failed to read PMS5003")
280 data
= float(data
.pm_ug_per_m3(1.0))
281 display_text(variables
[mode
], data
, unit
)
287 data
= pms5003
.read()
288 except pmsReadTimeoutError
:
289 logging
.warn("Failed to read PMS5003")
291 data
= float(data
.pm_ug_per_m3(2.5))
292 display_text(variables
[mode
], data
, unit
)
298 data
= pms5003
.read()
299 except pmsReadTimeoutError
:
300 logging
.warn("Failed to read PMS5003")
302 data
= float(data
.pm_ug_per_m3(10))
303 display_text(variables
[mode
], data
, unit
)
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
)
314 raw_data
= bme280
.get_pressure()
315 save_data(1, raw_data
)
317 raw_data
= bme280
.get_humidity()
318 save_data(2, raw_data
)
320 raw_data
= ltr559
.get_lux()
323 save_data(3, raw_data
)
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)
332 pms_data
= pms5003
.read()
333 except pmsReadTimeoutError
:
334 logging
.warn("Failed to read PMS5003")
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)))
344 except KeyboardInterrupt: