feat(examples/a): Add absolute dew point temperature display mode
[EVA-2020-02-2.git] / examples / all-in-one-enviro-mini-bk.py
CommitLineData
f0613e57
SBS
1#!/usr/bin/env python3
2
3import time
4import colorsys
5import os
6import sys
7import ST7735
8import schedule # https://pypi.org/project/schedule/ via https://stackoverflow.com/a/16786600
9import datetime # http://stackoverflow.com/questions/2150739/ddg#28147286
10try:
11 # Transitional fix for breaking change in LTR559
12 from ltr559 import LTR559
13 ltr559 = LTR559()
14except ImportError:
15 import ltr559
16
17from bme280 import BME280
18from enviroplus import gas
19from subprocess import PIPE, Popen
20from PIL import Image
21from PIL import ImageDraw
22from PIL import ImageFont
23from fonts.ttf import RobotoMedium as UserFont
24import logging
25import numpy as np
26
27logging.basicConfig(
28 format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
29 level=logging.INFO,
30 datefmt='%Y-%m-%d %H:%M:%S')
31
32logging.info("""all-in-one.py - Displays readings from all of Enviro plus' sensors
33Press Ctrl+C to exit!
34""")
35
36# Script Constants
37varLenBufferTTL = int((2*24*60*60)*10**9) # time-to-live in nanoseconds
38
39# BME280 temperature/pressure/humidity sensor
40bme280 = BME280()
41
42# Create ST7735 LCD display class
43st7735 = ST7735.ST7735(
44 port=0,
45 cs=1,
46 dc=9,
47 backlight=12,
4cbbc360
SBS
48 rotation=270,
49 #rotation=90, # flip upside down wrt enviro+ default orientation
f0613e57
SBS
50 spi_speed_hz=10000000
51)
52
53# Initialize display
54st7735.begin()
55
56WIDTH = st7735.width
57HEIGHT = st7735.height
58
59# Set up canvas and font
60img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
61draw = ImageDraw.Draw(img)
62path = os.path.dirname(os.path.realpath(__file__))
63font_size = 20
64font = ImageFont.truetype(UserFont, font_size)
65font2_size = 15
66font2 = ImageFont.truetype(UserFont, font2_size)
67
68message = ""
69
70# The position of the top bar
71top_pos = 25
72
73
74# Displays data and text on the 0.96" LCD
75def display_text(variable, data, unit):
76 # Maintain length of list
77 values[variable] = values[variable][1:] + [data]
78 # Scale the values for the variable between 0 and 1
79 vmin = min(values[variable])
80 vmax = max(values[variable])
81 colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]]
82 # Format the variable name and value
83 message = "{}: {:.1f} {}".format(variable[:4], data, unit)
84 logging.info(message)
85 draw.rectangle((0, 0, WIDTH, HEIGHT), (255, 255, 255))
86 for i in range(len(colours)):
87 # Convert the values to colours from red to blue
88 colour = (1.0 - colours[i]) * 0.6
89 r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)]
90 # Draw a 1-pixel wide rectangle of colour
91 draw.rectangle((i, top_pos, i + 1, HEIGHT), (r, g, b))
92 # Draw a line graph in black
93 line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos))) + top_pos
94 draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0))
95 # Write the text at the top in black
96 draw.text((0, 0), message, font=font, fill=(0, 0, 0))
97 st7735.display(img)
98
99# Displays data and text on the 0.96" LCD
100def display_text2(variable, data, unit, values):
101 # Scale the values for the variable between 0 and 1
eba03e3f 102 #print('DEBUG:len(values[' + str(variable) + ']):' + str(len(values[variable])))
f0613e57
SBS
103 #print('DEBUG:values[' + str(variable) + ']:' + str(values[variable]))
104 vmin = min(values[variable])
105 vmax = max(values[variable])
106 colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]]
107 # Format the variable name and value
108 message = "{}: {:.1f} {}".format(variable[:4], data, unit)
109 #message = "{}: {:.1f} {}".format(variable[:4], values[variable][-1], unit)
110 logging.info(message)
111 draw.rectangle((0, 0, WIDTH, HEIGHT), (255, 255, 255))
112 for i in range(len(colours)):
113 # Convert the values to colours from red to blue
114 colour = (1.0 - colours[i]) * 0.6
115 r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)]
116 # Draw a 1-pixel wide rectangle of colour
117 draw.rectangle((i, top_pos, i + 1, HEIGHT), (r, g, b))
118 # Draw a line graph in black
119 line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos))) + top_pos
120 draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0))
121 # Write the text at the top in black
122 draw.text((0, 0), message, font=font, fill=(0, 0, 0))
123 # Write text (test)
124 maxMsg = "MAX:{:.1f}".format(vmax)
125 durMsg = "HR:{:.1f}".format(span_time_h)
126 minMsg = "MIN:{:.1f}".format(vmin)
127 maxMsg_y = int( ((HEIGHT - top_pos)/(HEIGHT))*(HEIGHT*(1/4)) + top_pos - (font2_size/2) )
128 durMsg_y = int( ((HEIGHT - top_pos)/(HEIGHT))*(HEIGHT*(2/4)) + top_pos - (font2_size/2) )
129 minMsg_y = int( ((HEIGHT - top_pos)/(HEIGHT))*(HEIGHT*(3/4)) + top_pos - (font2_size/2) )
130 maxMsg_x = int( WIDTH*(3/100) )
131 durMsg_x = int( WIDTH*(3/100) )
132 minMsg_x = int( WIDTH*(3/100) )
133 draw.text((maxMsg_x, maxMsg_y), maxMsg, font=font2, fill=(0, 0, 0))
134 draw.text((durMsg_x, durMsg_y), durMsg, font=font2, fill=(0, 0, 0))
135 draw.text((minMsg_x, minMsg_y), minMsg, font=font2, fill=(0, 0, 0))
136 st7735.display(img)
137
138
139# Get the temperature of the CPU for compensation
140def get_cpu_temperature():
141 process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True)
142 output, _error = process.communicate()
143 return float(output[output.index('=') + 1:output.rindex("'")])
144
f4b2db2d
SBS
145def rel_to_abs(T,P,RH):
146 """Returns absolute humidity given relative humidity.
147
148 Inputs:
149 --------
150 T : float
151 Absolute temperature in units Kelvin (K).
152 P : float
153 Total pressure in units Pascals (Pa).
154 RH : float
155 Relative humidity in units percent (%).
156
157 Output:
158 --------
159 absolute_humidity : float
160 Absolute humidity in units [kg water vapor / kg dry air].
161
162 References:
163 --------
164 1. Sonntag, D. "Advancements in the field of hygrometry". 1994. https://doi.org/10.1127/metz/3/1994/51
165 2. Green, D. "Perry's Chemical Engineers' Handbook" (8th Edition). Page "12-4". McGraw-Hill Professional Publishing. 2007.
166
167 Version: 0.0.1
168 Author: Steven Baltakatei Sandoval
169 License: GPLv3+
170 """
171
172 import math;
173
174 # Check input types
175 T = float(T);
176 P = float(P);
177 RH = float(RH);
178
179 #debug
180 # print('DEBUG:Input Temperature (K) :' + str(T));
181 # print('DEBUG:Input Pressure (Pa) :' + str(P));
182 # print('DEBUG:Input Rel. Humidity (%) :' + str(RH));
183
184 # Set constants and initial conversions
185 epsilon = 0.62198 # (molar mass of water vapor) / (molar mass of dry air)
186 t = T - 273.15; # Celsius from Kelvin
187 P_hpa = P / 100; # hectoPascals (hPa) from Pascals (Pa)
188
189 # Calculate e_w(T), saturation vapor pressure of water in a pure phase, in Pascals
190 ln_e_w = -6096*T**-1 + 21.2409642 - 2.711193*10**-2*T + 1.673952*10**-5*T**2 + 2.433502*math.log(T); # Sonntag-1994 eq 7; e_w in Pascals
191 e_w = math.exp(ln_e_w);
192 e_w_hpa = e_w / 100; # also save e_w in hectoPascals (hPa)
193 # print('DEBUG:ln_e_w:' + str(ln_e_w)); # debug
194 # print('DEBUG:e_w:' + str(e_w)); # debug
195
196 # Calculate f_w(P,T), enhancement factor for water
197 f_w = 1 + (10**-4*e_w_hpa)/(273 + t)*(((38 + 173*math.exp(-t/43))*(1 - (e_w_hpa / P_hpa))) + ((6.39 + 4.28*math.exp(-t / 107))*((P_hpa / e_w_hpa) - 1))); # Sonntag-1994 eq 22.
198 # print('DEBUG:f_w:' + str(f_w)); # debug
199
200 # Calculate e_prime_w(P,T), saturation vapor pressure of water in air-water mixture, in Pascals
201 e_prime_w = f_w * e_w; # Sonntag-1994 eq 18
202 # print('DEBUG:e_prime_w:' + str(e_prime_w)); # debug
203
204 # Calculate e_prime, vapor pressure of water in air, in Pascals
205 e_prime = (RH / 100) * e_prime_w;
206 # print('DEBUG:e_prime:' + str(e_prime)); # debug
207
208 # Calculate r, the absolute humidity, in [kg water vapor / kg dry air]
209 r = (epsilon * e_prime) / (P - e_prime);
210 # print('DEBUG:r:' + str(r)); # debug
211
212 return float(r);
213
3905a56d
SBS
214def rel_to_dpt(T,P,RH):
215 """Returns dew point temperature given relative humidity.
216
217 Inputs:
218 --------
219 T : float
220 Absolute temperature in units Kelvin (K).
221 P : float
222 Total pressure in units Pascals (Pa).
223 RH : float
224 Relative humidity in units percent (%).
225
226 Output:
227 --------
228 T_d : float
229 Dew point temperature in units Kelvin (K).
230
231 References:
232 --------
233 1. Sonntag, D. "Advancements in the field of hygrometry". 1994. https://doi.org/10.1127/metz/3/1994/51
234 2. Green, D. "Perry's Chemical Engineers' Handbook" (8th Edition). Page "12-4". McGraw-Hill Professional Publishing. 2007.
235
236 Version: 0.0.1
237 Author: Steven Baltakatei Sandoval
238 License: GPLv3+
239 """
240
241 import math;
242
243 # Check input types
244 T = float(T);
245 P = float(P);
246 RH = float(RH);
247
248 #debug
249 # print('DEBUG:Input Temperature (K) :' + str(T));
250 # print('DEBUG:Input Pressure (Pa) :' + str(P));
251 # print('DEBUG:Input Rel. Humidity (%) :' + str(RH));
252
253 # Set constants and initial conversions
254 epsilon = 0.62198 # (molar mass of water vapor) / (molar mass of dry air)
255 t = T - 273.15; # Celsius from Kelvin
256 P_hpa = P / 100; # hectoPascals (hPa) from Pascals (Pa)
257
258 # Calculate e_w(T), saturation vapor pressure of water in a pure phase, in Pascals
259 ln_e_w = -6096*T**-1 + 21.2409642 - 2.711193*10**-2*T + 1.673952*10**-5*T**2 + 2.433502*math.log(T); # Sonntag-1994 eq 7; e_w in Pascals
260 e_w = math.exp(ln_e_w);
261 e_w_hpa = e_w / 100; # also save e_w in hectoPascals (hPa)
262 # print('DEBUG:ln_e_w:' + str(ln_e_w)); # debug
263 # print('DEBUG:e_w:' + str(e_w)); # debug
264
265 # Calculate f_w(P,T), enhancement factor for water
266 f_w = 1 + (10**-4*e_w_hpa)/(273 + t)*(((38 + 173*math.exp(-t/43))*(1 - (e_w_hpa / P_hpa))) + ((6.39 + 4.28*math.exp(-t / 107))*((P_hpa / e_w_hpa) - 1))); # Sonntag-1994 eq 22.
267 # print('DEBUG:f_w:' + str(f_w)); # debug
268
269 # Calculate e_prime_w(P,T), saturation vapor pressure of water in air-water mixture, in Pascals
270 e_prime_w = f_w * e_w; # Sonntag-1994 eq 18
271 # print('DEBUG:e_prime_w:' + str(e_prime_w)); # debug
272
273 # Calculate e_prime, vapor pressure of water in air, in Pascals
274 e_prime = (RH / 100) * e_prime_w;
275 # print('DEBUG:e_prime:' + str(e_prime)); # debug
276
277 n = 0; repeat_flag = True;
278 while repeat_flag == True:
279 # print('DEBUG:n:' + str(n)); # debug
280
281 # Calculate f_w_td, the enhancement factor for water at dew point temperature.
282 if n == 0:
283 f = 1.0016 + 3.15*10**-6*P_hpa - (0.074 / P_hpa); # Sonntag-1994 eq 24
284 f_w_td = f; # initial approximation
285 elif n > 0:
286 t_d_prev = float(t_d); # save previous t_d value for later comparison
287 f_w_td = 1 + (10**-4*e_w_hpa)/(273 + t_d)*(((38 + 173*math.exp(-t_d/43))*(1 - (e_w_hpa / P_hpa))) + ((6.39 + 4.28*math.exp(-t_d / 107))*((P_hpa / e_w_hpa) - 1))); # Sonntag-1994 eq 22.
288 # print('DEBUG:f_w_td:' + str(f_w_td)); # debug
289
290 # Calculate e, the vapor pressure of water in the pure phase, in Pascals
291 e = (e_prime / f_w_td); # Sonntag-1994 eq 9 and 20
292 # print('DEBUG:e:' + str(e)); # debug
293
294 # Calculate y, an intermediate dew point calculation variable
295 y = math.log(e / 611.213);
296 # print('DEBUG:y:' + str(y)); # debug
297
298 # Calculate t_d, the dew point temperature in degrees Celsius
299 t_d = 13.715*y + 8.4262*10**-1*y**2 + 1.9048*10**-2*y**3 + 7.8158*10**-3*y**4;# Sonntag-1994 eq 10
300 # print('DEBUG:t_d:' + str(t_d)); # debug
301
302 if n == 0:
303 # First run
304 repeat_flag = True;
305 else:
306 # Test t_d accuracy
307 t_d_diff = math.fabs(t_d - t_d_prev);
308 # print('DEBUG:t_d :' + str(t_d)); # debug
309 # print('DEBUG:t_d_prev:' + str(t_d_prev)); # debug
310 # print('DEBUG:t_d_diff:' + str(t_d_diff)); # debug
311 if t_d_diff < 0.01:
312 repeat_flag = False;
313 else:
314 repeat_flag = True;
315
316 # Calculate T_d, the dew point temperature in Kelvin
317 T_d = 273.15 + t_d;
318 # print('DEBUG:T_d:' + str(T_d)); # debug
319
320 if n > 100:
321 return T_d; # good enough
322
323 # update loop counter
324 n += 1;
325 return T_d;
f0613e57
SBS
326
327# Tuning factor for compensation. Decrease this number to adjust the
328# temperature down, and increase to adjust up
329factor = 2.25
330
331cpu_temps = [get_cpu_temperature()] * 5
332
333delay = 0.5 # Debounce the proximity tap
334mode = 0 # The starting mode
335last_page = 0
336light = 1
337
338# Create a values dict to store the data
339variables = ["temperature",
340 "pressure",
341 "humidity",
f4b2db2d 342 "humidity_abs",
3905a56d 343 "dewpoint_temperature",
f0613e57
SBS
344 "light"]
345values = {} # Initialize values dictionary
346for v in variables:
347 values[v] = [1] * WIDTH # Init a WIDTH-length list as value for each string in variables
348
349# Create a varLenBuffer dict to store recent data
350varLenBuffer = {}
351for v in variables:
352 varLenBuffer[v] = [] # Init an empty list for each string in variables
353
354# Create a varLenBufferFlt dict to store recent data as floats only
355varLenBufferFlt = {}
356for v in variables:
357 varLenBufferFlt[v] = [] # Init an empty list for each string in variables
358
359# Create a fixLenBuffer dict to store data for displaying
360fixLenBuffer = {}
361for v in variables:
362 fixLenBuffer[v] = [] # Init an empty list for each string in variables
363
364pollDelay = 5.0
365
366def pollSensors():
367 # Desc: Update variables containing latest sensor tuples
368 # Output: (time [ns], unit, float)
369 # now_temp_tuple (°C)
370 # now_pressure_tuple (hPa)
371 # now_humidity_tuple (%)
eba03e3f 372 # now_humidity_abs_gkg_tuple (g water vapor / kg dry air)
f0613e57 373 # now_illuminance_tuple (lux)
3905a56d 374 # Depends: time, bme280, ltr559, get_cpu_temperature(), rel_to_abs(), rel_to_dpt()
f0613e57
SBS
375
376 # Tell function to modify these global variables
377 global now_temp_tuple
378 global now_pressure_tuple
379 global now_humidity_tuple
eba03e3f 380 global now_humidity_abs_gkg_tuple
3905a56d 381 global now_humidity_dpt_c_tuple
f0613e57
SBS
382 global now_illuminance_tuple
383 # Initialize
384 cpu_temps = []
385 poll_time_ns_start = time.time_ns() # Get time reading (unix spoech, nanoseconds)
386 # Get temperature reading
387 cpu_temp = get_cpu_temperature() # get °C from CPU
388 # Smooth out with some averaging to decrease jitter
389 cpu_temps = cpu_temps[1:] + [cpu_temp]
390 avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
391 raw_temp = bme280.get_temperature() # get °C from BME280 sensor
392 now_temp = raw_temp - ((avg_cpu_temp - raw_temp) / factor)
393 now_temp_tuple = (time.time_ns(), '°C', now_temp)
394 # Get pressure reading
395 now_time_ns = time.time_ns() # Get time reading (unix epoch, nanoseconds)
396 now_pressure = bme280.get_pressure() # get hPa from BME280 sensor
397 now_pressure_tuple = (time.time_ns(), 'hPa', now_pressure)
f4b2db2d
SBS
398 # Get relative humidity reading
399 now_humidity = bme280.get_humidity() # get % relative humidity from BME280 sensor
f0613e57 400 now_humidity_tuple = (time.time_ns(), '%', now_humidity)
f4b2db2d
SBS
401 # Calculate absolute humidity reading
402 raw_temp_k = 273.15 + raw_temp; # convert sensor temp from degC to K
403 now_pressure_pa = now_pressure * 100; # convert sensor pressure from hPa to Pa
eba03e3f
SBS
404 now_humidity_abs = rel_to_abs(raw_temp_k, now_pressure_pa, now_humidity); # calc kg/kg abs humidity
405 now_humidity_abs_gkg = now_humidity_abs * 1000;
406 now_humidity_abs_gkg_tuple = (time.time_ns(), 'g/kg', now_humidity_abs_gkg);
3905a56d
SBS
407 # Calculate dew point temperature
408 now_humidity_dpt = rel_to_dpt(raw_temp_k, now_pressure_pa, now_humidity); # calc K dpt
409 now_humidity_dpt_c = now_humidity_dpt - 273.15;
410 now_humidity_dpt_c_tuple = (time.time_ns(), '°C', now_humidity_dpt_c);
f0613e57
SBS
411 # Get light reading
412 proximity = ltr559.get_proximity() # get proximity reading
413 if proximity < 10:
414 now_illuminance = ltr559.get_lux() # get lux reading from LTR-559 sensor if nothing is nearby
415 now_illuminance_tuple = (time.time_ns(), 'lux', now_illuminance)
416 poll_time_ns_end = time.time_ns()
417 #print('DEBUG:poll time (s):' + str((poll_time_ns_end - poll_time_ns_start)/1000000000))
418
419
420def dateIso8601Str():
421 nowUTC = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat()
422 return str(nowUTC)
423
424def medianSubset(listIn: list = [], listOutLen: int = 0) -> list:
425 # Input: int: listOutLen: quantity of elements in output list
426 # list: listIn: input list consisting of integers or floats
427 # Output: list: ints/floats of specified size
428 # Ref/Attrib: PEP 3107 typing https://stackoverflow.com/a/21384492
429 # Version: 0.1.0
430 #print('DEBUG:listOutLen:' + str(listOutLen))
431 #print('DEBUG:listIn:' + str(listIn))
432
433 # Exit for invalid input
434 if not isinstance(listOutLen, int):
435 raise ValueError('ERROR:Not a valid int:' + str(listOutLen))
436 else:
437 if not listOutLen > 0:
438 raise ValueError('ERROR:Invalid value:' + str(listOutLen))
439 if not isinstance(listIn, list):
440 raise ValueError('ERROR:Not a valid list:' + str(listOutLen))
441 if not all([( (isinstance(x,int)) or (isinstance(x,float)) ) for x in listIn]):
442 raise ValueError('ERROR:Input list contains something besides integers or floating point numbers.')
443
444 # Initialize listOut
445 listOut = [None] * listOutLen
446 #print('DEBUG:listOut:' + str(listOut))
447
448 # Calc listIn length
449 listInLen = len(listIn)
450 #print('DEBUG:listInLen:' + str(listInLen))
451
452 # Calc subset length float
453 subsetLenFloat = ( (max([listInLen,listOutLen]) - 1) /min([listInLen,listOutLen]))
454 subsetIndRatio = ( (listInLen)/(listOutLen) )
455 #print('DEBUG:subsetLenFloat: %.5f' % subsetLenFloat)
456 #print('DEBUG:subsetLenFloat2: %.5f' % subsetIndRatio)
457
458 # Iterate for each element in listOut
459 for i_out in range(listOutLen):
460 #print('DEBUG:i_out:' + str(i_out))
461 ## Decide to expand or reduce listIn to produce listOut
462 if listInLen > listOutLen:
463 ### reduce listIn to listOut
464 #print('DEBUG:listOutLen:' + str(listOutLen))
465 #print('DEBUG:listInLen:' + str(listInLen))
466 if i_out == 0:
467 #### Initialize subsetIndLo in first loop
468 subsetIndLo = int(0)
469 #print('DEBUG:subsetIndLo:' + str(subsetIndLo))
470 #print('DEBUG:i_out:' + str(i_out))
471 #### Calc indices of i_out'th subset of listIn
472 subsetIndHi = (listInLen - 1) * (i_out + 1) // listOutLen
473 subsetLen = subsetIndHi - subsetIndLo + 1
474 #print('DEBUG:subsetIndLo:' + str(subsetIndLo))
475 #print('DEBUG:subsetIndHi:' + str(subsetIndHi))
476 #print('DEBUG:subsetLen:' + str(subsetLen))
477 #### Extract subset from listIn using indices inclusively
478 subset = listIn[ int(subsetIndLo) : int(subsetIndHi)+1 ]
479 #print('DEBUG:subset:' + str(subset))
480 #### Calculate median for subset
481 subsetMedian = np.median(subset)
482 #print('DEBUG:subset median:' + str(subsetMedian))
483 #### Set listOut element
484 listOut[i_out] = subsetMedian
485 #### Housekeeping
486 ##### Update subsetIndLo for next loop
487 subsetIndLo = subsetIndHi + 1
488 #print('DEBUG:Updated subsetIndLo:' + str(subsetIndLo))
489 elif listOutLen > listInLen:
490 ### Expand listIn to listOut
491 #print('DEBUG:listOutLen:' + str(listOutLen))
492 #print('DEBUG:listInLen:' + str(listInLen))
493 #### Identify index list of lists mapping listIn to ListOut
494 expandIndex = int(i_out / subsetLenFloat)
495 expandIndex = min([expandIndex,(listInLen - 1)])
496 #print('DEBUG:expandIndex:' + str(expandIndex))
497 listOut[i_out] = listIn[expandIndex]
498 #print('DEBUG:listOut[i_out]:' + str(listOut[i_out]))
499 elif listOutLen == listInLen:
500 listOut = listIn
501 #print('DEBUG:end for loop===========')
502 return listOut
503
504def updateBuffer():
505 global now_temp_tuple
506 global now_pressure_tuple
507 global now_humidity_tuple
eba03e3f 508 global now_humidity_abs_gkg_tuple
3905a56d 509 global now_humidity_dpt_c_tuple
f0613e57
SBS
510 global now_illuminance_tuple
511 global varLenBuffer
512 global fixLenBuffer
513 global fixLenBufferFlt
514 global span_time_h
515 #print('DEBUG:This is the updateBuffer() function.')
516 #print('DEBUG:===========================================================')
517 #print('DEBUG:===========================================================')
518 # Capture new sensor tuples
519 pollSensors()
520 #print('DEBUG:now_temp_tuple:' + str(now_temp_tuple))
521 #print('DEBUG:now_pressure_tuple:' + str(now_pressure_tuple))
522 #print('DEBUG:now_humidity_tuple:' + str(now_humidity_tuple))
eba03e3f 523 #print('DEBUG:now_humidity_abs_gkg_tuple:' + str(now_humidity_abs_gkg_tuple))
3905a56d 524 #print('DEBUG:now_humidity_dpt_c_tuple:' + str(now_humidity_dpt_c_tuple))
f0613e57
SBS
525 #print('DEBUG:now_illuminance_tuple:' + str(now_illuminance_tuple))
526
527 # Append new sensor tuples to varying-length buffer
528 ## Temperature
529 varLenBuffer[variables[0]].append(now_temp_tuple)
530 ## Pressure
531 varLenBuffer[variables[1]].append(now_pressure_tuple)
f4b2db2d 532 ## Relative Humidity
f0613e57 533 varLenBuffer[variables[2]].append(now_humidity_tuple)
f4b2db2d 534 ## Absolute Humidity
eba03e3f 535 varLenBuffer[variables[3]].append(now_humidity_abs_gkg_tuple)
3905a56d
SBS
536 ## Dew Point Temperature
537 varLenBuffer[variables[4]].append(now_humidity_dpt_c_tuple)
f0613e57 538 ## Illuminance
3905a56d 539 varLenBuffer[variables[5]].append(now_illuminance_tuple)
f0613e57
SBS
540 #print('DEBUG:varLenBuffer:' + str(varLenBuffer))
541
542 # Trim outdated sensor tuples from varying-length buffer
543 ## iterate through each tuple list and remove old tuples
544 varLenBufferTemp = []
545 for v in variables:
546 #varLenBufferTemp = varLenBuffer[v].copy()
547 now_time_ns = time.time_ns() # get ns timestamp of now
548 thn_time_ns = varLenBuffer[v][0][0] # get ns timestamp of earliest tuple
549 dif_time_ns = now_time_ns - thn_time_ns # calc nanosecond difference
550 #print('DEBUG:varLenBufferTTL:' + str(varLenBufferTTL))
551 #print('DEBUG:now:' + str(now_time_ns))
552 #print('DEBUG:thn:' + str(thn_time_ns))
553 #print('DEBUG:dif:' + str(dif_time_ns))
554 #print('DEBUG:dif(s):' + str(dif_time_ns / 1000000000))
555 if dif_time_ns > varLenBufferTTL:
556 varLenBuffer[v].pop(0) # discard earliest tuple if age > varLenBufferTTL
557 print('DEBUG:Len of varLenBuffer[' + str(v) + ']:' + str(len(varLenBuffer[v])))
558 #print('DEBUG:*******************************************')
559 #print('DEBUG:varLenBuffer[variables[' + str(v) + ']]:' + str(varLenBuffer[v]))
560 #print('DEBUG:*******************************************')
561
562 # Calculate buffer time span in hours
563 ## Get earliest timestamp (use temperature tuples)
564 first_time_ns = varLenBuffer[variables[0]][0][0]
565 last_time_ns = varLenBuffer[variables[0]][-1][0]
566 span_time_ns = int(last_time_ns - first_time_ns)
567 span_time_h = float(span_time_ns / (10**9*60*60)) # nanoseconds to hours
568
569 # Convert tuple buffer into float buffer
570 for v in variables:
571 varLenBufferFlt[v].clear() # clear old float list
572 #print('DEBUG:v:' + str(v))
573 for t in varLenBuffer[v]:
574 #print('DEBUG:t:' + str(t))
575 #print('DEBUG:t[2]:' + str(t[2]))
576 #print('DEBUG:------------------------------------------')
577 #print('DEBUG:varLenBufferFlt[' + str(v) + ']:' + str(varLenBufferFlt[v]))
578 #print('DEBUG:------------------------------------------')
579 if isinstance(t[2], float):
580 varLenBufferFlt[v].append(float(t[2])) # build new float list
581 else:
582 varLenBufferFlt[v].append(float(-273)) # add obvious zero otherwise
583 #print('DEBUG:varLenBufferFlt[' + str(v) + ']:' + str(varLenBufferFlt[v]))
584
585 # Compress/expand buffer to fixed-length buffer
586 for v in variables:
587 #print('DEBUG:varLenBufferFlt[0]:' + str(varLenBufferFlt[variables[0]]))
588 fixLenBuffer[v] = medianSubset(varLenBufferFlt[v], WIDTH)
589 print('DEBUG:Len of fixLenBuffer[' + str(v) + ']:' + str(len(fixLenBuffer[v])))
590 #print('DEBUG:fixLenBuffer[' + str(v) + ']:' + str(fixLenBuffer[v]))
591
592
593# The main loop
594try:
595 # Schedule tasks
596 # schedule.every(1).second.do(updateBuffer)
597 schedule.every().minute.at(":00").do(updateBuffer)
598 # schedule.every().minute.at(":05").do(updateBuffer)
599 # schedule.every().minute.at(":10").do(updateBuffer)
600 # schedule.every().minute.at(":15").do(updateBuffer)
601 # schedule.every().minute.at(":20").do(updateBuffer)
602 # schedule.every().minute.at(":25").do(updateBuffer)
603 # schedule.every().minute.at(":30").do(updateBuffer)
604 # schedule.every().minute.at(":35").do(updateBuffer)
605 # schedule.every().minute.at(":40").do(updateBuffer)
606 # schedule.every().minute.at(":45").do(updateBuffer)
607 # schedule.every().minute.at(":50").do(updateBuffer)
608 # schedule.every().minute.at(":55").do(updateBuffer)
699a70ff
SBS
609 pollSensors() # initial run to start up sensors
610 time.sleep(1) # pause to give sensors time to initialize
f0613e57 611 updateBuffer() # initial run
699a70ff 612
f0613e57
SBS
613 while True:
614 proximity = ltr559.get_proximity()
615
616 # If the proximity crosses the threshold, toggle the display mode
617 if proximity > 1500 and time.time() - last_page > delay:
618 mode += 1
619 mode %= len(variables)
620 last_page = time.time()
621
622 # Run scheduled tasks
623 schedule.run_pending()
624
625 # One display mode for each variable
626 if mode == 0:
627 # variable = "temperature"
628 ## run function to display latest temp values in variables[mode] list
629 unit = "°C"
630 cpu_temp = get_cpu_temperature()
631 # Smooth out with some averaging to decrease jitter
632 cpu_temps = cpu_temps[1:] + [cpu_temp]
633 avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps))
634 raw_temp = bme280.get_temperature()
635 data = raw_temp - ((avg_cpu_temp - raw_temp) / factor)
636 #display_text(variables[mode], data, unit)
637 display_text2(variables[mode],data,unit,fixLenBuffer)
638
639 if mode == 1:
640 # variable = "pressure"
641 unit = "hPa"
642 data = bme280.get_pressure()
643 #display_text(variables[mode], data, unit)
644 display_text2(variables[mode],data,unit,fixLenBuffer)
645
646 if mode == 2:
647 # variable = "humidity"
648 unit = "%"
649 data = bme280.get_humidity()
650 #display_text(variables[mode], data, unit)
651 display_text2(variables[mode],data,unit,fixLenBuffer)
f4b2db2d 652
f0613e57 653 if mode == 3:
f4b2db2d
SBS
654 # variable = "humidity_abs"
655 unit = "g/kg"
656 raw_temp = bme280.get_temperature() # get °C from BME280 sensor
657 raw_temp_k = 273.15 + raw_temp; # convert sensor temp from degC to K
658 now_pressure = bme280.get_pressure() # get hPa from BME280 sensor
659 now_pressure_pa = now_pressure * 100; # convert sensor pressure from hPa to Pa
660 now_humidity = bme280.get_humidity() # get % relative humidity from BME280 sensor
eba03e3f
SBS
661 now_humidity_abs = rel_to_abs(raw_temp_k,now_pressure_pa,now_humidity); # calc [kg water / kg dry air] abs humidity
662 now_humidity_abs_gkg = now_humidity_abs * 1000; # convert kg/kg to g/kg abs humidity
663 data = now_humidity_abs_gkg;
664 # print('DEBUG:raw_temp:' + str(raw_temp));
665 # print('DEBUG:raw_temp_k:' + str(raw_temp_k));
666 # print('DEBUG:now_pressure:' + str(now_pressure));
667 # print('DEBUG:now_pressure_pa:' + str(now_pressure_pa));
668 # print('DEBUG:now_humidity:' + str(now_humidity));
3905a56d 669 # print('DEBUG:now_humidity_abs_gkg:' + str(now_humidity_abs_gkg));
f4b2db2d 670 display_text2(variables[mode],data,unit,fixLenBuffer)
3905a56d 671
f4b2db2d 672 if mode == 4:
3905a56d
SBS
673 # variable = "humidity_abs"
674 unit = "°C"
675 raw_temp = bme280.get_temperature() # get °C from BME280 sensor
676 raw_temp_k = 273.15 + raw_temp; # convert sensor temp from degC to K
677 now_pressure = bme280.get_pressure() # get hPa from BME280 sensor
678 now_pressure_pa = now_pressure * 100; # convert sensor pressure from hPa to Pa
679 now_humidity = bme280.get_humidity() # get % relative humidity from BME280 sensor
680 now_humidity_dpt = rel_to_dpt(raw_temp_k,now_pressure_pa,now_humidity); # calc K dpt humidity
681 now_humidity_dpt_c = now_humidity_dpt - 273.15; # convert K to °C dpt humidity
682 data = now_humidity_dpt_c;
683 # print('DEBUG:raw_temp:' + str(raw_temp));
684 # print('DEBUG:raw_temp_k:' + str(raw_temp_k));
685 # print('DEBUG:now_pressure:' + str(now_pressure));
686 # print('DEBUG:now_pressure_pa:' + str(now_pressure_pa));
687 # print('DEBUG:now_humidity:' + str(now_humidity));
688 # print('DEBUG:now_humidity_dpt_c:' + str(now_humidity_dpt_c));
689 display_text2(variables[mode],data,unit,fixLenBuffer)
690
691 if mode == 5:
f0613e57
SBS
692 # variable = "light"
693 unit = "Lux"
694 if proximity < 10:
695 data = ltr559.get_lux()
696 else:
697 data = 1
698 #display_text(variables[mode], data, unit)
699 display_text2(variables[mode],data,unit,fixLenBuffer)
700
701
702
703# Exit cleanly
704except KeyboardInterrupt:
705 sys.exit(0)