Raspberry PI and OneWire temperature sensors

manifold-2014
We have installed underfloor heating in our House-on-Hudson, there are 14 independent loops, of various length, and one circulation pump. Two Manifolds with individual valves connects the input and output for each loop, each have a very bad flow-meter, and possibility for a motor-valves. Hence it would be possible to control the heat delivery independently for each loop under computer control, if you so desire, I don’t (at the moment).

Normally what people do is to tune the system once, when it is installed, so that heat is distributed evenly to all loops, using the flow meters as guide. I do this by adjusting the system so that the return temperature on all the loops will be more or less the same. This has the added benefit that the temperature of the water returning to the boiler is as low as possible, on a condensing-boiler, this gives a very good economy.

I read the temperatures with 14 ds18b20, you can get them on amazon or ebay for less than $2 a piece, The brain is a Raspberry Pi $20-$35 (any A/A+/B/B+ will do), USB wifi $3 on Ebay, SD-card $5, USB-charger $5. The total is less than $60. If you want a screen on it too, a 4.3″ car dvd-lcd will set you back less than $15.

Linux and the Raspberry Pi has good support for one-wire devices, just connect them to the right pin, all in parallel, add a pullup resistor and the hardware part is done with.

For the software, I like to do most of my work on the commandline, using one-liners, It is amazing what you can do in one line. Let us play around a bit:

pi@raspa ~ $ cd /sys/bus/w1/devices
pi@raspa /sys/bus/w1/devices $ ls -l
total 0
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e1305b -> ../../../devices/w1_bus_master1/28-000003e1305b
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e13462 -> ../../../devices/w1_bus_master1/28-000003e13462
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e139e2 -> ../../../devices/w1_bus_master1/28-000003e139e2
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e13cf9 -> ../../../devices/w1_bus_master1/28-000003e13cf9
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e144b0 -> ../../../devices/w1_bus_master1/28-000003e144b0
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e147b1 -> ../../../devices/w1_bus_master1/28-000003e147b1
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e15681 -> ../../../devices/w1_bus_master1/28-000003e15681
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e15f03 -> ../../../devices/w1_bus_master1/28-000003e15f03
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e16853 -> ../../../devices/w1_bus_master1/28-000003e16853
lrwxrwxrwx 1 root root 0 Nov 10 12:17 28-000003e169ff -> ../../../devices/w1_bus_master1/28-000003e169ff
lrwxrwxrwx 1 root root 0 Nov 10 12:50 28-000003e1729e -> ../../../devices/w1_bus_master1/28-000003e1729e
lrwxrwxrwx 1 root root 0 Nov 10 12:50 28-000003e173a0 -> ../../../devices/w1_bus_master1/28-000003e173a0
lrwxrwxrwx 1 root root 0 Nov 10 12:50 28-000003e1752d -> ../../../devices/w1_bus_master1/28-000003e1752d
lrwxrwxrwx 1 root root 0 Nov 10 12:50 28-000003e17b6c -> ../../../devices/w1_bus_master1/28-000003e17b6c
lrwxrwxrwx 1 root root 0 Nov 10 12:17 w1_bus_master1 -> ../../../devices/w1_bus_master1
pi@raspa /sys/bus/w1/devices $ cat 28-000003e1305b/w1_slave 
40 01 4b 46 7f ff 10 10 1d : crc=1d YES
40 01 4b 46 7f ff 10 10 1d t=20000
pi@raspa /sys/bus/w1/devices $ 

You see 14 sensors, the temperature on the first one is 20 degree Celcius.

I have made a little script which also does a little data-compression, in a still human-readable way. The main reason is that if there is activity on a single sensor, it is easy to spot.

#!/bin/bash
# Read all the ds18b20 and append data to templog
# Trying to be nice to flash-drive we work in /tmp/ow which is supposed to be on ramdisk
# we copy data to permanent storage every hour.
#
# IDS:ID_1:ID_2:...   where ID_? is the last 4 digits of the ds18b20 address
# epoch:t1*1000:t2*1000:...
# +12:+1:-3     difference since last full reading, -3 = -3*62.5
#
# the following awk-script will "unpack" it
# awk -F: '
# /^[0-9]/ {
#       epoch=$1;
#       print $0;
#       for (i=2;i<=NF;i++) base[i]=$i;
# }
# /^+/ {
#       printf("%d",epoch+$1);
#       for (i=2;i<=NF;i++) printf(":%d",base[i]+$i*62.5);
#       printf "\n";
# }'
#
WORK=/tmp/ow
PERM=/home/ow
MAX_DIFF=500    # +/- 0.5 Celcius
MAX_SECS=600    # 10 minutter

DS18B20_BASE=28-000003e1
declare -g -A TEMPS

save_and_roll_log()     # $HOUR_last $HOUR
{
        DIR=`dirname $1`
        FILE=`basename $1`
        mkdir -p $PERM/$DIR
        if [ -L $PERM/$1 ]; then
                rm -f $PERM/$1
        fi
        if [ -f $WORK/$FILE ]; then
                mv $WORK/$FILE  $PERM/$1
        fi
        DIR=`dirname $2`
        FILE=`basename $2`
        mkdir -p $WORK
        mkdir -p $PERM/$DIR
        rm -f $PERM/$2
        ln -s $WORK/$FILE $PERM/$2
        LOG=$WORK/$FILE
}

wr_log()
{
        if [ x$VERBOSE != x ]; then
                echo $*
        fi
        echo $* >> $LOG
}

cd /sys/bus/w1/devices
IDS_last=x
HOUR_last=x
ATTEMPT_last=x
EPOCH_last=`date +%s`
sec=0
while true;do
        ATTEMPT=`cat w1_bus_master1/w1_master_attempts`
        if [ $ATTEMPT = $ATTEMPT_last ];then
                sleep 1
                continue
        fi
        ATTEMPT_last=$ATTEMPT

        EPOCH=`date +%s`
        HOUR=`date +%Y/%m/%d/%Y%m%d%H`
        if [ $HOUR != $HOUR_last ]; then
                save_and_roll_log $HOUR_last $HOUR
                HOUR_last=$HOUR
                IDS_last=x      # start new
        fi

        DEVS=`sort w1_bus_master1/w1_master_slaves`
        IDS=`echo $DEVS | sed -e "s,$DS18B20_BASE,,g" -e 's/ /:/g'`

        sec=$(($EPOCH - $EPOCH_last))
        if [ $IDS != $IDS_last -o $sec -gt $MAX_SECS ]; then
                ALL=1
        else
                ALL=0
        fi
        TS=""
        IDS_=""
        CHANGED=0
        for i in $DEVS; do
                ID=""
                T=`awk -F= '/NO/ {print "%";exit} /t=/ {print $2}' < $i/w1_slave`
                if [ x$T = x -o x$T = x% ]; then
                        T="%"
                elif [ x$T = x85000 ]; then
                        T="%"
                elif [ $ALL = 1 ]; then
                        TEMPS[$i]=$T
                elif [ x${TEMPS[$i]} != x ]; then       # differential coding
                        DIFF=$(($T-${TEMPS[$i]}))
                        #echo "$i: $T-${TEMPS[$i]}=$DIFF"
                        if [ $DIFF = 0 ]; then
                                T=""
                        elif [ $DIFF -gt -$MAX_DIFF -a $DIFF -lt $MAX_DIFF ]; then
                                T="$(($DIFF/62))"
                        else
                                TEMPS[$i]=$T    # only update when change is more than $MAX_DIFF
                                ID=`echo $i | sed "s,$DS18B20_BASE,,g"`
                                CHANGED=1
                        fi
                else
                        TEMPS[$i]=$T
                fi
                TS=$TS:$T
                IDS_=$IDS_:$ID
        done

        if [ $CHANGED = 1 ]; then
                wr_log IDS_$IDS_
        fi
        if [ $IDS != $IDS_last ]; then
                wr_log IDS:$IDS
                IDS_last="$IDS"
        fi
        if [ $ALL = 1 ]; then
                wr_log "$EPOCH$TS"
                EPOCH_last=$EPOCH
        else
                wr_log "+$sec$TS"
        fi

done

Here is how the output looks like

pi@raspa ~ $ head -24 ../ow/2014/12/03/2014120305
IDS:305b:3462:39e2:3cf9:44b0:5681:5f03:6853:69ff:729e:73a0:752d:7b6c
1417600809:19437:19437:36687:19500:36562:19437:19625:19437:19562:19812:19312:18750:19437
+11:1::1::1:::1:-1::::
+22:1::1::1:::1::::1:
+33:1::2::1:::1:::::
+44:1::2::2:1::1::::1:
+56:1::3::2:1:%:::::1:1
+67:1::3::2:1:1:1:::1:1:1
+78:1::3::3:1:1:1:::1:1:1
+89:1:1:4:1:3:1:1:1:1:::1:1
+100:2:1:%:1:3:1:1:1:1:1::1:1
+111:1:1:4:1:4:1:1:1:1:1:1:1:1
+123:2:1:5:1:4:1:1:2:1:1::1:1
+134:2:2:5:1:4:2:1:2:1:1:%:1:2
+145:2:2:6:1:5:2:2:1:1:1:1:1:1
+156:2:1:6:1:5:2:2:2:1:1:1:1:%
+167:2:2:7:1:5:2:2:2:1:1:1:2:2
+178:2:2:7:1:%:2:2:2:%:1:1:2:2
+190:2:1:7:2:6:%:2:2:2:2:2:2:1
IDS_:::39e2::::::::::
+201:2:2:37187:2:6:2:2:3:2:1:1:1:2
+212:3:2::2:6:2:2:2:2:2:2:2:2
+223:3:2::2:6:2:2:3:2:2:2:2:2
+234:3:2::2:6:3:2:3:2:2:2:2:3

You see that every 11-12 seconds a new set of values is ready, and not much is changing, only sensor 39e2 has changed more than half a degree Celcius.

Below decoder in awk, and the result:

pi@raspa ~ $ head -24 ../ow/2014/12/03/2014120305 |awk -F: '
/^I/ { print $0; }
/^[0-9]/ {
    epoch=$1;
    print $0;
    for (i=2;i<=NF;i++) base[i]=$i;
}
/^+/ {
    printf("%d",epoch+$1);
    for (i=2;i<=NF;i++) printf(":%d",(base[i]+$i*62.5));
    printf "\n";
}'
IDS:305b:3462:39e2:3cf9:44b0:5681:5f03:6853:69ff:729e:73a0:752d:7b6c
1417600809:19437:19437:36687:19500:36562:19437:19625:19437:19562:19812:19312:18750:19437
1417600820:19499:19437:36749:19500:36624:19437:19625:19499:19499:19812:19312:18750:19437
1417600831:19499:19437:36749:19500:36624:19437:19625:19499:19562:19812:19312:18812:19437
1417600842:19499:19437:36812:19500:36624:19437:19625:19499:19562:19812:19312:18750:19437
1417600853:19499:19437:36812:19500:36687:19499:19625:19499:19562:19812:19312:18812:19437
1417600865:19499:19437:36874:19500:36687:19499:19625:19437:19562:19812:19312:18812:19499
1417600876:19499:19437:36874:19500:36687:19499:19687:19499:19562:19812:19374:18812:19499
1417600887:19499:19437:36874:19500:36749:19499:19687:19499:19562:19812:19374:18812:19499
1417600898:19499:19499:36937:19562:36749:19499:19687:19499:19624:19812:19312:18812:19499
1417600909:19562:19499:36687:19562:36749:19499:19687:19499:19624:19874:19312:18812:19499
1417600920:19499:19499:36937:19562:36812:19499:19687:19499:19624:19874:19374:18812:19499
1417600932:19562:19499:36999:19562:36812:19499:19687:19562:19624:19874:19312:18812:19499
1417600943:19562:19562:36999:19562:36812:19562:19687:19562:19624:19874:19312:18812:19562
1417600954:19562:19562:37062:19562:36874:19562:19750:19499:19624:19874:19374:18812:19499
1417600965:19562:19499:37062:19562:36874:19562:19750:19562:19624:19874:19374:18812:19437
1417600976:19562:19562:37124:19562:36874:19562:19750:19562:19624:19874:19374:18875:19562
1417600987:19562:19562:37124:19562:36562:19562:19750:19562:19562:19874:19374:18875:19562
1417600999:19562:19499:37124:19625:36937:19437:19750:19562:19687:19937:19437:18875:19499
IDS_::::39e2::::::::::
1417601010:19562:19562:2360874:19625:36937:19562:19750:19624:19687:19874:19374:18812:19562
1417601021:19624:19562:36687:19625:36937:19562:19750:19562:19687:19937:19437:18875:19562
1417601032:19624:19562:36687:19625:36937:19562:19750:19624:19687:19937:19437:18875:19562
1417601043:19624:19562:36687:19625:36937:19624:19750:19624:19687:19937:19437:18875:19624
pi@raspa ~ $ 

This little awk-script will format it even more human readable

#!/bin/sh
cat $* |awk -F: '
/^[0-9]/ {sec=epoch=$1}
/^+/ {sec=epoch+$1}
/^IDS/ {
	changed=1;
	for (i=2;i<=NF;i++) {
		sensor[i]=$i;
	}
	next
}
changed==1 {
	changed=0;
	printf("%s  ",strftime("%H:%M %d.%b %Y ", sec));
	for (i=2;i<=NF;i++) {
		if (sensor[i] != "") {
			printf("%4s=%2.1f ", sensor[i], $i/1000);
			sensor[i]="";
		} else {
			printf("          ");
		}
	}
	printf("\n");
}'
This entry was posted in HomeAutomation, Linux, Raspberry Pi. Bookmark the permalink.