Temperature and Fan controlling board

To keep my computer system as quiet as possible, I don't want any more fans running inside it than absolutely necessary. But I feared that my computer would overheat on hot days and crash. So I developed this temperature controlling board in order monitor the temperature at different locations in my computer case and enable additional fans if certain temperature thresholds were reached.

This board is build around an AVR 8-Bit RISC microcontroller from Atmel. While the hardware setup has nothing special, I took the chance to explore the possibilities of using generic programming for microcontroller development. Since I couldn't find any existing source code on the internet, I decided to put mine online.

Software

Techniques

Traditionally, software for microcontrollers is still written in plain old C. While it is no problem to write efficient software in C, it is very difficult to write software that is efficient, modular and serviceable.

This project demonstrates that C++ using generic programming techniques can be as efficient as the most optimized C code while still being modular and serviceable.

Conventional libraries have the big disadvantage that the code is compiled independently from the project that uses these libraries, so adding functionality is only possible through callback functions, which are very inefficient.

The template mechanism of C++ allows for libraries that are compiled for each project that uses them individually. And, more importantly, the code that is compiled can be customized through the template parameters. This allows the compiler to aggressively optimize the code for each application individually.

The protocols for external peripherals are ideal candidates for being put into a library. While the protocol for a certain peripheral, for example a LC Display or a sensor, is always the same, the way in which the peripheral is connected to the controller varies with each application. By parameterizing the protocol class (which resides in the library) with an interface class (which is located in the application code) it is possible to adapt the library code to a wide variety of usages.

To demonstrate the effectiveness, here is an excerpt from the DS1620 code (used in an early version that had only one temperature sensor):


// the library code

template<class Interface> 
class DS1620 : public Interface {
    public:
        void write( u08 data );
	//... 

};

template<class Interface>
void DS1620protocol<Interface>::write( u08 data )
{
    for ( u08 i = 0; i < 8; ++i ) {
        setCLK( false );
        setDQ( data & 1 );
        data >>= 1;
        shortdelay();
        setCLK( true );
        shortdelay();
    }
}



// the application code

class DS1620Interface
{
   protected:
      void setCLK( bool clk )
      {
         if ( clk )
            sbi(PORTB, 3);
         else
            cbi(PORTB, 3);
      };
      
      void setDQ( bool dq )
      {
         if ( dq )
            sbi(PORTB, 4);
         else
            cbi(PORTB, 4);
      };
};

void main()
{  
   DS1620protocol<DS1620Interface> sensor;
   sensor.write( 123 );
   // ...
}   
When combined by the compiler, apart from the call to sensor.write( ) there are no other function calls involved, because the Interface methods are inlined. And since a call to setCLK( false ) has a constant parameter, the if dissolves completely and only the cbi( )function call is left, which is in fact a macro that translates to the single CBI machine instruction. What at first looks like a complete method call is optimized to a single assembly instruction. A higher level of optimization is not possible!

And still, if we have a different application which uses several DS1620 sensors connected to different ports or even through latches, we do not need to modify the library, just use a more complex interface class. This is code reuse at its best!

Source Code

The source code is placed in the place domain.

The libraries written for this project are not general purpose libraries that cover all possible needs. They contain just the functions that I needed for this project.

And I don't claim that the code is optimized to the maximum extent possible. It is efficient enough to run on an AT90S4433 controller, which has 4k of flash program memory and 128 Bytes of RAM. The actual size of the compiled code is 3382 Byte.

You can download the source code or browse it online.

Hardware

The hardware is absolutely unspectacular. An Atmel AT90S4433 microcontroller, three DS1620 digital thermometer from Maxim, a LC display, rotary encoder, several multicolored LEDs and some transistors.

The front view during normal operation:
frontal view of fan controller

Fitting the system in 5,25" drive bay was a real pain:
top view of fan controller

The all time maximum and minimum temperatures of this sensor, and some LEDs in warning mode:
frontal view of fan controller

Conclusion

It works. Both the software and the hardware.

Using generic programming techniques enables a degree of code reusability that is not possible with plain old C, without any runtime or program size penalties.

The hardware works too. Since the summer 2003 was extremely hot here in Germany, the temperatures in my computer case reached 46 C (air temperature, not CPU or board temperature). This is the threshold I've set for enabling an additional case fan, because my CPU cooler is specified for a maximum of 45 C air temperature. I didn't suffer a single crash and could nevertheless enjoy a relatively silent computer for most of the time.


Back to front page