Ronin Technologies
Toggle Content Home | Customers | Downloads | Forums | Gallery | Links | Services | About
Main Menu

Tech News


How To Gather and Chart Temperatures in your House



All | Theatre | HTPC | Furnace | Nook | Garage | Bedroom | Attic | How To Do This

Setting up the Sensor Network

I've been into Home Automation ever since I was introduced to the X10 Firecracker deal back in the mid 90's. For a few bucks they sent you a dongle for your serial port that could control a lamp module via RF. I was hooked. I picked up a shareware package called Homeseer and Homeseer Phone for $40 and started to purchase devices. I even developed a Canadian Weather script that parsed the Environment Canada weather site for your location and made all the information available to Homeseer which would display the info on its web pages and also speak the data when prompted.

So when we built our new house back in 2004, I wanted to wire up all sorts of stuff. That included a temperature sensor network. This would let us see how the house heated and cooled throughout the year relative to the outside temperature. With this info I could do things like turn on the air exchange fan when the outside temperature went cooler than the inside temperature during the summer months. After doing some Googling, the answer was obvious: Maxim's 1-wire network and devices were designed for projects like this. All they require is a dongle on your PC to interface your computer to their 1-wire network, then a simple Cat5 wiring setup to connect the dongle to various 1-wire devices around your house.

After sending off for some samples I had the pieces to setup my sensor network. This included the USB DS9490 1-wire interface and a bunch of DS18S20 temp sensors. I used an old bread board I had leftover from university days to hook everything up. It looks messy but worked like a charm.

sensors, -55°C to +125°C and accurate to ±0.5°C over the range of -10°C to +85°C
1 Wire USB Network interface

Hooking up the sensors was straight forward. Even though its called a 1-wire network, there's actually 3 lines: GND, DATA and POWER. If you run the sensors in parasitic mode you don't need the POWER line as they will get their juice from the data line so you can just connect the POWER pin to GND. I've found the network is more stable in parasitic mode. If I run with power some of the sensors always return 85 degrees which is a known issue.

I used CAT-5 from the breadboard next to the computer to every sensor location in the house. I had picked up a 1000' spool of CAT5E from Home Depot that I used to wire the whole hose for Ethernet, Phone and Sensors. When I was done wiring everything up I had accurate temperature sensors at the following locations:

  • The breadboard used to wire up the sensors located next to the Home Theatre/Home Automation PC in the basement.
  • The furnace room where the firewall and main Linux server is located.
  • The main Home Theater room in the walkout basement.
  • The dining nook in the open main floor at the south facing rear of the house.
  • The north facing garage.
  • The south facing master bedroom.
  • The attic.
  • Outside the house in a shaded spot protected from direct sunlight.

Acquiring The Data

So now that I had a 1-wire network, I had to come up with a way to gather the data from the sensors. At first I used a 1-wire plugin to Homeseer but I found it was a bit flaky. Then I came across the OWFS 1-Wire File System. It was developed for Linux but had also been ported to most other OS's including Windows (native and under Cygwin). After building and installing it under Cygwin, all I had to do to start it up was add a shortcut to my startup folder that did the following:

"C:\Cygwin\usr\bin\owserver.exe -u -p 3000"

This starts up the owfs server on port 3000. From here it is a simple matter to read the temperatures. The owdir command will list all of the 1-wire devices on your network. You can then use owread to get the data. For instance, owdir on my network returns:

$ owdir localhost:3000
/bus.0
/settings
/system
/statistics
/10.B0FB3E010800
/10.A4DD4B010800
/10.34F73E010800
/10.791D6B000800
/10.C59A3B000800
/10.53474C010800
/10.F38C3B000800
/10.37D96A000800
/28.81B350000000
/22.398703000000
/22.738803000000
/81.6BDC22000000
The 10 and 22 addresses are my temperature sensors. Another more specific owdir will give more info on a sensor. For example:
$ owdir localhost:3000 /10.B0FB3E010800
/10.B0FB3E010800/address
/10.B0FB3E010800/crc8
/10.B0FB3E010800/die
/10.B0FB3E010800/family
/10.B0FB3E010800/id
/10.B0FB3E010800/locator
/10.B0FB3E010800/power
/10.B0FB3E010800/present
/10.B0FB3E010800/r_address
/10.B0FB3E010800/r_id
/10.B0FB3E010800/r_locator
/10.B0FB3E010800/temperature
/10.B0FB3E010800/temphigh
/10.B0FB3E010800/templow
/10.B0FB3E010800/trim
/10.B0FB3E010800/trimblanket
/10.B0FB3E010800/trimvalid
/10.B0FB3E010800/type
This shows all the properties of a specific sensor, in this case my Outside DS18S20 temperature sensor. To get the temperature (or any other property) for this sensor the owread command is used:
$ owread localhost:3000 /10.B0FB3E010800/temperature
     35.4375
A great aspect of the OWFS is that it can be used in a client/server setup. The owserver is running on my Home Automation PC (HAPC) but can be accessed by any other machine able to talk to it on port 3000. Since I have a dedicated firewall that means access is limited to machines on my home/office private network. To unload work from the HAPC I decided to store the data on my main Linux server. Ideally the server would also run on the Linux box but I had wired everything back to my Home run near the HAPC. After building and installing OWFS on my Linux box I was able to access temperature data across my network with the following command:
$ owread -s hapc:3000 /10.B0FB3E010800/temperature
      35.875

UPDATE (April, 2006):

I've moved everything onto my Linux server so rather than using owdir/owread to remotely query the owserver running on my windows HTPC box, I now run FUSE/OWFS and simply cat the temperature in the OWFS filesystem. The new command to start the owfs server under linux is now "owfs -C -u /mnt/1wire" which starts owfs over the USB interface and with the units as Celsius and mounts it on the existing /mnt/1wire directory. The fuse kernel module must be running so an "insmod fuse" may be required:
$ lsmod | grep fuse
$ insmod fuse
$ owfs -C -u /mnt/1wire
$ cat /mnt/1wire/10.B0FB3E010800/temperature
      13.25

UPDATE (November, 2009):

I finally upgraded my old Mandriva Linux server to Ubuntu. Owfs is available as an installable package in Ubuntu which is nice. To automatically start things off when the machine boots up I added an owfs file to /etc/init.d. Make sure you chmod it to 777, sudo is your friend:
#!/bin/sh
owserver -u -p 4304
I then ran "sudo update-rc.d owfs defaults" which makes sure the new script is called at startup. Next I migrated a lot of my scripts to Perl and the < a href="http://owfs.org/index.php?page=ownet-pm">OWNet module. This just simplified some of the stuff I was doing in shell scripts.

Sometimes it doesn't start up properly and I have to do the following 2 commands:
killall owserver
sudo /etc/init.d/owfser

So now my cronjob that runs every 5 mins calls temps.pl which looks like this:

use strict;
use warnings;
use DateTime;
use Data::Dumper;
use DateTime::Event::Sunrise;
use Math::Round;
use OWNet;
use RRDs;

my %sensors = (
        "10.C59A3B000800" => { name => "Basement", temp => 99 },
        "22.738803000000" => { name => "HTPC", temp => 99 },
        "22.398703000000" => { name => "Furnace", temp => 99 },
        "10.791D6B000800" => { name => "Nook", temp => 99 },
        "10.F38C3B000800" => { name => "Garage", temp => 99 },
        "10.37D96A000800" => { name => "Bedroom", temp => 99 },
        "28.81B350000000" => { name => "Attic", temp => 99 },
        "10.B0FB3E010800" => { name => "Outside", temp => 99 }
        );

my $dir = "/var/www/html/temps";
my $file = "temps.rrd";
my $db = "$dir/$file";
my ($sunrise_secs, $sunset_secs, $SunsetHrs, $SunsetHrsInSecs, $SunsetMins, $SunriseHrs, $SunriseHrsInSecs, $SunriseMins);
my $SUNS = "";
my $width=600;
my $height=100;
my $watermark="Ronin Technologies Inc.";
my $IMAGE = "$dir/temps.png";
my $COMMENT="Generated on `date \"+%d %m %y %H\:%M %Z\"`";
my $LAT = "50.839152";
my $LON = "-114.009692";

#
# Function to calculate the sunrise and sunset times to show on the graphs
#
sub get_sunrise_sunset()
{
    my $dt = DateTime->today(time_zone => 'America/Edmonton');
    my $sunrise_span = DateTime::Event::Sunrise ->new( longitude => $LON , latitude => $LAT, altitude => '-0.833', iteration => '1');
    my $both_times = $sunrise_span->sunrise_sunset_span($dt);
#    print "Sunrise is: " , $both_times->start->datetime, "\n";
#    print "Sunset is: " , $both_times->end->datetime, "\n";

    my $sunr = $both_times->start->datetime;
    $sunr =~ s/^.+T//;
#    print $sunr, "\n";
    my $suns = $both_times->end->datetime;
    $suns =~ s/^.+T//;
#    print $suns, "\n";
    my @sunr_bits = split(/:/, $sunr);
    my @suns_bits = split(/:/, $suns);

    $sunrise_secs = $sunr_bits[0]*3600 + $sunr_bits[1]*60 + $sunr_bits[2];
    $sunset_secs = $suns_bits[0]*3600 + $suns_bits[1]*60 + $suns_bits[2];

    $SunsetHrs = round(($sunset_secs / 3600) - 12);
    $SunsetHrsInSecs = ($SunsetHrs + 12) * 3600;
    $SunsetMins = round(($sunset_secs - $SunsetHrsInSecs) / 60);
    $SunriseHrs = round($sunrise_secs / 3600);
    $SunriseHrsInSecs = ($SunriseHrs + 12) * 3600;
    $SunriseMins = round(($sunrise_secs - $SunriseHrsInSecs) / 60);
    $SUNS = "Sunset: ${suns} PM   Sunrise: ${sunr} AM";
#    $SUNS = "Sunset\: $SunsetHrs\:$SunsetMins PM Sunrise\: $SunriseHrs\:$SunriseMins AM";
    print "$SUNS\n";
    #print "Sunrise seconds = ", $sunrise_secs, "\n";
    #print "Sunrise seconds = ", $sunset_secs, "\n";
}

sub get_temps()
{
    # Setup comms to the OWFS server
    my $owserver = OWNet->new('localhost:4304 -v -C');
    # Loop through each sensor
    my $update_string = "N";
    print "\n";
    foreach my $sensor ( keys(%sensors))
    {
        my $temp = 99;
        my $passed;
        if ( $owserver->present("/$sensor") )
        {
            $sensors{$sensor}{temp} = $owserver->read("/$sensor/temperature");
            $sensors{$sensor}{temp} =~ s/^\s+//;
            print "$sensor: $sensors{$sensor}{name} =>\t$sensors{$sensor}{temp} \n";
            $passed = 1;
        } else
        {
            print "$sensors{$sensor}{name} not responding, retrying in 5 seconds\n";
            sleep 5;
            # Retry 3 times if we can't communicate on first attempt
            for (my $i = 0; $i < 3;$i++)
            {
                $passed = 0;
                if ( $owserver->present("/$sensor") )
                {
                    $sensors{$sensor}{temp} = $owserver->read("/$sensor/temperature");
                    $sensors{$sensor}{temp} =~ s/^\s+//;
                    print "$sensor: $sensors{$sensor} =>\t$temp \n";
                    $passed = 1;
                    last;
                } else
                {
                    print "$sensors{$sensor}{name} not responding, retrying in 5 seconds\n";
                    sleep 5;
                }
            }
        }
        if ( !$passed )
        {
            print "$sensor: $sensors{$sensor} not responding\n";
            # Insert last valid value.
            $sensors{$sensor}{temp} = "U";
        }
        # Trim any leading whitespace
        $sensors{$sensor}{temp} =~ s/^\s+//;
        $update_string .= ":$sensors{$sensor}{temp}";
    }
    print "\n";
    #rrdtool update $DIR/temps.rrd N:${temp[0]}:${temp[1]}:${temp[2]}:${temp[3]}:${temp[4]}:${temp[5]}:${temp[6]}:${temp[7]}
    # Now we can submit the data as one set.
    RRDs::update( "$db","N:".$sensors{"10.C59A3B000800"}{temp}.":".$sensors{"22.738803000000"}{temp}.":".$sensors{"22.398703000000"}{temp}.":".$sensors{"10.791D6B000800"}{temp}.":".$sensors{"10.F38C3B000800"}{temp}.":".$sensors{"10.37D96A000800"}{temp}.":".$sensors{"28.81B350000000"}{temp}.":".$sensors{"10.B0FB3E010800"}{temp} );
    my $err = RRDs::error;
    print "ERROR while updating DB: $err\n" if $err;
    #print "\n$update_string\n";
}

sub graph_summary_temps()
{
    my $result_arr = ();
    my $xsize = 0;
    my $ysize = 0;
    my $time = "3h";
    my $PERIOD = "Last_3_Hours";
    print "Graphing\n";
    ($result_arr,$xsize,$ysize) = RRDs::graph( "$IMAGE", "-A", "-s", "-$time", "-e", "now", "-a", "PNG",
    "-t", "Paul and Helen's House Temps For $PERIOD",
    "-v", "°C",
    "-w", "$width",
    "-h", "$height",
    "-W", "$watermark",
    "-z", "-Y",
    "DEF:Basement=$db:theatre:AVERAGE",
    "DEF:HTPC=$db:htpc:AVERAGE",
    "DEF:Furnace=$db:furnace:AVERAGE",
    "DEF:Nook=$db:nook:AVERAGE",
    "DEF:Garage=$db:garage:AVERAGE",
    "DEF:Bedroom=$db:bedroom:AVERAGE",
    "DEF:Attic=$db:attic:AVERAGE",
    "DEF:Outside=$db:outside:AVERAGE",
    "CDEF:nightplus=LTIME,86400,%,$sunrise_secs,LT,INF,LTIME,86400,%,$sunset_secs,GT,INF,UNKN,Basement,*,IF,IF",
    "CDEF:nightminus=LTIME,86400,%,$sunrise_secs,LT,NEGINF,LTIME,86400,%,$sunset_secs,GT,NEGINF,UNKN,Basement,*,IF,IF",
    "AREA:nightplus#CCCCCCAA",
    "AREA:nightminus#CCCCCCAA",
    "COMMENT:\t\t\tnow       avg.      max.      min.\n",
    "LINE1:Basement#0000FF:Theatre\t",
    "GPRINT:Basement:LAST:%5.1lf °C",
    "GPRINT:Basement:AVERAGE:%5.1lf °C",
    "GPRINT:Basement:MAX:%5.1lf °C",
    "GPRINT:Basement:MIN:%5.1lf °C\n",
    "LINE1:HTPC#FF0000:HTPC\t",
    "GPRINT:HTPC:LAST:%5.1lf °C",
    "GPRINT:HTPC:AVERAGE:%5.1lf °C",
    "GPRINT:HTPC:MAX:%5.1lf °C",
    "GPRINT:HTPC:MIN:%5.1lf °C\n",
    "LINE1:Furnace#9900FF:Furnace\t",
    "GPRINT:Furnace:LAST:%5.1lf °C",
    "GPRINT:Furnace:AVERAGE:%5.1lf °C",
    "GPRINT:Furnace:MAX:%5.1lf °C",
    "GPRINT:Furnace:MIN:%5.1lf °C\n",
    "LINE1:Nook#33CCCC:Nook\t",
    "GPRINT:Nook:LAST:%5.1lf °C",
    "GPRINT:Nook:AVERAGE:%5.1lf °C",
    "GPRINT:Nook:MAX:%5.1lf °C",
    "GPRINT:Nook:MIN:%5.1lf °C\n",
    "LINE1:Garage#FF00FF:Garage\t",
    "GPRINT:Garage:LAST:%5.1lf °C",
    "GPRINT:Garage:AVERAGE:%5.1lf °C",
    "GPRINT:Garage:MAX:%5.1lf °C",
    "GPRINT:Garage:MIN:%5.1lf °C\n",
    "LINE1:Bedroom#ffff00:Bedroom\t",
    "GPRINT:Bedroom:LAST:%5.1lf °C",
    "GPRINT:Bedroom:AVERAGE:%5.1lf °C",
    "GPRINT:Bedroom:MAX:%5.1lf °C",
    "GPRINT:Bedroom:MIN:%5.1lf °C\n",
    "LINE1:Attic#865F00:Attic\t",
    "GPRINT:Attic:LAST:%5.1lf °C",
    "GPRINT:Attic:AVERAGE:%5.1lf °C",
    "GPRINT:Attic:MAX:%5.1lf °C",
    "GPRINT:Attic:MIN:%5.1lf °C\n",
    "LINE2:Outside#00FF00:Outside\t",
    "GPRINT:Outside:LAST:%5.1lf °C",
    "GPRINT:Outside:AVERAGE:%5.1lf °C",
    "GPRINT:Outside:MAX:%5.1lf °C",
    "GPRINT:Outside:MIN:%5.1lf °C\n",
    "HRULE:0#00FFFF:Freezing\n",
    "COMMENT:$SUNS\n",
    "COMMENT:$COMMENT");
    print "Imagesize: $xsize x $ysize\n";
    print Dumper($result_arr);
}

#
# Main processing starts here
#
get_sunrise_sunset();

get_temps();

$SUNS =~ s/:/\:/g;
print "$SUNS\n";
exec("/bin/bash /var/www/html/temps/graphtemp.sh $sunrise_secs $sunset_secs");
#graph_summary_temps();

Storing The Data

Now that I could read temperature sensor data across the network I needed to com up with a way to store the information. For this I chose the RRDTool package to manage the data. Its a circular (RR in RRD stands for Round Robin) database that lets you store/graph a predefined amount of data. After initial creation of the DB it is as big as it will ever get and just contains "unknown" data. For example I set it up to track 5 years worth of temperatures. This is a widely used open source package that has a bit of a steep learning curve on some of its aspects but gives you everything including the kitchen sink for functionality. It works on multiple platforms including Linux and Windows and has a large, active support community.

The "rrdtool create" command is used to setup the database. Here's the tmpdb.sh bash script I used to set it up:



tempdb.sh
#!/bin/sh
echo "Creating rrdtool DB for 8 temp sensors"

# 50 hours of 5 minute data
# 350 hours  (~2 weeks) of 30 minute data
# 1550 hours (~2 months)  of 2 hour data
#  750 days (~2 years) of 12 hour data
# 2000 days (~5 years) of 24 hour data
rrdtool create temps.rrd \
	--start N \ # Start the DB now
	--step 300 \ # Expects data for every 300 second period
	DS:theatre:GAUGE:600:-50:60 \ # DS:<name>:<type>:<heartbeat>:<min>:<max>

	DS:htpc:GAUGE:600:-50:60 \
	DS:furnace:GAUGE:600:-50:60 \
	DS:nook:GAUGE:600:-50:60 \
	DS:garage:GAUGE:600:-50:60 \
	DS:bedroom:GAUGE:600:-50:60 \
	DS:attic:GAUGE:600:-50:70 \
	DS:outside:GAUGE:600:-50:70 \
	RRA:AVERAGE:0.5:1:600 \
	RRA:AVERAGE:0.5:6:700 \
	RRA:AVERAGE:0.5:24:775 \
	RRA:AVERAGE:0.5:144:1500 \
	RRA:AVERAGE:0.5:288:2000 \
	RRA:MAX:0.5:1:600 \
	RRA:MAX:0.5:6:700 \
	RRA:MAX:0.5:24:775 \
	RRA:MAX:0.5:144:1500 \
	RRA:MAX:0.5:288:2000 \
	RRA:MIN:0.5:1:600 \
	RRA:MIN:0.5:6:700 \
	RRA:MIN:0.5:24:775 \
	RRA:MIN:0.5:144:1500 \
	RRA:MIN:0.5:288:2000 

The script generates a temps.rrd database file that is approx 1MB in size. Since rrdtool is a circular buffer, you don't have to worry about the DB growing in size. It does this by creating an already populated DB. It just populates everything as "UNKNOWN" to start.

Periodically Storing Data in the Database

Once I had setup the database I needed something to read all of the sensors every 5 minutes and place the data in the DB. For this I setup a cron job on the Linux server. It runs every 5 minutes every hour, every day and executes the following command:

/var/www/html/temps/crontemps.sh  > /dev/null  

It pipes the output to /dev/null so we don't get emailed every time the cron job executes. If there is _any_ output at all from the script, an email gets generated. During initial debugging this was handy to find problems however a couple of mornings I'd wake up to see dozens of emails in my Inbox. :-)

Here's the contents of the bash crontemps.sh script that gets invoked by the cron job:

crontemps.sh
#!/bin/bash
DIR=/var/www/html/temps
cd $DIR

#Get ths sunrise/sunset info in variables
. suns.txt
#Poll the sensors and store their values in the DB
./verified_temps.sh
#Generate a graph for each sensor
./graphtemp.sh $sunrise $sunset

The suns.txt file is populated by a perl script that writes the number of seconds from midnight to sunrise and from midnight to sunset each day at 12AM. It is used to shade in the darkness period on the graph to see the effects of the sun on temps.

suns.pl blah blah

The verified_temps.sh bash shell script is responsible fro reading the sensor data, validating it and writing it to the DB:


verified_temps.sh
#!/bin/bash

DIR='/var/www/html/temps'
SLEEPTIME=3
cd $DIR

if [ -e $DIR/temps.ini ] ; then
   rm $DIR/temps.ini
fi
# Order is critical for the sensor ids: theatre, htpc, furnace, nook, garage, bedroom, attic, outside
IDS=( 10.C59A3B000800 22.738803000000 22.398703000000 10.791D6B000800 10.F38C3B000800 10.37D96A000800 28.81B350000000 10.B0FB3E010800 )
#0 starts from left on breadboard NEWIDS= ( 10.A4DD4B010800 10.53474C010800 10.A4DD4B010800 10.B0FB3E010800 )

PREVIOUS_TEMPS=( `rrdtool lastupdate $DIR/temps.rrd | grep ':' | sed -e 's/^.*: //g'` )
SENSORS=( `rrdtool lastupdate $DIR/temps.rrd | grep -v ":"` )
TIMESTAMP="`date "+%d/%m/%y %X"`"

echo "[House]" >> $DIR/temps.ini
i=0
save=0
for sensor in ${SENSORS[@]}
do
	#try charging it with a dir first.
	owdir -s htpc:3000 ${IDS[$i]} > /dev/null
	sleep 1
	temp[$i]=`echo "$(2>/dev/null owread -s htpc:3000 -C ${IDS[$i]}/temperature)" | tr -d ' '`
	if [ "${PREVIOUS_TEMPS[$i]}" = "U" ] ; then

		if [ -e $DIR/savedtemps ] ; then
			source savedtemps
		else
			echo "$TIMESTAMP:$LINENO: can't source savedtemps: `ls -al savedtemps`" >> $DIR/temps.log
		fi

		echo "$TIMESTAMP:line $LINENO: $sensor previous value was U, new value read [${temp[$i]}], changing previous to ${SAVED[$i]} from saved" >> $DIR/temps.log
		prev=`expr ${SAVED[$i]}`	
	else
		prev=`expr ${PREVIOUS_TEMPS[$i]}`
	fi	
		
	if [ -z "${temp[$i]}" ] ; then

		echo "$TIMESTAMP:line $LINENO: $sensor bad value read [${temp[$i]}] storing unknown" >> $DIR/temps.log
		temp[$i]="U"
		save=1
	else
		now=`expr ${temp[$i]}`
		deltatoobig=$(echo "(($prev - $now) >= 10) || (($prev - $now) <= -10)" | bc -l)
		#echo "temp = ${temp[$i]}, prev_temp = ${PREVIOUS_TEMPS[$i]}, deltatoobig = $deltatoobig"

		if [ $deltatoobig -ne 0 ] ; then
	echo "$TIMESTAMP:line $LINENO: $sensor bad value read [${temp[$i]}]: [$now] changed to previous temp [$prev] [${PREVIOUS_TEMPS[@]}]" >> $DIR/temps.log 
			temp[$i]="U"
			save=1
		fi

	fi
	echo "$sensor=${temp[$i]}" >> $DIR/temps.ini
	let i+=1
	sleep $SLEEPTIME
done
if [ $save -eq 1 ] ; then

	    if [ -e $DIR/savedtemps ] ; then
	 echo "$TIMESTAMP:line $LINENO: already have saved temps so don't re-save" >> $DIR/temps.log
	    else
			echo "$TIMESTAMP:line $LINENO: saving previous temps since bad value was read: [${PREVIOUS_TEMPS[@]}]" >> $DIR/temps.log
			echo "SAVED=(${PREVIOUS_TEMPS[@]})" > $DIR/savedtemps
			chmod 664 $DIR/savedtemps
        fi

elif [ -e $DIR/savedtemps ] ; then
	rm $DIR/savedtemps
fi
rrdtool update $DIR/temps.rrd N:${temp[0]}:${temp[1]}:${temp[2]}:${temp[3]}:${temp[4]}:${temp[5]}:${temp[6]}:${temp[7]}
cat $DIR/temps.ini
unix2dos -q $DIR/temps.ini
chmod 644 temps.ini

I threw in a dir and now an extra read (not in the script above) as I was having some problems with my outside temp. sensor. I found that these extra accesses resulted in fewer bad reads.

Charting The Data

Once you have some data in your temps.rrd database you can use rrdtool graph to generate some flashy graphs. Here's my script that first generates a graph showing ll of the sensors, then makes one for each sensor. I tried to pick hex colors that were easily visible on a web page. I also increased the thickness of the lines for better visibility. Currently I show sunrise/sunset with light/dark backgrounds on the graph. I only show the sunrise/sunset up to the monthly graphs since the times change to much to use one set of daily numbers for the full year.


graphtemp.sh
#!/bin/bash
HOMEDIR=/var/www/html/temps
cd $HOMEDIR
RRD="rrdtool"
DB="$HOMEDIR/temps.rrd"
SunsetHrs=$[ $2 / 3600 - 12 ]
SunsetMins=$[ ($2 - (($SunsetHrs + 12) * 3600)) / 60 ]
SunriseHrs=$[ $1 / 3600]
SunriseMins=$[ ($1 - (($SunriseHrs) * 3600)) / 60 ]
SUNS="Sunset\: $SunsetHrs\:$SunsetMins PM Sunrise\: $SunriseHrs\:$SunriseMins AM"
COMMENT="Generated on `date "+%d %m %y %H\:%M %Z"`"
IMAGE="temps.png"
watermark="Ronin Technologies Inc."
width=600
height=100
IDS=( 10.C59A3B000800 22.738803000000 22.398703000000 10.791D6B000800 10.F38C3B000800 10.37D96A000800 28.81B350000000 10.B0FB3E010800 )
#0 starts from left on breadboard NEWIDS= ( 10.A4DD4B010800 10.53474C010800 10.A4DD4B010800 10.B0FB3E010800 )
periods=("Last 3 Hours" "Last 24 Hours" "Last 2 Days" "Last Week" "Last Month" "Last Year")
period_index=0
for time in 3h 24h 48h 8days 1month 1year
do
	if [ $period_index -gt 4 ] ; then
		SUNS=""
	fi
	PERIOD="${periods[$period_index]}"
	TITLE="Paul and Helen's House Temps For $PERIOD"
	2>/dev/null rrdtool graph `echo $PERIOD | tr ' ' '_'`_$IMAGE -A -s -$time -e now -a PNG \
     -t "$TITLE" \
     -v "°C" \
	 -w $width \
	 -h $height \
	 -W "$watermark" \
	 -z \
	 -Y \
     DEF:Basement=$DB:theatre:AVERAGE \
     DEF:HTPC=$DB:htpc:AVERAGE \
	 DEF:Furnace=$DB:furnace:AVERAGE \
     DEF:Nook=$DB:nook:AVERAGE \
	 DEF:Garage=$DB:garage:AVERAGE \
     DEF:Bedroom=$DB:bedroom:AVERAGE \
     DEF:Attic=$DB:attic:AVERAGE \
	 DEF:Outside=$DB:outside:AVERAGE \
	 CDEF:nightplus=LTIME,86400,%,$1,LT,INF,LTIME,86400,%,$2,GT,INF,UNKN,Basement,*,IF,IF \
         CDEF:nightminus=LTIME,86400,%,$1,LT,NEGINF,LTIME,86400,%,$2,GT,NEGINF,UNKN,Basement,*,IF,IF \
     AREA:nightplus#CCCCCCAA \
     AREA:nightminus#CCCCCCAA \
     COMMENT:"\t\t\tnow       avg.      max.      min."\\n \
  LINE1:Basement#0000FF:"Theatre\t" \
      GPRINT:Basement:LAST:"%5.1lf °C" \
     GPRINT:Basement:AVERAGE:"%5.1lf °C" \
     GPRINT:Basement:MAX:"%5.1lf °C" \
     GPRINT:Basement:MIN:"%5.1lf °C"\\n \
     LINE1:HTPC#FF0000:"HTPC\t" \
     GPRINT:HTPC:LAST:"%5.1lf °C" \
     GPRINT:HTPC:AVERAGE:"%5.1lf °C" \
     GPRINT:HTPC:MAX:"%5.1lf °C" \
     GPRINT:HTPC:MIN:"%5.1lf °C"\\n \
     LINE1:Furnace#9900FF:"Furnace\t" \
     GPRINT:Furnace:LAST:"%5.1lf °C" \
     GPRINT:Furnace:AVERAGE:"%5.1lf °C" \
     GPRINT:Furnace:MAX:"%5.1lf °C" \
     GPRINT:Furnace:MIN:"%5.1lf °C"\\n \
     LINE1:Nook#33CCCC:"Nook\t" \
     GPRINT:Nook:LAST:"%5.1lf °C" \
     GPRINT:Nook:AVERAGE:"%5.1lf °C" \
     GPRINT:Nook:MAX:"%5.1lf °C" \
     GPRINT:Nook:MIN:"%5.1lf °C"\\n \
     LINE1:Garage#FF00FF:"Garage\t" \
     GPRINT:Garage:LAST:"%5.1lf °C" \
     GPRINT:Garage:AVERAGE:"%5.1lf °C" \
     GPRINT:Garage:MAX:"%5.1lf °C" \
     GPRINT:Garage:MIN:"%5.1lf °C"\\n \
     LINE1:Bedroom#ffff00:"Bedroom\t" \
     GPRINT:Bedroom:LAST:"%5.1lf °C" \
     GPRINT:Bedroom:AVERAGE:"%5.1lf °C" \
     GPRINT:Bedroom:MAX:"%5.1lf °C" \
     GPRINT:Bedroom:MIN:"%5.1lf °C"\\n \
     LINE1:Attic#865F00:"Attic\t" \
     GPRINT:Attic:LAST:"%5.1lf °C" \
     GPRINT:Attic:AVERAGE:"%5.1lf °C" \
     GPRINT:Attic:MAX:"%5.1lf °C" \
     GPRINT:Attic:MIN:"%5.1lf °C"\\n \
     LINE2:Outside#00FF00:"Outside\t" \
     GPRINT:Outside:LAST:"%5.1lf °C" \
     GPRINT:Outside:AVERAGE:"%5.1lf °C" \
     GPRINT:Outside:MAX:"%5.1lf °C" \
     GPRINT:Outside:MIN:"%5.1lf °C"\\n \
     COMMENT:"$SUNS"\\n \
     COMMENT:"$COMMENT"
	 
	colors=( "0000FF" "FF0000" "9900FF" "33CCCC" "FF00FF" "FFFF00" "865F00" "00FF00" )
	i=0
	for sensor in Theatre HTPC Furnace Nook Garage Bedroom Attic Outside 
	do
		name=`echo $sensor | tr [:upper:] [:lower:]`
		2>/dev/null rrdtool graph `echo $PERIOD | tr ' ' '_'`_${name}.png -A -s -$time -e now -a PNG \
		-t "$sensor Temperature For $PERIOD" \
		-v "°C" \
		-w $width \
		-h $height \
		-W "$watermark" \
		-z \
		-Y \
		DEF:$sensor=$DB:$name:AVERAGE \
		CDEF:nightplus=LTIME,86400,%,$1,LT,INF,LTIME,86400,%,$2,GT,INF,UNKN,$sensor,*,IF,IF \
		CDEF:nightminus=LTIME,86400,%,$1,LT,NEGINF,LTIME,86400,%,$2,GT,NEGINF,UNKN,$sensor,*,IF,IF \
		AREA:nightplus#CCCCCCAA \	
		AREA:nightminus#CCCCCCAA \
		COMMENT:"\t\t\tnow       avg.      max.      min."\\n \
		LINE2:$sensor#${colors[$i]}:"$sensor\t" \
		GPRINT:$sensor:LAST:"%5.1lf °C" \
		GPRINT:$sensor:AVERAGE:"%5.1lf °C" \
		GPRINT:$sensor:MAX:"%5.1lf °C" \
		GPRINT:$sensor:MIN:"%5.1lf °C"\\n \
		COMMENT:"$SUNS"\\n \
		COMMENT:"1-Wire Address ${IDS[$i]}"\\n \
		COMMENT:"$COMMENT"

		i=$[ $i + 1 ]
	done
		period_index=$[ $period_index + 1 ]
done

Once I've moved the furnace temperature sensor into the output plenum I also hope to show reddish backgrounds when the furnace is running. Currently its just sitting on the wall of the furnace room. You can really see a lot of information from the graphs and its been a fun project to do.

Links

Thanks to those that put in the effort to make some great open source tools that made this little project possible!

Maxim 1-Wire Devices
OWFS 1-Wire File System
RRDTool data logging and graphing system
error log

All | Theatre | HTPC | Furnace | Nook | Garage | Bedroom | Attic | How To Do This



(26716 reads) Printer Friendly Page
[ Return to Temps ]


Get Firefox!
The logos and trademarks used on this site are the property of their respective owners
We are not responsible for comments posted by our users, as they are the property of the poster
Interactive software released under GNU GPL, Code Credits, Privacy Policy