HOW COOL IS PERL

Document Sample
HOW COOL IS PERL Powered By Docstoc
					PROGRAMMING                      Perl: Temperature Sensors




Reading temperature sensors with Perl


HOW COOL IS PERL?
Linux lets tinkerers connect their hardware creations to computers.                           temperature values from the sensors.
                                                                                              One uses the FUSE user filesystem and
We’ll show you how you can use a simple Perl interface to check the                           maps the sensor data on the filesystem,
                                                                                              much like the /proc hierarchy in Linux.
temperature of your home or office while you’re away.                                         Figure 2 shows what kind of data gets
                                                                                              transferred from a dongle with two at-
BY MICHAEL SCHILLI                                                                            tached sensors. You can read the temper-
                                                                                              ature values and also the unique sensor
                                                                                              IDs, type designations, and other things.



I
    t’s not so long ago that users had to    about US$5 (for example from digikey.            The sensor housing contains a micro-
    write their own device drivers to add    com). It can handle a temperature range          controller with surprising capabilities.
    hardware. But with the standardiza-      between -55°C and +125°C. The                       Measured values can be read simply
tion of USB, and with fully-functional       DS9490R one-wire USB dongle, to which            by running cat against the FUSE-con-
hot-plugging support in kernel 2.6,          multiple sensors can be connected using          trolled files, although I used perl -ple1 to
things have become far easier.               western plugs, costs about US$15 to              add a newline to the output in Figure 2.
   The temperature sensor used in this       US$25 (at hobby-boards.com for exam-             Below the 10.B2A7C7000800/tempera-
article, a Dallas Semiconductor DS18S20      ple).                                            ture entry, you can see the value mea-
([3]), can be controlled via the one-wire      The owfs project ([2]) at Sourceforge          sured by the first sensor, 22.8125 degrees
bus, which is driven by a USB dongle at-     offers a number of interfaces for reading        Celsius. The second sensor with the ID
tached to the computer. The free owfs
control software at [2] can request data                                                                   Temperature Sensors
via a Perl interface. Instead of one-wire,
                                                                                                               DS18S20


                                                                                                                         DS18S20




the bus should really be called two-wire,             OWFS
as two thin copper wires (typically in a
single sheathing) connect the sensor to               Linux
the USB dongle (see Figure 1). At the                                    USB
other end there is a western connector          USB Hotplugging                     USB Dongle DS9490R
(RJ11), which plugs into the USB dongle.                                                                       One Wire Bus
   The DS18S20 temperature sensor is
available from most electronics stores for   Figure 1: The temperature sensors connect to the USB dongle via the one-wire bus.




58        ISSUE 65 APRIL 2006                   W W W. L I N U X - M A G A Z I N E . C O M
                                                               Perl: Temperature Sensors                  PROGRAMMING




Figure 2: Querying the one-wire USB dongle and attached tempera-    Figure 3: Two sensors connected to a one-wire USB dongle via a
ture sensors at the command line.                                   phone line splitter; the dongle plugs into the Linux system.


of 10.E0E3C7000800, which I left outside      vendor, Dallas, has all kinds of equip-      sors to the socket, I first had to solder a
overnight in San Francisco, returns a         ment on sale, including switches, and        long wire with a western plug onto the
cooler value of 14.4375 degrees (it never     voltage and current meters, which can        temperature sensor pins.
gets really cold in California). The type     all be attached to the same one-wire bus.      The easiest way of doing this is to buy
entry gives us the sensor type designa-                                                    a normal phone extension lead with
tion ("DS18S20"); this allows you to          Fire Up Your Soldering Iron                  plugs at both ends, and just cut off one
query the type of sensor connected to         The USB dongle has a western socket          end with wire cutters. Then strip the in-
the one-wire bus via the interface. The       (RJ11). To connect the three-legged sen-     sulation and you should see either two


                                                        advertisement
PROGRAMMING                         Perl: Temperature Sensors




                                                                            thicker (yellow)       opening the type entries for all the de-
                                                                            heat shrinkable        vices attached to the bus and checking
                                                                            tubing until you       for a DS18S20. OW::init('u') then tells
                                                                            can just see the tip   the module to contact the USB dongle,
                                                                            of the sensor, and     and the following call to the tempera-
                                                                            follow the same        tures() method returns pairs of sensor
                                                                            procedure to heat-     IDs and measured temperature values.
                                                                            shrink the tubing.     The destructor in line 49 calls OW::
                                                                            Figure 5 shows         finish() to close the connection to the
                                                                            you the finished       USB dongle.
                                                                            sensor; you can ei-       The script in Listing 2 shows a typical
                                                                            ther plug the tele-    sensor application. It uses the RRDTool::
Figure 4: Before soldering: green connects to the left pin and red to       phone connector        OO module from CPAN to provide an ob-
the right pin on the DS18S20.                                               directly into the      ject-oriented interface to Tobi Oetiker’s
                                                                            dongle, or you can     rrdtool.
                                                                            use an extension,         Without specifying any options, this
                                                                            if you have more       script reads all the sensors and stores the
                                                                            than one sensor        current temperature values in a round-
                                                                            (Figure 3).            robin database. If the database does not
                                                                                                   exist, the create method in line 37 of
                                                                            Finding the            the script creates the database with two
                                                                            Sensor                 data sources, “Inside” and “Outside,” for
                                                                          For test purposes,       the temperature inside the room and
                                                                          I will now leave         the temperature outside the window.
                                                                          one sensor in the        Lines 23 and 25 map the sensor IDs to
                                                                          room, while rout-        these intuitive names. The IDs are glob-
Figure 5: The finished sensor in the heat-shrunk tubing.                  ing the other            ally unique; any sensors you buy will
                                                                          through the win-         have different IDs.
or four thin wires.                              dow outdoors. The owfs project provides              The step parameter in Line 38 sets the
   We will just be using the red and             a generic Perl interface in the form of the       refresh interval to 300, that is, 300 sec-
green wires, so we can just cut the re-          OW module. The module in Listing 1                onds or 5 minutes; the database stores
maining ones. The temperature sensor             customizes the module to match the                5000 values before it starts to overwrite
has three pins, one of which is not              type of temperature sensor we will be             the older values. Reading and refreshing
needed – this is the pin on the right            using.                                            starts in Line 83 using the methods pro-
(when the flat side of the sensor case is          At first, we have no way of knowing             vided by OWTemp, and update() from
facing towards you with the pins point-          how many devices are attached to the              RRDTool::OO.
ing down). This pin is used to provide a         bus, which of them are temperature sen-              Lines 23 and 25 map the non-intuitive
power supply to the sensor, but the sen-         sors, and what their unique IDs are. The          sensor IDs to human-readable values:
sor is quite happy to use the current            discover method, which is called by the           “Outside” and “Inside.” It is easy to find
flowing through the data line ([8]). You         new constructor, finds this out for us by         out which sensor has which ID by just at-
can use wire cutters to remove the su-                                                             taching one sensor, and then viewing the
perfluous pin. Then go on to prepare the                                                           directory structure, as shown in Figure 2.
telephone wire by adding heat shrink-                                                                 If you call rrdmon with the -g parame-
able tubing, as shown in Figure 4. We                                                              ter, it creates a graph of the temperature
will be heating this tubing later to shrink                                                        curve from the RRD data from both sen-
it, making the appearance of the sensors                                                           sors and stores it in the /tmp/tempera-
easier to live with.                                                                               ture.png file (Figure 8). The inside sen-
   Now solder the green inner wire of the
telephone lead onto the left pin of the
DS18S20, and solder the red wire onto
the pin in the center. Hold your solder-
ing iron near the two red inner heat
shrinkable tubes to shrink them over the
stripped wire ends. If the tubing does
not shrink as much as you would like,            Figure 6: The gaim IM cli-
you can always add some insulating               ent speaks the IRC proto-
tape. This is just to prevent the bare           col. Just create an
wires touching and causing a short cir-          account, click “Online,”
cuit. Then adjust the position of the            and …                           Figure 7: … enter the command to enter the #sftemp channel.




60         ISSUE 65 APRIL 2006                       W W W. L I N U X - M A G A Z I N E . C O M
                                                                Perl: Temperature Sensors                   PROGRAMMING




sor is shown in red, and the outside sen-    an IRC client or the gaim IM
sor in blue.                                 client to visit the bot in its
                                             chatroom. Figure 6 shows the
Shouting Out                                 gaim configuration and Fig-
If you prefer remote, text-based access to   ure 7 gives you the command
the temperature output (say, you are on      for entering the chatroom,
vacation and want to know whether you        where the bot is waiting for
have left the oven on), you can write an     you to enter the "temp" key-       Figure 8: The graph drawn by RRDTool visualizes the
IRC bot like the one shown in Listing 3.     word. The bot listens to the       temperature curve for the inside and outside sensors.
The bot connects to the IRC server at irc.   conversation in the chat-
freenode.org and opens a chatroom            room, and if anyone says "temp", it ex-           Bot::BasicBot is a good example that
called #sftemp.                              tracts the last temperature values from        shows you how to complete highly com-
   To dispel your worries, so that you can   the RRD archive and sends them back to         plex tasks with a CPAN module and just
get on with your vacation, just launch       the chatroom (Figure 9).                       a bit of code. You just derive a class from

                                                 Listing 1: OWTemp.pm
 001 #############################            035   for my $dev (                            069     DEBUG "Found type $type";
 002 package OWTemp;                          036     @{ $self->{devices} }) {               070     next
 003 # Mike Schilli, 2005                     037                                            071       if defined $type
 004 # (m@perlmeister.com)                    038     my ($val) = owread(                    072       and $type ne
 005 #############################            039       "$dev/temperature");                 073       $self->{type};
 006                                          040     $val =~ s/\s//g;                       074     push @found, $entry;
 007 use Log::Log4perl qw(:easy);             041     push @temperatures,                    075   }
 008 use OW;                                  042       [ $dev, $val ];                      076   return @found;
 009                                          043   }                                        077 }
 010 #############################            044                                            078
 011 sub new {                                045   return @temperatures;                    079 #############################
 012 #############################            046 }                                          080 sub owread {
 013   my ($class, @options) = @_;            047                                            081 #############################
 014                                          048 #############################              082   my ($entry) = @_;
 015   my $self =                             049 sub DESTROY {                              083
 016     { type => "DS18S20", };              050 #############################              084   my @found = ();
 017                                          051   OW::finish();                            085
 018   bless $self, $class;                   052 }                                          086   my $result = OW::get($entry)
 019                                          053                                            087       or LOGDIE
 020   OW::init('u');                         054 #############################              088       "Failed to read $entry";
 021                                          055 sub discover {                             089
 022   $self->{devices} =                     056 #############################              090   DEBUG
 023     [ $self->discover() ];               057   my ($self) = @_;                         091       "owread result='$result'";
 024                                          058                                            092
 025   return $self;                          059   my @found = ();                          093   for my $entry (
 026 }                                        060                                            094       split /,/, $result) {
 027                                          061   for my $entry (owread("")) {             095     $entry =~ s#/$##;
 028 #############################            062     DEBUG                                  096     push @found, $entry;
 029 sub temperatures {                       063     "Found top entry '$entry'";            097   }
 030 #############################            064     next if $entry !~ /^\d/;               098
 031   my ($self) = @_;                       065                                            099   return @found;
 032                                          066     my ($type) =                           100 }
 033   my @temperatures = ();                 067       owread("$entry/type");               101
 034                                          068                                            102 1;




                                                W W W. L I N U X - M A G A Z I N E . C O M             ISSUE 65 APRIL 2006        61
PROGRAMMING                       Perl: Temperature Sensors




Bot::BasicBot, and define the said()       article went to print, only the latest          ing ls -l /usr/local/bin/fusermount).
method for the class; the method is        version from the owfs project’s CVS             Then enter the following:
called when someone says something in      repository worked; enter cvs -d:pserver:
the chatroom. said() receives the mes-     anonymous @cvs.sourceforge.net:/cvs-             ./bootstrap
sage as a parameter, and can then check    root/owfs co owfs to get the version.            ./configure
if the bot wants to respond, and either    There is also a tarball at [5] that has          make
return a message or undef. If you have     been shown to work with the scripts in
version 0.65 of Bot::BasicBot, you will    this issue.                                     to start the build. Then run make install
see a message when you launch that            You need the latest version of SWIG          to install the command line tool. The fol-
says Use of ->new() is deprecated,         ([7]) to install owfs; the developer ver-       lowing steps install the OW Perl module:
please use spawn(), but you can just       sion 1.3.27 worked fine. If you will be
ignore the warning.                        installing the owfs command line tool to         cd module/swig/perl5
                                           access the one-wire bus via the com-             perl Makefile.PL
Installation                               mand line (Figure 2), along with the Perl        make install
The owfs software distribution, which      interface, you also require the FUSE user
uses a USB interface to talk on the one-   filesystem from [4], unless your distribu-      from the owfs distribution. A cronjob
wire bus, is available at [2]. When this   tion happens to provide it (test by enter-      which runs every five minutes fills the

                                                   Listing 2: rrdmon
 01 #!/usr/bin/perl -w                      33    RRDTool::OO->new(                         65     },
 02 #############################           34   file => $RRDDB);                           66     draw => {
 03 # rrdmon -Temp sensor monitor           35                                              67       type    => "line",
 04 # Mike Schilli, 2005                    36 # Create a round-robin DB                    68       color    => 'FF0000',
 05 # (m@perlmeister.com)                   37 $rrd->create(                                69       dsname => 'Inside',
 06 #############################           38   step            => 300,                    70       legend => 'Inside',
 07 use strict;                             39   data_source => {                           71     },
 08 use Getopt::Std;                        40    name => "Outside",                        72     width         => 300,
 09 use Log::Log4perl qw(:easy);            41    type => "GAUGE"                           73     height        => 75,
 10 use Sysadm::Install qw(:all);           42   },                                         74     lower_limit => 0,
 11 use RRDTool::OO;                        43   data_source => {                           75   );
 12 use OWTemp;                             44    name => "Inside",                         76
 13                                         45    type => "GAUGE"                           77 } else {
 14 Log::Log4perl->easy_init(               46   },                                         78
 15                         $DEBUG);        47   archive => { rows => 5000 }                79   my $ow = OWTemp->new();
 16                                         48    )                                         80
 17 my $RRDDB =                             49    unless -f $RRDDB;                         81   my %values = ();
 18    "/tmp/temperature.rrd";              50                                              82
 19 my $GRAPH =                             51 if ($o{g}) {                                 83   for my $station (
 20    "/tmp/temperature.png";              52                                              84     $ow->temperatures())
 21                                         53   # Draw graph in a PNG image                85   {
 22 my %sensors = (                         54   $rrd->graph(                               86     my ($dev, $temp) =
 23   "10.E0E3C7000800" =>                  55    start => time() - 24 *                    87        @$station;
 24      "Outside",                         56        3600 * 3,                             88     $values{ $sensors{$dev} } =
 25   "10.B2A7C7000800" =>                  57    image               => $GRAPH,            89        $temp;
 26      "Inside",                          58    vertical_label =>                         90   }
 27 );                                      59        'Temperatures',                       91
 28                                         60    draw => {                                 92   $rrd->update(
 29 getopts("g", \my %o);                   61     color     => '00FF00',                   93     time      => time(),
 30                                         62     type      => "line",                     94     values => \%values
 31 # Constructor                           63     dsname => 'Outside',                     95   );
 32 my $rrd =                               64     legend => 'Outside',                     96 }




62        ISSUE 65 APRIL 2006                 W W W. L I N U X - M A G A Z I N E . C O M
                                                                  Perl: Temperature Sensors                   PROGRAMMING




                                                            For the bot, you need Bot::Ba-
                                                            sicBot, which automatically
                                                                                                                 INFO
                                                            installs the POE distribution.      [1] Listings for this article:
                                                                                                    http://www.linux-magazine.com/
                                                            Be Secure                               Magazine/Downloads/65/Perl
                                                           When you plug the USB don-           [2] The one-wire file system project:
                                                           gle into your computer, the              http://owfs.sourceforge.net
                                                           hot-plug mechanism creates           [3] DS18S20 temperature sensor:
                                                           a USB device, something like             http://www.maxim-ic.com/quick_
                                                           root -rw-r--r-- /proc/bus/usb/           view2.cfm/qv_pk/2815
                                                           003/008. As owfs needs write         [4] The Fuse project website:
                                                           access to the dongle, root               http://fuse.sourceforge.net
                                                           privileges are required to           [5] CVS snapshot for owfs:
Figure 9: The IRC bot responds with the current temper-    read the temperature values.             http://perlmeister.com/devel/owfs-2.
atures.                                                    An executable hot-plug script            2p0RC-cvssnap.tgz
                                                           in /etc/hotplug/usb/ds2940           [6] DS9490R one-wire USB dongle data-
RRD archive: */5 * * * * cd /path;              gives you a workaround to avoid run-                sheet: http://pdfserv.maxim-ic.com/
./rrdmon; ./rrdmon -g;.                         ning the scripts as root:                           en/ds/DS9490-DS9490R.pdf
   You need both rrdmon and the OW-                                                             [7] SWIG developer version :
Temp.pm Perl module in your /path. The            #!/bin/bash                                       http://prdownloads.sourceforge.net/
files created by rrdmon are stored in             # /etc/hotplug/usb/ds2940                         swig/swig-1.3.27.tar.gz
/tmp, but you can change the path vari-           chmod a+rwx "${DEVICE}"                       [8] One-wire bus:
ables in rrdmon (lines 18/20). As each                                                              http://en.wikipedia.org/wiki/1-Wire
sensor has a unique ID, you need to             To allow the hotplugger to run the script
modify lines 23 and 25 to reflect your          when the dongle is inserted, append the
environment. Use the approach shown             following code to /etc/hotplug/usb.user-        0x00 0x00 0x00 0x00U
in Figure 2 to discover the sensor IDs.         map:                                            0x00000000
   The remaining modules, Sysadm::
Install and Log::Log4perl, are available          # /etc/hotplug/usb.usermap                   This allows all our scripts to run with
from CPAN. RRDTool::OO requires either            # DS2940 one-wire USB device                 non-privileged user IDs, which keeps the
a working rrdtool installation, or it will        ds2940 0x0003 0x4fa 0x2490 U                 host security people from jumping on
try to download a working installation.           0x0000 0x0000 0x00 0x00 U                    your back. ■

                                                      Listing 3: tempbot
 01 #!/usr/bin/perl -w                         21      unless $mesg->{body} eq                  41
 02 #############################              22      "temp";                                  42     for (my $i=0;
 03 # tempbot-Temp sensor IRC bot              23                                               43          $i < @$dsnames; $i++){
 04 # Mike Schilli, 2005                       24    my $rrd =                                  44       $string .=
 05 # (m@perlmeister.com)                      25      RRDTool::OO->new(file =>                 45         sprintf "%10s: %.1f\n",
 06 #############################              26         "/tmp/temperature.rrd");              46         $dsnames->[$i],
 07 use strict;                                27                                               47         $values[$i];
 08 use Bot::BasicBot;                         28    my $dsnames =                              48     }
 09                                            29      $rrd->meta_data(                         49     return $string;
 10 package TempBot;                           30         "dsnames");                           50   }
 11 use base qw( Bot::BasicBot );              31                                               51 }
 12 use Log::Log4perl qw(:easy);               32    $rrd->fetch_start(                         52
 13 use RRDTool::OO;                           33     start => time() - 5 * 60,                 53 $^W = undef;
 14                                            34     end     => time()                         54
 15 #############################              35    );                                         55 TempBot->new(
 16 sub said {                                 36                                               56   server =>
 17 #############################              37    my $string;                                57       'irc.freenode.net',
 18   my ($self, $mesg) = @_;                  38                                               58   channels => ['#sftemp'],
 19                                            39    while (my ($time, @values) =               59   nick       => 'tempbot',
 20   return                                   40     $rrd->fetch_next()) {                     60 )->run();




                                                  W W W. L I N U X - M A G A Z I N E . C O M               ISSUE 65 APRIL 2006            63