From 90ea7edcf11eee538c6dffe4ceb93611cbf6a6aa Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Sun, 7 May 2017 15:38:20 -0400 Subject: [PATCH] Minor overhaul for LCD and BME Add BME280 sensor to tank top Add lcdproc interface which scrapes pidmonitor logs for display Other tweaks around town --- bin/BME280.py | 213 ++++++++++++++++++++++++++++++++++++ bin/lcdproc.expect | 117 ++++++++++++++++++++ bin/monitor-log-cgi.sh | 2 +- bin/monitor.py | 91 --------------- bin/pidloop.py | 4 +- bin/pidmonitor.py | 70 ++++++++++-- bin/rrdtool-creates.sh | 20 ++++ data/.gitignore | 1 - runit/pi-sc-lcd/run | 2 + runit/pi-sc-monitor/log/run | 4 +- runit/pi-sc-monitor/run | 2 +- 11 files changed, 420 insertions(+), 106 deletions(-) create mode 100644 bin/BME280.py create mode 100755 bin/lcdproc.expect delete mode 100755 bin/monitor.py create mode 100755 runit/pi-sc-lcd/run diff --git a/bin/BME280.py b/bin/BME280.py new file mode 100644 index 0000000..7abcecf --- /dev/null +++ b/bin/BME280.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015 Andrey Koryagin +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import smbus +import time + +# BME280 default address. +BME280_I2CADDR = 0x76 + +# BME280 Registers +BME280_CONTROL_MEAS = 0xF4 +BME280_CONTROL_HUM = 0xF2 + +BME280_CONFIG = 0xF5 +BME280_PRESSURE = 0xF7 +BME280_TEMP = 0xFA +BME280_HUMIDITY = 0xFD + +BME280_DIG_T1 = 0x88 +BME280_DIG_T2 = 0x8A +BME280_DIG_T3 = 0x8C +BME280_DIG_P1 = 0x8E +BME280_DIG_P2 = 0x90 +BME280_DIG_P3 = 0x92 +BME280_DIG_P4 = 0x94 +BME280_DIG_P5 = 0x96 +BME280_DIG_P6 = 0x98 +BME280_DIG_P7 = 0x9A +BME280_DIG_P8 = 0x9C +BME280_DIG_P9 = 0x9E +BME280_DIG_H1 = 0xA1 +BME280_DIG_H2 = 0xE1 +BME280_DIG_H3 = 0xE3 +BME280_DIG_H4 = 0xE4 +BME280_DIG_H5 = 0xE5 +BME280_DIG_H6 = 0xE7 + +# Oversampling Setting +BME280_OVERS_T1 = 0x20 +BME280_OVERS_T2 = 0x40 +BME280_OVERS_T4 = 0x60 +BME280_OVERS_T8 = 0x80 +BME280_OVERS_T16 = 0xA0 + +BME280_OVERS_P1 = 0x04 +BME280_OVERS_P2 = 0x08 +BME280_OVERS_P4 = 0x0C +BME280_OVERS_P8 = 0x10 +BME280_OVERS_P16 = 0x14 + +BME280_OVERS_H1 = 0x01 +BME280_OVERS_H2 = 0x02 +BME280_OVERS_H4 = 0x03 +BME280_OVERS_H8 = 0x04 +BME280_OVERS_H16 = 0x05 + +# Power Modes. +# This lib uses NORMAL mode only! +#BME280_SLEEP_MODE = 0x00 +#BME280_FORCED_MODE = 0x01 +BME280_NORMAL_MODE = 0x03 + +BME280_TSB_0_5 = 0x00 +BME280_TSB_62_5 = 0x20 +BME280_TSB_125 = 0x40 +BME280_TSB_250 = 0x60 +BME280_TSB_500 = 0x80 +BME280_TSB_1000 = 0xA0 +BME280_TSB_2000 = 0xC0 +BME280_TSB_4000 = 0xE0 + +BME280_FILTER_OFF = 0x00 +BME280_FILTER_COEFFICIENT2 = 0x04 +BME280_FILTER_COEFFICIENT4 = 0x08 +BME280_FILTER_COEFFICIENT8 = 0x0C +BME280_FILTER_COEFFICIENT16 = 0x10 + +BME280_SPI_OFF = 0x00 +BME280_SPI_ON = 0x01 + +BME280_CONTROL_MEAS_SET = (BME280_OVERS_T16 | BME280_OVERS_P16 | BME280_NORMAL_MODE) +BME280_CONTROL_HUM_SET = BME280_OVERS_H2 +BME280_CONFIG_SET = (BME280_TSB_0_5 | BME280_FILTER_COEFFICIENT16 | BME280_SPI_OFF) + +class BME280(object): + def __init__(self, port=1, address=BME280_I2CADDR): + self.bus = smbus.SMBus(port) + self.address = address + + # Read calibration values + self.dig_t1 = self.read_word(BME280_DIG_T1) # Unsigned + self.dig_t2 = self.read_word_sign(BME280_DIG_T2) + self.dig_t3 = self.read_word_sign(BME280_DIG_T3) + self.dig_p1 = self.read_word(BME280_DIG_P1) # Unsigned + self.dig_p2 = self.read_word_sign(BME280_DIG_P2) + self.dig_p3 = self.read_word_sign(BME280_DIG_P3) + self.dig_p4 = self.read_word_sign(BME280_DIG_P4) + self.dig_p5 = self.read_word_sign(BME280_DIG_P5) + self.dig_p6 = self.read_word_sign(BME280_DIG_P6) + self.dig_p7 = self.read_word_sign(BME280_DIG_P7) + self.dig_p8 = self.read_word_sign(BME280_DIG_P8) + self.dig_p9 = self.read_word_sign(BME280_DIG_P9) + + self.dig_h1 = self.read_byte(BME280_DIG_H1) # unsigned char + self.dig_h2 = self.read_word_sign(BME280_DIG_H2) + self.dig_h3 = self.read_byte(BME280_DIG_H3) # unsigned char + + self.dig_h4 = (self.read_byte(BME280_DIG_H4) << 24) >> 20 + self.dig_h4 = self.dig_h4 | self.read_byte(BME280_DIG_H4+1) & 0x0F + + self.dig_h5 = (self.read_byte(BME280_DIG_H5+1) << 24) >> 20 + self.dig_h5 = self.dig_h5 | (self.read_byte(BME280_DIG_H5) >> 4) & 0x0F + + self.dig_h6 = self.read_byte(BME280_DIG_H6) # signed char + if self.dig_h6 > 127: + self.dig_h6 = 127-self.dig_h6 + + # Set Configuration + self.write_byte(BME280_CONFIG, BME280_CONFIG_SET) + self.write_byte(BME280_CONTROL_HUM, BME280_CONTROL_HUM_SET) + self.write_byte(BME280_CONTROL_MEAS, BME280_CONTROL_MEAS_SET) + + def get_data(self): + adc_t = self.read_adc_long(BME280_TEMP) + adc_p = self.read_adc_long(BME280_PRESSURE) + adc_h = self.read_adc_word(BME280_HUMIDITY) + + var1 = (adc_t/16384.0 - self.dig_t1/1024.0) * self.dig_t2; + var2 = ((adc_t/131072.0 - self.dig_t1/8192.0) * (adc_t/131072.0 - self.dig_t1/8192.0)) * self.dig_t3; + t_fine = (var1 + var2); + temperature = round((t_fine / 5120.0), 2); + + var1 = (t_fine/2.0) - 64000.0; + var2 = var1 * var1 * self.dig_p6 / 32768.0; + var2 = var2 + var1 * self.dig_p5 * 2.0; + var2 = (var2/4.0)+(self.dig_p4 * 65536.0); + var1 = (self.dig_p3 * var1 * var1 / 524288.0 + self.dig_p2 * var1) / 524288.0; + var1 = (1.0 + var1 / 32768.0)*self.dig_p1; + + # Avoid exception caused by division by zero + if (var1 == 0.0): + return -1 + + p = 1048576.0 - adc_p; + p = (p - (var2 / 4096.0)) * 6250.0 / var1; + var1 = self.dig_p9 * p * p / 2147483648.0; + var2 = p * self.dig_p8 / 32768.0; + pressure = round((p + (var1 + var2 + self.dig_p7) / 16.0), 2); + + var_H = t_fine - 76800.0 + var_H = (adc_h-(self.dig_h4*64.0+self.dig_h5 / 16384.0 * var_H))*(self.dig_h2/65536.0 * (1.0 + self.dig_h6/67108864.0 * var_H * (1.0+self.dig_h3/67108864.0 * var_H))) + humidity = round(var_H * (1.0 - self.dig_h1*var_H/524288.0), 2) + if (humidity > 100.0): + humidity = 100.0 + else: + if(humidity < 0.0): + humidity = 0.0 + + return {'t':temperature, 'p':pressure, 'h':humidity} + + def get_altitude(self, pressure): + temp = pressure/101325; + temp = 1-pow(temp, 0.19029); + altitude = round(44330*temp, 3); + return altitude; + + def read_byte(self, adr): + return self.bus.read_byte_data(self.address, adr) + + def read_word(self, adr): + # ATANTION! Joke from Bosch! LBS before HBS. For calibration registers only! + lbs = self.bus.read_byte_data(self.address, adr) + hbs = self.bus.read_byte_data(self.address, adr+1) + return (hbs << 8) + lbs + + def read_word_sign(self, adr): + val = self.read_word(adr) + if (val >= 0x8000): + return -((65535 - val) + 1) + else: + return val + + def read_adc_long(self, adr): + mbs = self.bus.read_byte_data(self.address, adr) + lbs = self.bus.read_byte_data(self.address, adr+1) + xbs = self.bus.read_byte_data(self.address, adr+2) + val = (mbs << 16) + (lbs << 8) + xbs + val = (val >> 4) + return val + + def read_adc_word(self, adr): + mbs = self.bus.read_byte_data(self.address, adr) + lbs = self.bus.read_byte_data(self.address, adr+1) + val = (mbs << 8) + lbs + return val + + def write_byte(self, adr, byte): + self.bus.write_byte_data(self.address, adr, byte) diff --git a/bin/lcdproc.expect b/bin/lcdproc.expect new file mode 100755 index 0000000..28235e6 --- /dev/null +++ b/bin/lcdproc.expect @@ -0,0 +1,117 @@ +#!/usr/bin/expect + +proc lcdcmd {sid cmd} { + send -i ${sid} "${cmd}\n" + while { true } { + expect { + -i ${sid} -re "^[lindex [split "${cmd}"] 0]\[^\n\]*\r\n" { + while { true } { + expect { + -i ${sid} -re "^success\[^\n\]*\n" { return } + -i ${sid} -re "^\r\n" { } + -i ${sid} -re "^listen .*\n" {} + -i ${sid} -re "^ignore .*\n" {} + timeout { error "LCD protocol failure while waiting for success?" } + } + } + } + -i ${sid} -re "^listen .*\n" {} + -i ${sid} -re "^ignore .*\n" {} + timeout { error "LCD protocol failure while waiting for echo?" } + } + } +} + +proc spawnlcd {host port} { + spawn socat STDIO "TCP:${host}:${port}" + send -i ${spawn_id} "hello\n" + expect { + -i ${spawn_id} -re "^hello\r\nconnect .*\r\n" { return ${spawn_id} } + timeout { error "LCD protocol failure while sending hello?" } + } +} + +spawn tail --retry --follow=name /run/snakecontrol/monitor.log/current +set logsid ${spawn_id} + +set sid [spawnlcd "localhost" "13666"] +lcdcmd ${sid} "client_set -name test" + +lcdcmd ${sid} "screen_add 1" +lcdcmd ${sid} "screen_set 1 -name Vivarium" +lcdcmd ${sid} "screen_set 1 -priority hidden" +lcdcmd ${sid} "screen_set 1 -cursor off" + +lcdcmd ${sid} "widget_add 1 ti title" +lcdcmd ${sid} "widget_set 1 ti Vivarium" + +lcdcmd ${sid} "widget_add 1 i_up icon" +lcdcmd ${sid} "widget_set 1 i_up 1 2 ARROW_UP" +lcdcmd ${sid} "widget_add 1 i_th string" +lcdcmd ${sid} "widget_set 1 i_th 2 2 T" +lcdcmd ${sid} "widget_add 1 v_ut string" +lcdcmd ${sid} "widget_add 1 i_uh string" +lcdcmd ${sid} "widget_set 1 i_uh 8 2 H" +lcdcmd ${sid} "widget_add 1 v_uh string" + +lcdcmd ${sid} "widget_add 1 i_l icon" +lcdcmd ${sid} "widget_set 1 i_l 1 3 ARROW_LEFT" + +lcdcmd ${sid} "widget_add 1 i_r icon" +lcdcmd ${sid} "widget_set 1 i_r 1 4 ARROW_RIGHT" + +lcdcmd ${sid} "widget_add 1 i_lh icon" +lcdcmd ${sid} "widget_set 1 i_lh 2 3 ARROW_DOWN" +lcdcmd ${sid} "widget_add 1 v_lh string" + +lcdcmd ${sid} "widget_add 1 i_rh icon" +lcdcmd ${sid} "widget_set 1 i_rh 2 4 ARROW_DOWN" +lcdcmd ${sid} "widget_add 1 v_rh string" + +lcdcmd ${sid} "widget_add 1 i_lt icon" +lcdcmd ${sid} "widget_set 1 i_lt 9 3 ARROW_UP" +lcdcmd ${sid} "widget_add 1 v_lt string" + +lcdcmd ${sid} "widget_add 1 i_rt icon" +lcdcmd ${sid} "widget_set 1 i_rt 9 4 ARROW_UP" +lcdcmd ${sid} "widget_add 1 v_rt string" + +lcdcmd ${sid} "widget_add 1 v_ldmxl string" +lcdcmd ${sid} "widget_set 1 v_ldmxl 17 3 @" +lcdcmd ${sid} "widget_add 1 v_ldmx string" + +lcdcmd ${sid} "widget_add 1 v_rdmxl string" +lcdcmd ${sid} "widget_set 1 v_rdmxl 17 4 @" +lcdcmd ${sid} "widget_add 1 v_rdmx string" + +lcdcmd ${sid} "screen_set 1 -priority info" + +proc drawtemp { scr wid x y } { + global sid + global expect_out + set t [expr round(${expect_out(1,string)}*10.0)/10.0] + lcdcmd ${sid} "widget_set ${scr} ${wid} ${x} ${y} ${t}" +} + +set datapfx "^\[^ \]* DATA: " +set datare "(\[^ \r\n]*)" + +while { true } { + expect { + -i ${logsid} -re "${datapfx}hide-near.* temp=${datare}.*\n" { drawtemp 1 "v_lh" 4 3 } + -i ${logsid} -re "${datapfx}hide-far.* temp=${datare}.*\n" { drawtemp 1 "v_rh" 4 4 } + -i ${logsid} -re "${datapfx}tank-near.* temp=${datare}.*\n" { drawtemp 1 "v_lt" 11 3 } + -i ${logsid} -re "${datapfx}tank-far.* temp=${datare}.*\n" { drawtemp 1 "v_rt" 11 4 } + -i ${logsid} -re "${datapfx}tank-top.* temp=${datare}.*\n" { drawtemp 1 "v_ut" 3 2 } + -i ${logsid} -re "${datapfx}tank-top.* humid=${datare}.*\n" { drawtemp 1 "v_uh" 9 2 } + -i ${logsid} -re "${datapfx}dmx-near.* dmx=${datare}.*\n" { + lcdcmd ${sid} "widget_set 1 v_ldmx 18 3 ${expect_out(1,string)}" + } + -i ${logsid} -re "${datapfx}dmx-mid.* dmx=${datare}.*\n" { + lcdcmd ${sid} "widget_set 1 v_rdmx 18 4 ${expect_out(1,string)}" + } + -i ${logsid} -re "\n" {} + -i ${sid} -re "^listen .*\n" {} + -i ${sid} -re "^ignore .*\n" {} + } +} diff --git a/bin/monitor-log-cgi.sh b/bin/monitor-log-cgi.sh index dbfa8c3..dc2073e 100755 --- a/bin/monitor-log-cgi.sh +++ b/bin/monitor-log-cgi.sh @@ -2,4 +2,4 @@ echo "Content-Type: text/plain" echo -exec tail -n 40 /home/pi/sc/data/monitor.log/current +exec tail -n 40 /run/snakecontrol/monitor.log/current diff --git a/bin/monitor.py b/bin/monitor.py deleted file mode 100755 index 087db67..0000000 --- a/bin/monitor.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/python - -# Inspired by code found at http://wannabe.guru.org/scott/hobbies/temperature/ - -import os -import rrdtool -import sched -import time - -owdev_logname = { - "/sys/bus/w1/devices/28-011620c718ee/w1_slave" : "/home/pi/sc/data/hide-far-temp.rrd", - "/sys/bus/w1/devices/28-011620f10dee/w1_slave" : "/home/pi/sc/data/hide-near-temp.rrd", - "/sys/bus/w1/devices/28-02161e26acee/w1_slave" : "/home/pi/sc/data/tank-far-temp.rrd", - "/sys/bus/w1/devices/28-03164712aaff/w1_slave" : "/home/pi/sc/data/heater-temp.rrd", - "/sys/bus/w1/devices/28-0416526de6ff/w1_slave" : "/home/pi/sc/data/tank-near-temp.rrd", -} - -owdev_thresholds_heater1 = { - "/sys/bus/w1/devices/28-011620c718ee/w1_slave" : (23.0, 25.0), # hide far - "/sys/bus/w1/devices/28-02161e26acee/w1_slave" : (21.0, 25.0), # tank air far - "/sys/bus/w1/devices/28-0416526de6ff/w1_slave" : (23.0, 27.0), # tank air near -} - -# owdev_thresholds_heater2 = { -# "/sys/bus/w1/devices/28-011620f10dee/w1_slave" : (29.0, 29.5), # hide near -# } - -def nop (*arg, **kwarg): - pass - -def with_ow_temp_fk_id(devfn, x, *arg, **kwarg): - print("WARNING: failed to read %s" % devfn) - return x - -def with_ow_temp(devfn, sk, fk, *arg, **kwarg): - with open(devfn) as devf: - devstr = devf.read() - devlines = devstr.split("\n") - if devlines[0].find("YES") > 0: - return sk(devfn,float((devlines[1].split(" ")[9])[2:]) / 1000, *arg, **kwarg) - return fk(devfn, *arg, **kwarg) - - -def check_temps(sc): - print ("check temps init") - def check(devfn, temp, threshs, desire): - # Anything too hot: turn off the heater - if (temp > threshs[devfn][1]): desire = "OFF" - # No stated preference and something too cold, turn on the heater - elif (temp <= threshs[devfn][0] and desire == None): desire = "ON" - return desire - - did_interact = False - desire = None - for devfn in owdev_thresholds_heater1: - desire = with_ow_temp(devfn, check, with_ow_temp_fk_id, owdev_thresholds_heater1, desire) - if desire is not None: - did_interact = True - print("Set heater 1 %s" % desire) - os.system("/home/pi/sc/bin/rpb.expect 4 %s | grep -A 5 -e 'Plug ' | tr -d '\015'" % desire) - - # desire = None - # for devfn in owdev_thresholds_heater2: - # desire = with_ow_temp(devfn, check, with_ow_temp_fk_id, owdev_thresholds_heater2, desire) - # if desire is not None: - # did_interact = True - # print("Set heater 2 %s" % desire) - # os.system("/home/pi/sc/bin/rpb.expect 5 %s | grep -A 5 -e 'Plug ' | tr -d '\015'" % desire) - - if not did_interact: - os.system("/home/pi/sc/bin/rpb.expect | grep -A 5 -e 'Plug ' | tr -d '\015'") - - sc.enter(300, 1, check_temps, (sc,)) - print ("check temps fini") - -def do_log_temp(devfn, temp): - print("temp log: %s => %f" % (devfn, temp)) - rrdtool.update(owdev_logname[devfn], "N:" + ("%f" % temp)) - -def read_temps(sc,itime): - for devfn in owdev_logname: with_ow_temp(devfn, do_log_temp, nop) - itime = itime + 60 - sc.enterabs(itime, 2, read_temps, (sc,itime)) - -itime = time.time() -s = sched.scheduler(time.time, time.sleep) -s.enterabs(itime, 1, check_temps, (s,)) -s.enterabs(itime, 2, read_temps, (s,itime)) - -print("Monitor starting...") -s.run() diff --git a/bin/pidloop.py b/bin/pidloop.py index 6dd8317..192bcc4 100644 --- a/bin/pidloop.py +++ b/bin/pidloop.py @@ -45,7 +45,7 @@ class PIDLoop(object): def _value (self, error, edeltasmooth) : - print ("PID LOOP CONTRIBUTIONS: p=%r d=%r i=%r" % (self.kP * error, self.kD * edeltasmooth, self.kI * self.sum_error)) + # print ("PID LOOP CONTRIBUTIONS: p=%r d=%r i=%r" % (self.kP * error, self.kD * edeltasmooth, self.kI * self.sum_error)) return (self.kP * error) + (self.kD * edeltasmooth) + (self.kI * self.sum_error) @@ -78,7 +78,7 @@ class PIDLoop(object): elif self.hard_min is not None and ov <= self.hard_min and sum_error_delta < 0: # Do not decrement error; we're already slammed up against the hard limit pass - else if sum_error_delta != 0: + elif sum_error_delta != 0: # Update sum_error and recompute the output value self.sum_error += sum_error_delta ov = self._value(error, edeltasmooth) diff --git a/bin/pidmonitor.py b/bin/pidmonitor.py index d15ca74..2603ed1 100755 --- a/bin/pidmonitor.py +++ b/bin/pidmonitor.py @@ -6,9 +6,11 @@ import os import sched import time import serial -import pidloop import rrdtool +import pidloop +import BME280 + dmxdev = serial.Serial("/dev/serial/by-id/usb-DMXking.com_DMX_USB_PRO_6A0SVM7J-if00-port0", 57600); loop_hidenear = pidloop.PIDThresh(128,26,28,0,31,33,-128) @@ -18,24 +20,70 @@ loop_hidenear.setHardMin(-128) loop_hidenear.setKP(60.0) loop_hidenear.setKI(0.004) loop_hidenear.setKD(1000.0,0.95) -loop_hidenear.sum_error = -3000.0 # XXX Initialize offset point a bit +loop_hidenear.sum_error = -7600.0 # XXX Initialize offset point a bit + +bmetop = BME280.BME280(port=1, address=0x77) -def with_ow_temp_fk_id(devfn, loop, s, *arg, **kwarg): +def with_ow_temp_fk_id2(devfn, loop, s, *arg, **kwarg): print("WARNING: failed to read %s" % devfn) return s # an ugly default -def with_ow_temp(devfn, sk, fk, *arg, **kwarg): +def with_ow_temp(cache, devfn, sk, fk, *arg, **kwarg): + if devfn in cache : + return sk(devfn, cache[devfn], *arg, **kwarg) with open(devfn) as devf: devstr = devf.read() devlines = devstr.split("\n") if devlines[0].find("YES") > 0: - return sk(devfn,float((devlines[1].split(" ")[9])[2:]) / 1000, *arg, **kwarg) + val = float((devlines[1].split(" ")[9])[2:]) / 1000 + cache[devfn] = val + return sk(devfn, val, *arg, **kwarg) return fk(devfn, *arg, **kwarg) def check_temps(sc): sc.enter(10, 1, check_temps, (sc,)) - def check(devfn, temp, loop, s, offset, rrd): + cache = {} + + def log(devfn, temp, logname, rrd, kw="temp"): + rrdtool.update(rrd, "N:" + ("%f" % temp)) + print ("DATA: %s %s=%s" % (logname, kw, temp)) + + def logfail(devfn, name, *arg): + print ("FAIL: %s %s %s" % (name, devfn, arg)) + + # BME280 atop + try: + top = bmetop.get_data() + log("bme280-77", top['t'], "tank-top", "/home/pi/sc/data/tank-top-temp.rrd") + log("bme280-77", top['h'], "tank-top", "/home/pi/sc/data/tank-top-humid.rrd", kw="humid") + log("bme280-77", top['p'], "tank-top", "/home/pi/sc/data/tank-top-press.rrd", kw="press") + except Exception, e: + logfail("bme280-77", "hide-top") + + # Log (and populate cache, while we're at it) + with_ow_temp(cache, + "/sys/bus/w1/devices/28-011620f10dee/w1_slave", log, logfail, + "hide-near", "/home/pi/sc/data/hide-near-temp.rrd") + + with_ow_temp(cache, + "/sys/bus/w1/devices/28-011620c718ee/w1_slave", log, logfail, + "hide-far", "/home/pi/sc/data/hide-far-temp.rrd") + + with_ow_temp(cache, + "/sys/bus/w1/devices/28-02161e26acee/w1_slave", log, logfail, + "tank-far", "/home/pi/sc/data/tank-far-temp.rrd") + + with_ow_temp(cache, + "/sys/bus/w1/devices/28-03164712aaff/w1_slave", log, logfail, + "heater-near", "/home/pi/sc/data/heater-temp.rrd") + + with_ow_temp(cache, + "/sys/bus/w1/devices/28-0416526de6ff/w1_slave", log, logfail, + "tank-near", "/home/pi/sc/data/tank-near-temp.rrd") + + + def checkpid(devfn, temp, loop, s, offset, rrd, logname): desire = 128.0 + loop.update(temp, time.time()) if desire < 0 : @@ -46,8 +94,10 @@ def check_temps(sc): loop.output = 255 rrdtool.update(rrd, "N:" + ("%f" % desire)) + dmx = int(desire+0.5) + print("DATA: %s dmx=%s" % (logname, dmx)) - return s[:offset] + chr(int(desire+0.5)) + s[offset+1:] + return s[:offset] + chr(dmx) + s[offset+1:] # DMX conttrol string; initialize to all channels full off # 7E -- header @@ -60,8 +110,10 @@ def check_temps(sc): s = "\x7E\x06\x03\x00\x00\x00\x00\xE7" # Drive loop - s = with_ow_temp("/sys/bus/w1/devices/28-011620f10dee/w1_slave", - check, with_ow_temp_fk_id, loop_hidenear, s, 5, "/home/pi/sc/data/hide-near-dmx.rrd") + s = with_ow_temp(cache, "/sys/bus/w1/devices/28-011620f10dee/w1_slave", + checkpid, with_ow_temp_fk_id2, loop_hidenear, s, 5, "/home/pi/sc/data/hide-near-dmx.rrd", "dmx-near") + + # Log some other probes print ("check temps fini: out=%r lhn=(%s)" % (s, loop_hidenear)) assert(dmxdev.write(s) == 8) diff --git a/bin/rrdtool-creates.sh b/bin/rrdtool-creates.sh index 78bc5fb..eb38330 100755 --- a/bin/rrdtool-creates.sh +++ b/bin/rrdtool-creates.sh @@ -6,6 +6,7 @@ RRDS=( hide-far-temp tank-near-temp tank-far-temp + tank-top-temp ) ARGS=( @@ -39,3 +40,22 @@ ARGS=( for rrd in ${=RRDS[@]}; do rrdtool create /home/pi/sc/data/${rrd}.rrd ${=ARGS[@]} done + +rrdtool create /home/pi/sc/data/tank-top-humid.rrd \ + --no-overwrite \ + --step 60 \ + DS:humid:GAUGE:900:-5:105 \ + RRA:AVERAGE:0.5:6:525600 \ + RRA:AVERAGE:0.25:360:87600 \ + RRA:MIN:0.025:360:87600 \ + RRA:MAX:0.025:360:87600 + + +rrdtool create /home/pi/sc/data/tank-top-press.rrd \ + --no-overwrite \ + --step 60 \ + DS:press:GAUGE:900:-1000:120000 \ + RRA:AVERAGE:0.5:6:525600 \ + RRA:AVERAGE:0.25:360:87600 \ + RRA:MIN:0.025:360:87600 \ + RRA:MAX:0.025:360:87600 diff --git a/data/.gitignore b/data/.gitignore index a46a4f0..09173cc 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -1,2 +1 @@ *.rrd -monitor.log diff --git a/runit/pi-sc-lcd/run b/runit/pi-sc-lcd/run new file mode 100755 index 0000000..53844d8 --- /dev/null +++ b/runit/pi-sc-lcd/run @@ -0,0 +1,2 @@ +#!/bin/sh +exec chpst -u pi:pi expect /home/pi/sc/bin/lcdproc.expect >/dev/null diff --git a/runit/pi-sc-monitor/log/run b/runit/pi-sc-monitor/log/run index e6b7e17..da5928d 100755 --- a/runit/pi-sc-monitor/log/run +++ b/runit/pi-sc-monitor/log/run @@ -1,2 +1,4 @@ #!/bin/sh -exec chpst -u pi svlogd -tt /home/pi/sc/data/monitor.log +mkdir -p /run/snakecontrol/monitor.log +chown -R pi:pi /run/snakecontrol +exec chpst -u pi svlogd -tt /run/snakecontrol/monitor.log diff --git a/runit/pi-sc-monitor/run b/runit/pi-sc-monitor/run index 10a6247..0b98522 100755 --- a/runit/pi-sc-monitor/run +++ b/runit/pi-sc-monitor/run @@ -1,2 +1,2 @@ #!/bin/sh -exec chpst -u pi:pi:dialout python -u /home/pi/sc/bin/monitor.py +exec chpst -u pi:pi:dialout:i2c python -u /home/pi/sc/bin/pidmonitor.py -- 2.50.1