
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");
}'
