http://www.developer.com/

Back to article

Play it Cool: Solving Thermal Problems


August 30, 2006

Thermal problems have been a major bane of computers since the very first computer was plugged into AC power. This series explores how a system can be built to monitor and report room temperatures using methods that can be duplicated for a variety of uses.

In Review

The previous two articles in this series Play It Cool: Remote Monitoring of Equipment Room Temps and Play It Cool: Incorporating Reporting Mechanisms from Physical Devices showed how a modular scripting system was built around a single temperature sensor connected to a serial port. The system read the temperature from the sensor, reported it in a variety of ways, and could even read the temperature audibly at regular intervals. This article improves upon the serial-port communication between the scripts and the sensor, and shows how an analog button can be added to perform certain functions on demand.

Note: This series of articles uses Linux as the operating system of choice, with scripts written for the shell (Bash) and in Perl. For the tasks outlined within this series, I found Linux to be ideal. If you're using a different programming platform, or if you're unable to replicate these methods on Linux, you'll still gain value from these articles. Where possible, I'll point out analogous possibilities in Windows and other operating systems.

Faulty Readings

After using my temperature-reading mechanism for a few weeks, I noticed some disturbing trends in the data. Sometimes the sensor would report "32 degrees Fahrenheit." After investigating the various pieces involved in the mechanism, I determined that Kermit was the root cause, returning bogus results that the rest of the mechanism interpreted as 32 degrees.

I've never quite determined why Kermit wasn't reading the sensor data correctly, but suspect the way that Kermit initializes the serial port. The temperature sensor relies on power from the RTS and DTR serial lines-I suspect that Kermit might drop those lines when initializing the port. Occasionally the script runs faster or slower than normal, catching the sensor without power, and hence returning bogus readings.

In any case, a new mechanism was needed to read the sensor. For a variety of reasons, I settled on Perl, with the addition of the Device::Serial module to handle the communication.

Note: The Device::Serial Perl module handles most of the rigors of serial-port manipulation, allowing the programmer to concentrate on what to send to and receive from the port. Device::Serial is available packaged for most Linux distributions or from CPAN (Comprehensive Perl Archive Network).

Of course, most languages available today have serial-handling modules available. My choice to use Perl in particular was driven by many factors, but other languages (C, Python, etc.) could be used just as easily.

Implementing Perl and the Device::Serial Object

The Device::Serial module provides an object-oriented interface to serial ports. Using this interface follows the typical methods for such interfaces:

  1. Create a new serial-port object.
  2. Configure the new object.
  3. Manipulate the object as necessary.
  4. Destroy the object.

My original Kermit script wasn't very flexible-it operated only on one serial port, which was hard-coded into the script. At the time I was poised to rewrite the serial-port handling part of the mechanism, my employers were also looking to implement a system for monitoring their server room and lab. I decided to write the new Perl script to be more flexible, by accepting the serial port to poll as an argument on the command line.

Note: As you might recall from the previous articles in this series, it's important to keep the system modular in design and not load any one piece with more than one task. In this case, the modular task is returning data from the sensor connected to a serial port. However, that data has one purpose-to be converted to a temperature value-so this script was designed to read the raw data and convert it to a usable temperature value.

The complete script is shown in Listing 1. The comments included in the script appropriately outline and document its functionality.

Listing 1: The readtemp.pl Perl script

#!/usr/bin/perl -w

# Declarations
use strict;
use Device::SerialPort qw( :PARAM :STAT 0.07 );
use vars qw /$VERSION/;
($VERSION) = '$Revision: 1.0 $' =~ /([d.]+)/;
my ($num, $char, $countchar, $buffer, $argument, $port);

# Get argument(s) from command line
if (@ARGV) {
  $port = "";
  # Note that the server room sensor is connected to 
  #   serial port 1 (ttyS0), lab sensor to serial
  #   serial port 2 (ttyS1)
  $argument = (shift @ARGV);
  if ($argument eq "ttyS0" || $argument eq "server") {
    $port = "/dev/ttyS0" }
  if ($argument eq "ttyS1" || $argument eq "lab" ) {
    $port = "/dev/ttyS1" }

  # If the port is set, proceed
  if ($port ne "" ) {

    # Open port
    my $PortObj = Device::SerialPort->new($port);

    # Init port and protocol vars
    my $STALL_DEFAULT=10;
    my $timeout=$STALL_DEFAULT;
    $PortObj->baudrate(9600);
    $PortObj->parity("none");
    $PortObj->databits(8);
    $PortObj->stopbits(1);
    
    # don't wait for each character
    $PortObj->read_char_time(0);   
    # 1 second per unfulfilled "read" call
    $PortObj->read_const_time(1000); 

    # Raise DTR and RTS
    my $dtr=$PortObj->dtr_active(1);
    my $rts=$PortObj->rts_active(1);

    # Clear buffers
    $PortObj->lookclear;
    $char = $PortObj->read(4);

    # Send temp read command
    $countchar = $PortObj->write("!0RT");

    # Read two-byte response (discard first byte)
    $char = $PortObj->read(1);

    # Close and destroy port
    undef $PortObj;

    # Convert to temp reading and output
    if ($char ne "") {
      $num = (ord($char) / 2 * 9 / 5) + 32;
    } else {
      $num = "Error reading temperature!";
    }
    print "$numn";

  } else {

    # If port not set, wrong params given on 
    #   command line; display usage
    &usage; }

} else {

  # If no arguments are given, display usage
  &usage; }

  # Display usage
sub usage {
  die "nUsage: readtemp.pl ttyS0 | ttyS1 | server | lab$!nn";
}

As the code shows, specifically in the usage() function, the script accepts four parameters to determine the serial port that you want to access. Because the script was meant for Progeny's use, I included "server" and "lab" as shortcuts for the ports to which the appropriate sensor was connected. Users who were unsure of which port was for the lab sensor could simply run the script with "lab" as the parameter, to have the script return the temperature in the lab. An even more flexible construction would have been to create a configuration file that mapped the ports to human-readable forms as needed. However, that was overkill at the time, when the script was intended for limited use.

This one script replaced the use of Kermit and its script, as well as the Perl routine to convert the sensor's reading to a temperature value. The new script functioned much better than the Kermit solution-no errors in reading, and a lot less overhead.

On-Demand Reading

Once I had the entire system up and running, I found one thing was missing-temperature reporting on demand. Although I spent most of my time on a computer of one type or another, I found it inconvenient to pull up a Web page or otherwise access the current data. After all, I had the audio system set up to be called upon at any time; I just needed a method to call it.

Contrary to my digital roots, I decided an analog solution would be best-put a physical button on my desktop, and I could push it anytime to hear the current temperature.

The Basics

I decided to use the extra available serial port on the sensor machine for my switch. A typical serial port leaves the request to send (RTS) signal high-which is what I decided to use to power my switch. The return signal would be sent back via the data set ready (DSR) line. All I had to do was rig a switch between the RTS pin (4) and DSR pin (6). When the switch was pressed, the power from the RTS pin would flow back through the DSR pin. A script on the system would monitor the DSR signal and fire off the audio script when it detected the signal high.

The Equipment

A visit to the local Radio Shack netted the required hardware, shown in Table 1.

Table 1: Hardware for the analog switch

Part Radio Shack Reference Page
Momentary switch http://www.radioshack.com/product/index.jsp?productId=2062496
25-pin D-sub connector http://www.radioshack.com/product/index.jsp?productId=2103240
Project box http://www.radioshack.com/product/index.jsp?productId=2062279

I already had a 9-to-25-pin serial cable (the reason I went with a 25-pin connector for my project), assorted wire, a soldering iron, and a Dremel.

Building the Switch

Building the switch itself was straightforward:

  1. Solder wires between pins 4 and 6 on the D-sub connector and the contacts on the momentary switch.
  2. Dremel out a slot in the project box for the D-sub connector.
  3. Drill a hole in the other side of the project box for the momentary switch (button).
  4. Mount the pieces in the project box and connect it to the computer.

Figures 1 through 4 show the completed switch.

Figure 1: The inside of the project box.

Figure 2: The momentary switch (button) mounts on the front of the box.

Figure 3: The D-sub connector mounts on the back of the box.

Figure 4: The switch sits nearby, where I can press it whenever I like.

Implementing a Script To Monitor the Button

Many methods can be used to monitor the signals on a serial line. Most telecom programs have built-in scripting to monitor the signals and perform actions accordingly. If the scripting language cannot invoke the audio script directly, it could write a log file that another script could monitor and act on accordingly.

However, sticking with our lightweight, modular design, I chose to use a little utility called statserial, which simply displays the status of a specified serial port. For example, running statserial with no parameters displays the status of serial port 2, /dev/ttyS1, as shown in Listing 2.

Listing 2: The default statserial display

Device: /dev/ttyS1

Signal  Pin  Pin  Direction  Status  Full
Name    (25) (9)  (computer)         Name
-----   ---  ---  ---------  ------  -----
FG       1    -      -           -   Frame Ground
TxD      2    3      out         -   Transmit Data
RxD      3    2      in          -   Receive Data
RTS      4    7      out         1   Request To Send
CTS      5    8      in          0   Clear To Send
DSR      6    6      in          0   Data Set Ready
GND      7    5      -           -   Signal Ground
DCD      8    1      in          0   Data Carrier Detect
DTR     20    4      out         1   Data Terminal Ready
RI      22    9      in          0   Ring Indicator

This display is updated every second. The statserial utility also has an option to display the status of the port in decimal form, coded using the scheme shown in Listing 3.

Listing 3: The encoding used by statserial for displaying the status of the serial port.


+---+---+---+---+---+---+---+---+---+
| 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---+---+---+---+---+---+---+---+---+
|DSR|RI |DCD|CTS|XXX|XXX|RTS|DTR|XXX|
+---+---+---+---+---+---+---+---+---+

The script will only have to check bit 8 of the result (DSR). When it's set high, the script runs the audio script, and the temperature is read aloud. The final shell script is shown in Listing 4.

Listing 4: The final script to monitor the analog button (buttonsay.sh).


#!/bin/sh

DIR=/home/sschafer/officetemp

chkDSR() {
  LINESTAT=`statserial -d /dev/ttyS1`
  # If bit 8 is set, DSR is on
  #   (button pressed)
  if [ $(( $LINESTAT & 256 )) -eq 256 ]; then
    DSR=1
  else
    DSR=0
  fi
}

# Repeat endlessly
while [ true ]
do

chkDSR

# If DSR is high, run audio script
if [ $DSR == 1 ]; then

  $DIR/saytemp.sh >/dev/null 2>&1

  # Loop while line remains hot
  while [ $DSR -eq 1 ]
  do
    chkDSR
    sleep 2
  done

fi

The script is executed in the background every time the machine is rebooted. Standard script control signals can be used to stop and kill the script as necessary.

Next Time

This article showed how the serial communication between the computer and the temperature sensor was improved by using a dedicated script, and an analog button was added for on-demand actions.

The last article in this series will show how the data can be sent to various reporting applications so it can be charted and trended appropriately.

About the Author

Freelance consultant Steve Schafer has written multiple technology books and articles. In the business world, he most recently worked in-house as COO/CFO of Progeny Linux Systems in Indianapolis. Serving as president and CEO in the company's startup years, Steve led Progeny's business and financial turnaround during the tech crash of the early 2000s. Prior to joining Progeny, he was a senior title manager for Macmillan Digital USA, overseeing the operating system and entertainment software products and tripling Macmillan's Linux product line revenue. He partnered Macmillan with Mandrake, bringing Mandrake's Linux distribution to the competitive retail market.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date