diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/README b/README deleted file mode 100644 index 5b59f85..0000000 --- a/README +++ /dev/null @@ -1,7 +0,0 @@ -Raspberry Pi Temperature Logger -=============================== - -This repo contains code for a Raspberry Pi temperature logger which uses SQLite to store data read from a DS18B20 sensor. You can see more details here: -http://raspberrywebserver.com/cgiscripting/rpi-temperature-logger/building-an-sqlite-temperature-logger.html - -In webgui.py, there are several lines that contain hardcoded dates so that you can use the script with the sample database provided. There is an equivalent version of each of these lines that uses 'now' instead of a hardcoded timestamp. If you want to view data you've collected yourself, you should uncomment the lines that use 'now', and comment out the lines that have a hardocded date. See webgui.py, lines 45, 117, 122, 127 and 148. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8312a5 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +#Raspberry Pi Temperature Logger +This repo contains code for a Raspberry Pi temperature logger which uses SQLite to store data read from a DS18B20 sensor. +![Screenshot of temperature logger](http://i.imgur.com/nAFKJ4M.png) +#Installation: +**Warning:** Your Raspberry Pi will reboot after installation to enable GPIO +```bash +cd && git clone https://github.com/poohzrn/rpi_temp_logger && cd ~/rpi_temp_logger && ./install.sh +``` diff --git a/createDatabase.sql b/createDatabase.sql new file mode 100644 index 0000000..f4d9bf9 --- /dev/null +++ b/createDatabase.sql @@ -0,0 +1,10 @@ +CREATE TABLE sensor_data ( + sensor_id TEXT NOT NULL, + timestamp datetime NOT NULL DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'LOCALTIME')), + value real NOT NULL +); +CREATE TABLE sensor ( + sensor_id TEXT NOT NULL UNIQUE, + sensor_name TEXT NOT NULL DEFAULT 'SensorName', + PRIMARY KEY(sensor_id) +); diff --git a/devmonitor.py b/devmonitor.py new file mode 100755 index 0000000..6afaeb7 --- /dev/null +++ b/devmonitor.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +import sqlite3 +import glob +import random +import commands + +# global variables +dbname='/var/www/tmplog/tempdb2.db' + +# store the temperature in the database +def log_temperature(iD, temp): + conn=sqlite3.connect(dbname) + curs=conn.cursor() + insertDeviceQuery = "INSERT OR IGNORE INTO sensor (sensor_id) VALUES('"+str(iD)+"');" + curs.execute(insertDeviceQuery); + insertDataQuery = "INSERT INTO sensor_data (sensor_id,value) VALUES('"+str(iD)+"','"+str(temp)+"');" + curs.execute(insertDataQuery); + # commit the changes + conn.commit() + conn.close() + +# main function +# This is where the program starts +def main(): + devicelist = ['device1','device2',"device321"] + if devicelist=='': + # no devices + return None + else: + for device in devicelist: + temperature = random.uniform(0.0,45.0) + deviceid = device + print temperature + print deviceid + log_temperature(deviceid, temperature) +if __name__=="__main__": + main() diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..a80e202 --- /dev/null +++ b/install.sh @@ -0,0 +1,28 @@ +!#/usr/bin/env bash +#Sudo access +sudo -v +sudo apt-get update +sudo apt-get intsall apache2 -y +sudo apt-get install sqlite3 -y +sudo a2enmod mpm_prefork cgi +sudo mkdir -p /var/www/tmplog +cat ~/rpi_temp_logger/createDatabase.sql | sqlite3 tempdb2.db + +if [ -e /var/www/tmplog/tempdb2.db ]; then + sudo mv /var/www/tmplog ~/tempdb2.bak +fi +sudo cp ~/rpi_temp_logger/tmplog.conf /etc/apache2/sites-enabled +sudo mv ~/rpi_temp_logger/tempdb2.db /var/www/tmplog +sudo ln -s ~/rpi_temp_logger/monitor.py /usr/lib/cgi-bin/ +sudo ln -s ~/rpi_temp_logger/webgui.py /var/www/tmplog/index.py + +sudo chown www-data:www-data /usr/lib/cgi-bin/monitor.py +sudo chown www-data:www-data /var/www/tmplog/tempdb2.db +#Add Crontab to get data from sensors +sudo crontab -l > currentCron +sudo echo "* * * * * /usr/lib/cgi-bin/monitor.py" >> currentCron +sudo crontab currentCron && sudo rm currentCron +#enable GPIO +sudo echo "dtoverlay=w1-gpio" >> /boot/config.txt +#restart to enable GPIO +sudo reboot now diff --git a/monitor.py b/monitor.py index b20a80e..f28b9e4 100755 --- a/monitor.py +++ b/monitor.py @@ -1,45 +1,39 @@ #!/usr/bin/env python import sqlite3 - import os -import time import glob +import commands # global variables speriod=(15*60)-1 -dbname='/var/www/templog.db' - - +dbname='/var/www/tmplog/tempdb2.db' # store the temperature in the database -def log_temperature(temp): - +def log_temperature(iD, temp): conn=sqlite3.connect(dbname) curs=conn.cursor() - - curs.execute("INSERT INTO temps values(datetime('now'), (?))", (temp,)) - + insertDeviceQuery = "INSERT OR IGNORE INTO sensor (sensor_id) VALUES('"+str(iD)+"');" + curs.execute(insertDeviceQuery); + insertDataQuery = "INSERT INTO sensor_data (sensor_id,value) VALUES('"+str(iD)+"','"+str(temp)+"');" + curs.execute(insertDataQuery); # commit the changes conn.commit() - conn.close() - # display the contents of the database -def display_data(): - +def display_data(iD): conn=sqlite3.connect(dbname) curs=conn.cursor() - - for row in curs.execute("SELECT * FROM temps"): - print str(row[0])+" "+str(row[1]) - + getDevices = "SELECT * FROM sensor" + getSensorDataQuery = "SELECT * FROM sensor_data WHERE sensor_id =?" + for row in curs.execute(getSensorDataQuery, [iD]): + print str(row[0])+" "+str(row[1])+" "+str(row[2]) + for row in curs.execute(getDevices): + print str(row[0])+" "+str(row[1]) conn.close() - - -# get temerature +# get temperature # returns None on error, or the temperature as a float def get_temp(devicefile): @@ -64,8 +58,6 @@ def get_temp(devicefile): print "There was an error." return None - - # main function # This is where the program starts def main(): @@ -75,38 +67,24 @@ def main(): os.system('sudo modprobe w1-therm') # search for a device file that starts with 28 - devicelist = glob.glob('/sys/bus/w1/devices/28*') + deviceDir = '/sys/bus/w1/devices/' + devicelist = glob.glob(deviceDir + '28*') + print devicelist if devicelist=='': + # no devices return None else: # append /w1slave to the device file - w1devicefile = devicelist[0] + '/w1_slave' - - -# while True: - - # get the temperature from the device file - temperature = get_temp(w1devicefile) - if temperature != None: - print "temperature="+str(temperature) - else: - # Sometimes reads fail on the first attempt - # so we need to retry - temperature = get_temp(w1devicefile) - print "temperature="+str(temperature) - - # Store the temperature in the database - log_temperature(temperature) - - # display the contents of the database -# display_data() - -# time.sleep(speriod) - - + for w1devicefile in devicelist: + w1devicefile = w1devicefile + '/w1_slave' + # get the temperature from the device file + temperature = get_temp(w1devicefile) + while temperature == None: + temperature = get_temp(w1devicefile) + + deviceid = w1devicefile.split("/")[5] + # Store the temperature in the database + log_temperature(deviceid, temperature) + #display_data(deviceid) if __name__=="__main__": main() - - - - diff --git a/templog.db b/templog.db deleted file mode 100644 index 99983d0..0000000 Binary files a/templog.db and /dev/null differ diff --git a/tmplog.conf b/tmplog.conf new file mode 100644 index 0000000..8c4bb5e --- /dev/null +++ b/tmplog.conf @@ -0,0 +1,11 @@ +NameVirtualHost *:80 + + + Options +ExecCGI + DirectoryIndex index.py + + AddHandler cgi-script + DocumentRoot /var/www/tmplog + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/webgui.py b/webgui.py index a139980..71ada5a 100755 --- a/webgui.py +++ b/webgui.py @@ -2,22 +2,18 @@ import sqlite3 import sys +import platform +from collections import namedtuple import cgi import cgitb - # global variables -speriod=(15*60)-1 -dbname='/var/www/templog.db' - - +dbname='/var/www/tmplog/tempdb2.db' # print the HTTP header def printHTTPheader(): print "Content-type: text/html\n\n" - - # print the HTML head section # arguments are the page title and the table for the chart def printHTMLHead(title, table): @@ -25,52 +21,61 @@ def printHTMLHead(title, table): print " " print title print " " - print_graph_script(table) - print "" - -# get data from the database -# if an interval is passed, -# return a list of records from the database -def get_data(interval): - +#Get the number of sensors +def getSensorCount(): conn=sqlite3.connect(dbname) curs=conn.cursor() - - if interval == None: - curs.execute("SELECT * FROM temps") - else: -# curs.execute("SELECT * FROM temps WHERE timestamp>datetime('now','-%s hours')" % interval) - curs.execute("SELECT * FROM temps WHERE timestamp>datetime('2013-09-19 21:30:02','-%s hours') AND timestamp<=datetime('2013-09-19 21:31:02')" % interval) - - rows=curs.fetchall() - + curs.execute("select count(DISTINCT sensor.sensor_id) from sensor;") + rows=curs.fetchone() conn.close() + return int(format((rows[0]))) - return rows - +#get data from sensors based on a given interval +#todo singleton +def getSensorData(interval): + SensorDataRecord = namedtuple('sensor_data','name,timestamp,temperature') + conn=sqlite3.connect(dbname) + curs=conn.cursor() + if interval is None: + #uptimize - we dont need all data + #todo fixed time + curs.execute("SELECT sensor.sensor_name, timestamp, value FROM sensor_data,sensor WHERE timestamp>=datetime('now','-1 hours','+2 hours') AND sensor.sensor_id = sensor_data.sensor_id") + else: + curs.execute("SELECT sensor.sensor_name, timestamp, value FROM sensor_data,sensor WHERE timestamp>=datetime('now','-{0} hours','+2 hours') AND sensor.sensor_id = sensor_data.sensor_id".format(interval)) + return map(SensorDataRecord._make,curs.fetchall()) # convert rows from database into a javascript table -def create_table(rows): - chart_table="" - - for row in rows[:-1]: - rowstr="['{0}', {1}],\n".format(str(row[0]),str(row[1])) - chart_table+=rowstr - - row=rows[-1] - rowstr="['{0}', {1}]\n".format(str(row[0]),str(row[1])) - chart_table+=rowstr - - return chart_table - +def createMultiTable(interval): + sensorCount = getSensorCount() + sensorData = getSensorData(interval) + dataTable = "['Time'," + for name in range(0,sensorCount - 1): + dataTable+="'{0}',".format(sensorData[name].name) + dataTable+="'{0}'],\n".format(sensorData[-1].name)#last sensor (-1) #sensorname[1] + dataTable+="['{0}',{1},".format(sensorData[0].timestamp[:-3],sensorData[0].temperature) + counter = 1 + for data in sensorData[1:-1]: + if counter % sensorCount is 0: + dataTable+="],\n[" + dataTable+="'{0}',".format(data.timestamp[:-3]) + counter = 0 + if counter is sensorCount: + dataTable+="]{0}".format(data.temperature) + counter = 0 + if counter is sensorCount -1: + dataTable+="{0}".format(data.temperature) + elif counter is not sensorCount: + dataTable+="{0},".format(data.temperature) + counter += 1 + dataTable+="'{0}']".format(sensorData[-1].temperature) + return dataTable # print the javascript to generate the chart # pass the table generated from the database info def print_graph_script(table): - # google chart snippet chart_code=""" @@ -78,96 +83,67 @@ def print_graph_script(table): google.load("visualization", "1", {packages:["corechart"]}); google.setOnLoadCallback(drawChart); function drawChart() { - var data = google.visualization.arrayToDataTable([ - ['Time', 'Temperature'], -%s - ]); - - var options = { - title: 'Temperature' - }; - + var data = google.visualization.arrayToDataTable([%s]); + var options = {title: 'Temperature', curveType: 'function'}; var chart = new google.visualization.LineChart(document.getElementById('chart_div')); chart.draw(data, options); } """ - print chart_code % (table) - - - # print the div that contains the graph def show_graph(): print "

Temperature Chart

" - print '
' - - + print '
' # connect to the db and show some stats # argument option is the number of hours -def show_stats(option): - +def show_stats(interval): conn=sqlite3.connect(dbname) curs=conn.cursor() + if not interval: + interval = str(24) - if option is None: - option = str(24) - -# curs.execute("SELECT timestamp,max(temp) FROM temps WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option) - curs.execute("SELECT timestamp,max(temp) FROM temps WHERE timestamp>datetime('2013-09-19 21:30:02','-%s hour') AND timestamp<=datetime('2013-09-19 21:31:02')" % option) + curs.execute("SELECT timestamp,max(value) FROM sensor_data WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now','+2 hour')" % interval) rowmax=curs.fetchone() - rowstrmax="{0}   {1}C".format(str(rowmax[0]),str(rowmax[1])) + rowstrmax="{0} {1}C".format((str(rowmax[0]))[:-3],(str(rowmax[1]))[:4]) -# curs.execute("SELECT timestamp,min(temp) FROM temps WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option) - curs.execute("SELECT timestamp,min(temp) FROM temps WHERE timestamp>datetime('2013-09-19 21:30:02','-%s hour') AND timestamp<=datetime('2013-09-19 21:31:02')" % option) + curs.execute("SELECT timestamp,min(value) FROM sensor_data WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now','+2 hour')" % interval) rowmin=curs.fetchone() - rowstrmin="{0}   {1}C".format(str(rowmin[0]),str(rowmin[1])) + rowstrmin="{0} {1}C".format((str(rowmin[0]))[:-3],(str(rowmin[1]))[:4]) -# curs.execute("SELECT avg(temp) FROM temps WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option) - curs.execute("SELECT avg(temp) FROM temps WHERE timestamp>datetime('2013-09-19 21:30:02','-%s hour') AND timestamp<=datetime('2013-09-19 21:31:02')" % option) + curs.execute("SELECT avg(value) FROM sensor_data WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now','+2 hour')" % interval) rowavg=curs.fetchone() - - - print "
" - - - print "

Minumum temperature 

" + print "
Minumum temperature " print rowstrmin - print "

Maximum temperature

" + print "Maximum temperature" print rowstrmax - print "

Average temperature

" + print "Average temperature" print "%.3f" % rowavg+"C" - print "
" - print "

In the last hour:

" print "" - print "" + print "" -# rows=curs.execute("SELECT * FROM temps WHERE timestamp>datetime('new','-1 hour') AND timestamp<=datetime('new')") - rows=curs.execute("SELECT * FROM temps WHERE timestamp>datetime('2013-09-19 21:30:02','-1 hour') AND timestamp<=datetime('2013-09-19 21:31:02')") + + rows=curs.execute("SELECT timestamp,value,sensor_data.sensor_id,sensor_name FROM sensor_data,sensor WHERE timestamp>datetime('now','+1 hour') AND timestamp<=datetime('now','+2 hour') AND sensor_data.sensor_id = sensor.sensor_id") for row in rows: - rowstr="".format(str(row[0]),str(row[1])) + rowstr="".format((str(row[0]))[:-3],(str(row[1])[:4]),str(row[2]),str(row[3])) print rowstr print "
Date/TimeTemperature
Date/TimeTemperatureDeviceSensor Name
{0}  {1}C
{0}  {1}C{2}{3}
" - print "
" - conn.close() - - - def print_time_selector(option): - - print """
+ print """ Show the temperature logs for + print """
""" @@ -199,72 +180,50 @@ def validate_input(option_str): # check that the option string represents a number if option_str.isalnum(): # check that the option is within a specific range - if int(option_str) > 0 and int(option_str) <= 24: + if int(option_str) > 0 and int(option_str) <= 168: return option_str else: return None - else: + else: return None #return the option passed to the script -def get_option(): +def getTimeInterval(): form=cgi.FieldStorage() if "timeinterval" in form: option = form["timeinterval"].value return validate_input (option) else: return None - - - - # main function # This is where the program starts def main(): - cgitb.enable() - # get options that may have been passed to this script - option=get_option() + interval=getTimeInterval() + if not interval: + interval= str(1) #24 hour std interval - if option is None: - option = str(24) - - # get data from the database - records=get_data(option) - - # print the HTTP header printHTTPheader() - - if len(records) != 0: - # convert the data into a table - table=create_table(records) - else: + if getSensorCount() is 0: print "No data found" return - - # start printing the page - print "" + else: + table = createMultiTable(interval) # print the head section including the table # used by the javascript for the chart printHTMLHead("Raspberry Pi Temperature Logger", table) - # print the page body print "" - print "

Raspberry Pi Temperature Logger

" + print "

Temperature Logger

" print "
" - print_time_selector(option) + print_time_selector(interval) show_graph() - show_stats(option) + show_stats(interval) print "" print "" - sys.stdout.flush() if __name__=="__main__": main() - - - -