|
#!/usr/bin/python |
|
|
|
# This script is a continuation of my series of temperature measurements |
|
# usinjg the Dallas DS18B20 One-wire temperature sensor. |
|
# See my blog for previous versions. |
|
# |
|
# This version adds graphing the results using RRDtool and PyRRD |
|
# |
|
# MJL -- www.thepiandi.blogspot.com -- 10/3/2013 |
|
|
|
import os |
|
from datetime import datetime |
|
import time |
|
import subprocess |
|
import sys |
|
from Tkinter import Tk |
|
from tkFileDialog import asksaveasfilename |
|
from LCD_2X16 import * |
|
from pyrrd.rrd import DataSource, RRA, RRD |
|
from pyrrd.graph import DEF, CDEF, VDEF, LINE, AREA, GPRINT, COMMENT |
|
from pyrrd.graph import ColorAttributes |
|
from pyrrd.graph import Graph |
|
|
|
def loadmodules(): |
|
""" Checks to see if the 1-wire modules are loaded. If not, loades them.""" |
|
err = 'error' |
|
while err != '': |
|
modloaded = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE, stderr = subprocess.PIPE) |
|
out, err = modloaded.communicate() |
|
if 'w1_therm' not in out or 'w1_gpio' not in out: |
|
print '1-Wire modules had to be loaded. Waiting 10 seconds' |
|
print |
|
os.system('sudo modprobe w1-gpio') |
|
os.system('sudo modprobe w1-therm') |
|
time.sleep(10) |
|
|
|
def read_temp_raw(): |
|
|
|
""" Uses Popen to open then read contents of w-slave. Returns the contents of the file as a two line list. |
|
If the file cannot be read the 1-wire modules are unloaded and loaded again. This is repeated |
|
three times until the file is read successfully. After three times, the program |
|
is stopped.""" |
|
|
|
global glitches |
|
trials = 3 |
|
catdata = subprocess.Popen(['cat', device_file], stdout = subprocess.PIPE, stderr = subprocess.PIPE) |
|
out, err = catdata.communicate() |
|
while err != "" and trials: |
|
print "Got a glitch - trying to recover" |
|
trials -= 1 |
|
time.sleep(1) |
|
os.system('sudo modprobe -r w1-gpio') |
|
os.system('sudo modprobe -r w1-therm') |
|
time.sleep(1) |
|
os.system('sudo modprobe w1-gpio') |
|
os.system('sudo modprobe w1-therm') |
|
time.sleep(10) |
|
glitches += 1 |
|
catdata = subprocess.Popen(['cat', device_file], stdout = subprocess.PIPE, stderr = subprocess.PIPE) |
|
out, err = catdata.communicate() |
|
if trials: |
|
out_decode = out.decode('utf-8') |
|
lines = out_decode.split('\n') |
|
return lines |
|
else: |
|
raise(IOError) |
|
|
|
def print2display(temperature, sen): |
|
""" Prints the temperature and time to the 16 character |
|
by two line LCD display. Also prints to the stdio""" |
|
|
|
time.sleep(.01) |
|
if sen == '0': |
|
lcd_byte(LCD_LINE_1, LCD_CMD) # Print to top line |
|
string1 = "Brdbd: " + str(temperature) + " degF" |
|
time.sleep(.01) |
|
lcd_string(string1) |
|
if sen =='1': |
|
lcd_byte(LCD_LINE_2, LCD_CMD) # Print to bottom line |
|
string1 = "Cable: " + str(temperature) + " degF" |
|
time.sleep(.01) |
|
lcd_string(string1) |
|
|
|
def check_switch(): |
|
"""Checks to see if the breadboard switch was pressed. If so it generates a keyboard interrupt |
|
as if CTRL-C was pressed. Switch must be held down for at least .2 seconds to be reconized""" |
|
input_switch = GPIO.input(SwitchInput) # Look for breadboard switch |
|
if not input_switch: |
|
time.sleep(.2) |
|
input_switch = GPIO.input(SwitchInput) # Look for breadboard switch again |
|
if not input_switch: # Must still be pressed |
|
print |
|
print "Switch Pressed" |
|
raise(KeyboardInterrupt) |
|
|
|
def check_title(title): |
|
"""Makes sure that all of the space characters are escaped with the backspace character""" |
|
cnt_spaces = title.count(' ') |
|
title_good = True |
|
start_pos = 0 |
|
while cnt_spaces: |
|
index_space = title.find(' ', start_pos) |
|
if title[index_space -1] != '\\': |
|
title_good = False |
|
start_pos = index_space + 1 |
|
cnt_spaces -= 1 |
|
return title_good |
|
|
|
# -------------------------------------------------------------------------------------------------- |
|
|
|
# Main Program |
|
|
|
glitches = 0 |
|
device_id = ('28-00000400d39d' ,'28-000004986cbb') |
|
prompt = 'Enter the measurement time interval in minutes (integer values only): ' |
|
prompt1 = 'Enter 0 for sensor on breadboard, 1 for cable sensor, 2 for both: ' |
|
prompt2 = 'Enter the maximum number of measurements: ' |
|
prompt3 = 'What are we measuring with the breadboard sensor? Default is Breadboard: ' |
|
prompt4 = 'What are we measuring with the cable sensor? Default is Cable: ' |
|
prompt5 = 'Enter comment if desired (Remember to escape punctuation): ' |
|
prompt6 = 'Enter a name for the file. No spaces, extension, or directory: ' |
|
prompt7 = 'Enter a title for the graph (Remember to escape spaces and punctuation): ' |
|
prompt8 = 'Satisfied with the inputs? Y or N: ' |
|
prompt9 = 'OK to proceed? If not, we quit. Y or N: ' |
|
prompt10 = 'Graph background color: Enter 0 for black, 1 for white, 2 for both: ' |
|
prompt11 = 'Choose height of graph in the range if 100 to 400 pixels: ' |
|
|
|
short_wait = .1 |
|
|
|
lcd_byte(0x01, LCD_CMD) # Clear Display |
|
loadmodules() # Check to see if 1-Wire modules are loaded. |
|
|
|
# Operator Inputs |
|
|
|
good_inputs = False |
|
|
|
while not good_inputs: |
|
|
|
# Choose which sensor or both |
|
print |
|
sensor = '' |
|
while sensor != '0' and sensor != '1' and sensor !='2': |
|
sensor = raw_input(prompt1) |
|
|
|
print |
|
if sensor == '0': |
|
print "You have choosen the breadboard sensor" |
|
elif sensor == '1': |
|
print "You have choosen the cable sensor" |
|
elif sensor == '2': |
|
print "You have choosen both sensors" |
|
|
|
# Legend Titles |
|
print |
|
if sensor == '0' or sensor == '2': |
|
measure_what0 = raw_input(prompt3) |
|
if measure_what0 == '': |
|
measure_what0 = 'Breadboard' |
|
|
|
if sensor == '1' or sensor == '2': |
|
measure_what1 = raw_input(prompt4) |
|
if measure_what1 == '': |
|
measure_what1 = 'Cable' |
|
|
|
# Graph Title |
|
print |
|
title_good = False |
|
while not title_good: |
|
title_it = '' |
|
while not title_it: |
|
title_it = raw_input(prompt7) |
|
title_good = check_title(title_it) |
|
if not title_good: |
|
print "\n\tYou forgot to escape all of the space characters, try again: " |
|
|
|
# Comment |
|
print |
|
comment = raw_input(prompt5) |
|
cmt = COMMENT(comment) |
|
|
|
# Graph Colors |
|
print |
|
background = '' |
|
while background != '0' and background != '1' and background !='2': |
|
background = raw_input(prompt10) |
|
|
|
# Height of Graph |
|
print |
|
how_high = 0 |
|
while how_high < 100 or how_high > 400: |
|
try: |
|
how_high = abs(int(raw_input(prompt11))) |
|
except: |
|
pass |
|
|
|
# File names |
|
print |
|
directory = '/home/pi/Documents/PythonProjects/TempProbe/TempResults/' |
|
filename = raw_input(prompt6) |
|
graphfile_blk = directory + filename + '_black.png' |
|
graphfile_wht = directory + filename + '_white.png' |
|
|
|
if sensor == '0' or sensor == '2': |
|
rrdfile0 = directory + filename + '_bread.rrd' |
|
|
|
if sensor == '1' or sensor == '2': |
|
rrdfile1 = directory + filename + '_cable.rrd' |
|
|
|
# Maximum number of measurements |
|
print |
|
max_measurements = 0 |
|
while not max_measurements: |
|
try: |
|
max_measurements = abs(int(raw_input(prompt2))) |
|
except: |
|
pass |
|
|
|
# Measurement Interval |
|
print |
|
measurement_interval = 0 |
|
while not measurement_interval: |
|
try: |
|
measurement_interval = abs(int(raw_input(prompt))) |
|
except: |
|
pass |
|
|
|
# Satisfied With The Inputs? |
|
print |
|
response = "" |
|
while response != 'y' and response != 'Y' and response != 'n' and response != 'N': |
|
response = raw_input(prompt8) |
|
if response == 'Y' or response == 'y': |
|
good_inputs = True |
|
|
|
# Good to Proceed? |
|
print |
|
proceed = '' |
|
while proceed != 'y' and proceed != 'Y' and proceed != 'n' and proceed != 'N': |
|
proceed = raw_input(prompt9) |
|
|
|
print |
|
|
|
measurement_interval *= 60 |
|
|
|
start_time = int(time.time() / measurement_interval) * measurement_interval |
|
|
|
next_meas_time= start_time + measurement_interval |
|
|
|
# Breadboard RRD Setup |
|
|
|
if sensor == '0' or sensor == '2': |
|
dataSources = [] |
|
roundRobinArchives = [] |
|
dataSource = DataSource(dsName='Breadboard', dsType='GAUGE', heartbeat=int(1.5 * measurement_interval)) |
|
dataSources.append(dataSource) |
|
|
|
roundRobinArchives.append(RRA(cf='LAST', xff=0.5, steps=1, rows=max_measurements)) |
|
|
|
breadboard = RRD(rrdfile0, step=measurement_interval, ds=dataSources, rra=roundRobinArchives, start=start_time) |
|
breadboard.create(debug=False) |
|
|
|
# Cable RRD Setup |
|
|
|
if sensor == '1' or sensor == '2': |
|
dataSources = [] |
|
roundRobinArchives = [] |
|
dataSource = DataSource(dsName='Cable', dsType='GAUGE', heartbeat=int(1.5 * measurement_interval)) |
|
dataSources.append(dataSource) |
|
|
|
roundRobinArchives.append(RRA(cf='LAST', xff=0.5, steps=1, rows=max_measurements)) |
|
|
|
cable = RRD(rrdfile1, step=measurement_interval, ds=dataSources, rra=roundRobinArchives, start=start_time) |
|
cable.create(debug=False) |
|
|
|
# Breadboard Graph Setup |
|
|
|
if sensor == '0' or sensor == '2': |
|
bread_def = DEF(rrdfile=rrdfile0, vname='Bread_data', dsName='Breadboard', cdef='LAST') |
|
bread_line = LINE(defObj=bread_def, color='#00FF00', legend=measure_what0 + ' Temperature') |
|
bread_aver = VDEF(vname='Bread_aver', rpn='%s,AVERAGE' % bread_def.vname) |
|
bread_val = GPRINT(bread_aver, 'Average ' + measure_what0 + ' Temperature: %6.2lf Degrees F') |
|
|
|
# Cable Graph Setup |
|
|
|
if sensor == '1' or sensor == '2': |
|
cable_def = DEF(rrdfile=rrdfile1, vname='Cable_data', dsName='Cable', cdef='LAST') |
|
cable_line = LINE(defObj=cable_def, color='#FF0000', legend=measure_what1 + ' Temperature') |
|
cable_aver = VDEF(vname='Cable_aver', rpn='%s,AVERAGE' % cable_def.vname) |
|
cable_val = GPRINT(cable_aver, 'Average ' + measure_what1 + ' Temperature: %6.2lf Degrees F') |
|
|
|
# Define Graph Colors |
|
# black background: |
|
black_bkgnd = ColorAttributes() |
|
black_bkgnd.back = '#000000' |
|
black_bkgnd.canvas = '#333333' |
|
black_bkgnd.shadea = '#000000' |
|
black_bkgnd.shadeb = '#111111' |
|
black_bkgnd.mgrid = '#CCCCCC' |
|
black_bkgnd.axis = '#FFFFFF' |
|
black_bkgnd.frame = '#0000AA' |
|
black_bkgnd.font = '#FFFFFF' |
|
black_bkgnd.arrow = '#FFFFFF' |
|
|
|
# white background: |
|
white_bkgnd = ColorAttributes() |
|
white_bkgnd.back = '#FFFFFF' |
|
white_bkgnd.canvas = '#EEEEEE' |
|
white_bkgnd.shadea = '#000000' |
|
white_bkgnd.shadeb = '#111111' |
|
white_bkgnd.mgrid = '#444444' |
|
white_bkgnd.axis = '#000000' |
|
white_bkgnd.frame = '#0000AA' |
|
white_bkgnd.font = '#000000' |
|
white_bkgnd.arrow = '#000000' |
|
|
|
# Let's make some measurements and graph them |
|
try: |
|
if proceed == 'N' or proceed == 'n': |
|
raise(KeyboardInterrupt) |
|
|
|
print 'First Measurement Will Be Made At: ' + time.asctime(time.localtime(next_meas_time)) |
|
print |
|
while max_measurements: |
|
time_now = time.time() |
|
while time_now < next_meas_time: |
|
check_switch() |
|
time.sleep(0.5) |
|
time_now = time.time() |
|
|
|
if sensor == '0' or sensor == '2': |
|
device_file = '/sys/bus/w1/devices/' + device_id[0] + '/w1_slave' |
|
measurement_good = False |
|
while measurement_good == False: |
|
lines = read_temp_raw() |
|
if 'YES' in lines[0]: |
|
equals_pos = lines[1].find('t=') |
|
if equals_pos != -1: |
|
measurement_good = True |
|
temp_string = lines[1][equals_pos +2:] |
|
Deg_C = float(temp_string)/1000.0 |
|
Deg_F = round(Deg_C * 1.8 + 32.0, 1) |
|
timenow = datetime.now() |
|
print timenow.strftime("%A, %B %d, %I:%M:%S %p") + (", the Breadboard sensor temperature is %3.1f") %(Deg_F) + u"\xB0" +"F" |
|
print2display(Deg_F, '0') |
|
if measurement_good == False: |
|
time.sleep(short_wait) |
|
|
|
breadboard.bufferValue(next_meas_time, str(Deg_F)) |
|
breadboard.update(debug = False) |
|
|
|
if sensor == '1' or sensor == '2': |
|
device_file = '/sys/bus/w1/devices/' + device_id[1] + '/w1_slave' |
|
measurement_good = False |
|
while measurement_good == False: |
|
lines = read_temp_raw() |
|
if 'YES' in lines[0]: |
|
equals_pos = lines[1].find('t=') |
|
if equals_pos != -1: |
|
measurement_good = True |
|
temp_string = lines[1][equals_pos +2:] |
|
Deg_C = float(temp_string)/1000.0 |
|
Deg_F = round(Deg_C * 1.8 + 32.0, 1) |
|
timenow = datetime.now() |
|
print timenow.strftime("%A, %B %d, %I:%M:%S %p") + (", the Cable sensor temperature is %3.1f") %(Deg_F) + u"\xB0" +"F" |
|
print2display(Deg_F, '1') |
|
if measurement_good == False: |
|
time.sleep(short_wait) |
|
|
|
cable.bufferValue(next_meas_time, str(Deg_F)) |
|
cable.update(debug = False) |
|
|
|
next_meas_time += measurement_interval |
|
max_measurements -= 1 |
|
|
|
gb = Graph(graphfile_blk, start = start_time, end = next_meas_time - measurement_interval, color = black_bkgnd, vertical_label='Degrees\ F', width=600, height=how_high, title=title_it) |
|
gw = Graph(graphfile_wht, start = start_time, end = next_meas_time - measurement_interval, color = white_bkgnd, vertical_label='Degrees\ F', width=600, height=how_high, title=title_it) |
|
|
|
if sensor == '0': |
|
gb.data.extend([bread_def, bread_line, bread_aver, bread_val]) |
|
gw.data.extend([bread_def, bread_line, bread_aver, bread_val]) |
|
if sensor == '1': |
|
gb.data.extend([cable_def, cable_line, cable_aver, cable_val]) |
|
gw.data.extend([cable_def, cable_line, cable_aver, cable_val]) |
|
if sensor == '2': |
|
gb.data.extend([bread_def, bread_line, bread_aver]) |
|
gb.data.extend([cable_def, cable_line, cable_aver]) |
|
gb.data.extend([bread_val, cable_val]) |
|
gw.data.extend([bread_def, bread_line, bread_aver]) |
|
gw.data.extend([cable_def, cable_line, cable_aver]) |
|
gw.data.extend([bread_val, cable_val]) |
|
if comment: |
|
gb.data.extend([cmt]) |
|
gw.data.extend([cmt]) |
|
|
|
if background == '0' or background == '2': |
|
gb.write() |
|
if background == '1' or background == '2': |
|
gw.write() |
|
|
|
except(KeyboardInterrupt): |
|
print |
|
|
|
except(IOError): |
|
print |
|
print 'Three tries and we are out of here' |
|
|
|
except: |
|
print |
|
print "Unexpected Error: ", sys.exc_info()[1] |
|
|
|
print |
|
print "See you later, Glitches = %d" % glitches |
|
|
|
lcd_byte(0x01, LCD_CMD) # Clear Display |
|
GPIO.cleanup() |
|
|
|
print |
|
print "start time: ", start_time |
|
print "last measurement: ", next_meas_time |
|
|
|
run_time = next_meas_time - start_time - measurement_interval |
|
run_days = run_time / 86400 |
|
run_hours = run_time % 86400 / 3600 |
|
run_minutes = run_time % 86400 % 3600 / 60 |
|
total_measurements = run_time / measurement_interval |
|
|
|
print |
|
print 'Total Run Time: %2d days, %2d hours, %2d minutes' %(run_days, run_hours, run_minutes) |
|
print 'Total Number of Measurements Per Device: ' + str(total_measurements) |
|
print |
|
|
|
|
|
|