NSLU2-Linux
view · edit · print · history

DigiTemp is a Linux package for interfacing to Maxim/Dalsemi 1-Wire and iButton devices. It supports reading from the following devices:

  • DS18S20 (and the older obsolete DS1820) Digital Thermometer
  • DS18B20 High-Precision 1-Wire Digital Thermometer
  • DS1822 Econo 1-Wire Digital Thermometer
  • DS2438 Smart Battery Monitor
  • DS2409 1-Wire coupler (used in 1-Wire hubs)
  • DS2422 1-Wire Temperature/Data Logger with 8kB Datalog Memory
  • DS2423 4 kbit 1-Wire RAM with Counter

See Leon Kos' page for building a serial interface for the 1-Wire bus or search the Maxim website for the DS9490 USB to 1-Wire adapter based on the DS2490 USB to 1-Wire bridge chip.

Details about 1-Wire RS-232 interface and general informations about 1-wire can be found on http://pdfserv.maxim-ic.com/en/an/tb1.pdf(approve sites)

Temperature graphing server and remote clients

One can create http server without PHP support to display temperatures. Good alternative to PHP is to use shell CGI programs that can be executed by any http server including busybox_httpd. Server provides two functionalities:

  1. Stores temperatures from clients into RRDatabase?
  2. Displays graphs

Clients

Clients can be other embeded routers with digitemp installed. Client can also be located on server. /opt/etc/init.d/S98digitemp client script that sends temperature by wget looks like:

#!/bin/sh
PIDFILE=/var/run/digitemp.pid
PATH=/sbin:/bin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin
exit 0

start ()
{
        if [ -f $PIDFILE ]; then
                echo "Warning : $PIDFILE still present. Unclean shutdown ?"
                kill -9 `cat $PIDFILE` 2>/dev/null
                rm -f $PIDFILE 2>/dev/null
                killall digitemp
        fi

        cd /opt/etc

        echo -n "Starting digitemp... "
        if [ ! -f /opt/etc/.digitemprc ]; then
                digitemp -i
        fi

        digitemp_DS2490 -a -q -o"%4.1C" -n 0 -d 300 | while true; do
                read t3;
                if [ "$t3" != "Found DS2490 device #1 at 003/002" ]; then
                wget -q "http://192.168.1.1/cgi-bin/t.cgi?sensor=3;value=$t3" -O - >/dev/null 2>&1
                fi
        done &
        echo $! > $PIDFILE
        echo "done"
}

stop ()
{
        if [ -f $PIDFILE ]; then
                kill -9 `cat $PIDFILE` 2>/dev/null
                rm -f $PIDFILE 2>/dev/null
                killall digitemp
        fi
        echo "done"
}

case "$1" in
        start)
                start
                ;;
        stop)
                stop
                ;;
        restart)
                stop
                sleep 1
                start
                ;;
        *)
                echo "Usage: $0 (start|stop|restart)"
                exit 1
                ;;
esac

In this client script the following things can noted:

  1. HTTP server IP address is 192.168.1.1
  2. Client calls sctipt t.cgi script and appends the sensor number and actual temperature to call
  3. This client uses USB digitemp sensor. For serial digitem the 5 minute loop looks like
./digitemp -a -q -o"%4.1C" -n 0 -d 300 | while true; do
        read t1;
         wget -q "http://192.168.1.1/cgi-bin/t.cgi?sensor=2;value=$t1" -O - > /dev/null 2>&1
done &

RRDatabase? script for serving client calls

This script is located in /usr/local/www/cgi-bin/t.cgi and server calls from clients. For every wget from client it stores temperature int RRDatabase?. This means that rrdtool package is required for handling RR databases. Each sensor requires its own database. This choice prevents database locks with simultaneous writes. For 3 sensors 3 databases are required. The following @@t.cgi@ script takes care of updating each sensor database. If database does not exist it creates a new one with prescribed defaults for 5 minute update and averaging for up to 2 years:

#!/bin/sh
#
# Expected sensor update every 5 minutes
#

echo "Content-type: text/plain"
echo ""

sensor="none"

eval ${QUERY_STRING}

if [ ${sensor} != 3 -a ${sensor} != 2 ]; then
        echo "Invalid sensor number"
        exit 2
else
        echo "Accepted sensor ${sensor} with value ${value}"
fi

test -d /opt/var/lib/rrd/digitemp || mkdir -p /opt/var/lib/rrd/digitemp

if [ ! -e /opt/var/lib/rrd/digitemp/sensor-${sensor}.rrd ]; then
        /opt/bin/rrdtool create /opt/var/lib/rrd/digitemp/sensor-${sensor}.rrd \
        DS:temp:GAUGE:600:-40:100\
        RRA:AVERAGE:0.5:1:600    \
        RRA:AVERAGE:0.5:6:700    \
        RRA:AVERAGE:0.5:24:775   \
        RRA:AVERAGE:0.5:288:797  \
        RRA:MIN:0.5:1:600        \
        RRA:MIN:0.5:6:700        \
        RRA:MIN:0.5:24:775       \
        RRA:MIN:0.5:288:797      \
        RRA:MAX:0.5:1:600        \
        RRA:MAX:0.5:6:700        \
        RRA:MAX:0.5:24:775       \
        RRA:MAX:0.5:288:797
fi

test -e /tmp/sensor-${sensor}.rrd || cp /opt/var/lib/rrd/digitemp/sensor-${sensor}.rrd /tmp
test -e /tmp/sensor-2.rrd || cp /opt/var/lib/rrd/digitemp/sensor-2.rrd /tmp


/opt/bin/rrdupdate /tmp/sensor-${sensor}.rrd  N:${value}
end=$(/opt/bin/rrdtool last /tmp/sensor-${sensor}.rrd )
e=$((end/7200*7200))
s=$((e-86400))
min=$(/opt/bin/rrdtool fetch /tmp/sensor-${sensor}.rrd MIN  -s $s -e $e -r 7200 |
  awk '/[0-9]+: [-0-9.e+]+/ {if (!n) {min=$2; n++; next}; if ($2 < min){min =$2};}
  END{printf("%.1f\n", min)}')
max=$(/opt/bin/rrdtool fetch /tmp/sensor-${sensor}.rrd MAX  -s $s -e $e -r 7200 |
  awk '/[0-9]+: [-0-9.e+]+/ {if (!n) {max=$2; n++; next}; if ($2 > max){max =$2};}
  END{printf("%.1f\n", max)}')

echo -e "${value}\n${min}\n${max}" > /tmp/sensor-${sensor}.stat

[ $((end%86400)) = 0 ] && cp /tmp/sensor-${sensor}.rrd /opt/var/lib/rrd/digitemp/

if [ ${sensor} = 2 ] ; then
    PREJETO=$(date +%H:%M)
    echo "var prejeto='${PREJETO}';" > /tmp/temperatura.js
    echo "var temperatura='${value}';" >> /tmp/temperatura.js
fi

#set > /tmp/digitemp.log

Script assumes that valid sensor numbers are 2 and 3. At the end of the script one can also notice that this script after it updates a database it also creates files for 24h sensor statistics and saves if into /tmp directory for fetching this /tmp/sensor-?.stat files with remote display clients with HTTP protocol. In addition this script for sensor 2 creates simple JavaScript? file /tmp/temperatura.js for inclusion in web page that can show last temperature update with the following HTML sippet:

<script type="text/javascript" src="temperatura.js"> </script>
Temperature at  <script type="text/javascript"> <!--
                             document.writeln(prejeto + " uri : ");
                            document.writeln( temperatura + "&deg;C");
                          // -->
                          </script>

It should be noted that temperatura.js should be properly symlinked to /tmp where it actually resides. One can also notice that actual databases reside in /tmp directory that is not persistent. But databases are copied to disk location every 86400 second. Reason for such behaviour is that server uses USB key for Optware packages. To minimize Flash memory wear out databases are saved once a day!. If you reboot a server meanwhile, all updates are lost. I sugest the following /etc/profile function to force file backup just before reboot:

reboot() { cp /tmp/rssi-*.rrd /opt/var/lib/rrd/rssi;
        cp /tmp/sensor-*.rrd /opt/var/lib/rrd/digitemp;
          ifdown wan 2>&1 >/dev/null ; /sbin/reboot; }

RRD graphing with rrdcgi

Thhe following /usr/local/www/cgi-bin/vreme.cgi displays graphs on demand. This means that graphs are not created until someone requests a page with http://192.168.1.1/cgi-bin/vreme.cgi(approve sites). It takes some seconds before graphs are created. But this also means that there is no need for cron jobs for pre-creating graphs and thus saves CPU. Default graph is for 2 days. Later user can selec wider range for up to two years averaging.

#!/opt/bin/rrdcgi
<html>
<head>
<title>Vreme v Begunjah</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2">
<link href="/vreme/default.css" rel="stylesheet" type="text/css">
<RRD::SETENV LANG sl_SI>
</head>
<body>
<h1>Potek temperature v Begunjah in Topolu</h2>
<form>
<button type=submit name=range value="">Danes</button>
<button type=submit name=range value="-1day">Dva dni</button>
<button type=submit name=range value="-1week">Zadnji teden</button>
<button type=submit name=range value="-1month">Zadnji mesec</button>
<button type=submit name=range value="-1year">Eno leto</button>
</form>
<RRD::GRAPH /tmp/digitemp.png --title="Temperatura"
        --width 640 --height 200 --slope-mode --start -1day<RRD::CV range>
        --imginfo '<IMG SRC=/vreme/%s WIDTH=%lu HEIGHT=%lu ALT="RRD prikaz">'
                 DEF:beg=/tmp/sensor-3.rrd:temp:AVERAGE
                 DEF:top=/tmp/sensor-2.rrd:temp:AVERAGE
                 COMMENT:"           "
                 COMMENT:"  Min       Max      Povp     Zadnja\n"
                 LINE1:beg#00a000:"Begunje"
                 GPRINT:beg:MIN:" %5.2lf C"
                 GPRINT:beg:MAX:" %5.2lf C"
                 GPRINT:beg:AVERAGE:" %5.2lf C"
                 GPRINT:beg:LAST:" %5.2lf C\n"
                 LINE1:top#a000a0:"Topol  "
                 GPRINT:top:MIN:" %5.2lf C"
                 GPRINT:top:MAX:" %5.2lf C"
                 GPRINT:top:AVERAGE:" %5.2lf C"
                 GPRINT:top:LAST:" %5.2lf C\n"
                 >
<p>Last temperature update at: <RRD::TIME::LAST /tmp/sensor-3.rrd "%A %e. %B %Y ob %H:%M"> </p>
<p>MIN, MAX and AVG temperatures shown are for displayed rande</p>
<p>Buttons does not work with  Windows Internet Explorer 7, although they are correctly HTML coded.
Firefox works as intended!</p>
</body>
</html>

One can set local language for graphs created as shown with <RRD::SETENV LANG sl_SI> for Slovenian.

---

view · edit · print · history · Last edited by oleo.
Based on work by oleo, Petr Jakes, and tman.
Originally by marceln.
Page last modified on June 03, 2008, at 09:36 PM