Electric Brewing Supply 30A BCS Giveaway!


Home Brew Forums > Home Brewing Beer > DIY Projects > Arduino - Networked Temperature Monitor
Reply
 
LinkBack Thread Tools
Old 07-08-2012, 09:14 PM   #1
jimmayhugh
Turgid Member
HBT_LIFETIMESUPPORTER.png
Feedback Score: 0 reviews
 
jimmayhugh's Avatar
Recipes 
 
Join Date: Feb 2011
Location: Las Vegas, NV
Posts: 590
Liked 49 Times on 43 Posts
Likes Given: 2

Default Arduino - Networked Temperature Monitor

I have two large upright freezers in my garage that I use as beer coolers. Both use an E-bay aquarium temperature controller, to keep my beers at about 50°.

Since I have Rolladen Security Shutters installed on all of the exterior doors and windows of my home, including the door between the house and the garage, I don't go into the garage often.

One day my wife came from the garage and told me that one of the freezers was making a racket. When I investigated, it turned out that the fan that circulates the cold air was on its way out, and the freezer was struggling to hold temperature. I replaced the fan, and everything was copacetic, but I was concerned about the possibility of another problem that might cause one of the freezers to not hold temperature.

What I needed was something that would allow me to monitor the freezers remotely.

I initially considered one of the wireless indoor/outdoor thermometers that I use in my kitchen, but the wireless signal would not penetrate the metal skin of the freezer.

Then I hit upon using an Arduino microcontroller board to run some One-Wire temperature sensors, and have it serve a web page over my home intranet when interrogated.

I then picked up the following parts:

Freetronics EtherTen: This is a single board that is 100% Ardunio Uno-compatible, and has the equivalent of an Arduino Ethernet Shield baked into a single board. It's 100% compatible with standard Arduino libraries, and costs about the same or slightly less than the street cost of the two Arduino boards it replaces (unless of course you buy knock-offs off of ebay). It also reduces the overall height of the project, which involves "stacking" the cards.

Freetronics Short Protoshield: This is a prototyping breadboad that fits behind the RJ-45 jack on the Etherten or Ethernet Shield. I used this to hold the connectors and components for the One-Wire system.

DS18B20 Temperature Probes: These probes use a Maxim DS18B20 Digital Thermometer that uses the One-Wire technology that allows multiple devices to share a simple two or three wire microlan. I chose to use the three wire setup, which simply involved placing a 4k7 ohm resistor between DS2 of the Etherten (Uno) and 5V

Adafruit I2C RGB 16x2 LCD Display: This is a slick little kit that allows you to run an LCD display that can change backlight color under software control. It can also be used as a stackable shield, or connected with just 4 wires from the Etherten.

Once I had the boards assembled and stacked, I downloaded the IDE from the Arduino "Getting Started" page. I use OSX, Linux and Windows, so I got the IDE for all three and installed them. I also downloaded the "Adafruit RGB LCD Shield Library" and the "OneWire Library" from their respective sites, and loaded them into the appropriate locations in the IDE.

I then wrote some code that will read each probe, and display the result on both the display, and on a simple text-based web page. I used the RGB backlighting to indicate whether or not the temperature was within a specified range. If the temp was too high, the display would turn red:



If the temp was too cold it would turn blue:


But if everything was correct, then the display would be green:


You can watch the monitor in action here.

If I ran into a situation where one was too cold, and one was too hot, I opted to display a red screen. Additionally, the red and blue screens both blink on and off as another visual indication of a problem.

I use Powerline Communication Adapters that allow me to use my house wiring for ad hoc ethernet communication. I've been using the older 200 MBPS adapter for a while now, and they work great. I can simply put one of the adapters in my garage, connect it to the Etherten, and then interrogate it from any other computer in my home using a web browser:



I also use red, blue and green on the web pages to show too hot, too cold, and just right.

I'm planning on doing something similar to monitor "Barney", so stay tuned for further developments.

jimmayhugh is offline
4
People Like This 
Reply With Quote Quick reply to this message
Old 07-08-2012, 09:36 PM   #2
carlisle_bob
Feedback Score: 0 reviews
Recipes 
 
Join Date: Apr 2012
Location: Carlisle, PA
Posts: 1,205
Liked 27 Times on 26 Posts

Default

Hi

How many probes do you think you could support without adding more hardware than the probes themselves?

Bob

__________________
carlisle_bob is offline
 
Reply With Quote Quick reply to this message
Old 07-08-2012, 10:47 PM   #3
cosmatics
HBT_SUPPORTER.png
Feedback Score: 3 reviews
Recipes 
 
Join Date: Apr 2010
Location: New Jersey
Posts: 248
Liked 18 Times on 18 Posts
Likes Given: 47

Default

Awesome stuff, definitely will be ripping this off. Well done.

__________________
cosmatics is online now
 
Reply With Quote Quick reply to this message
Old 07-08-2012, 11:14 PM   #4
jimmayhugh
Turgid Member
HBT_LIFETIMESUPPORTER.png
Feedback Score: 0 reviews
 
jimmayhugh's Avatar
Recipes 
 
Join Date: Feb 2011
Location: Las Vegas, NV
Posts: 590
Liked 49 Times on 43 Posts
Likes Given: 2

Default

Quote:
Originally Posted by carlisle_bob View Post
Hi

How many probes do you think you could support without adding more hardware than the probes themselves?

Bob
I bought 50 of the DS18B20's and breadboarded 15 of them, no problems. As long as you don't use parasitic mode, ie, as long as you use a single pull-up resistor on the data line, I think the biggest problem would be the time lag polling each probe.

I'm using an RJ-45 patch panel wired in parallel, and am using some of the several bazillion ethernet cables I have laying around to make new probes.

Supposedly there can be reflection problems if you use a star-type network instead of a short stub off of a main line, but I haven't seen it in my tests.
jimmayhugh is offline
 
Reply With Quote Quick reply to this message
Old 07-08-2012, 11:30 PM   #5
jimmayhugh
Turgid Member
HBT_LIFETIMESUPPORTER.png
Feedback Score: 0 reviews
 
jimmayhugh's Avatar
Recipes 
 
Join Date: Feb 2011
Location: Las Vegas, NV
Posts: 590
Liked 49 Times on 43 Posts
Likes Given: 2

Default

Quote:
Originally Posted by cosmatics View Post
Awesome stuff, definitely will be ripping this off. Well done.
Here's the current code:
Code:
/*
RGB-LCD and Web Temperature monitor utilizes Dallas Semiconductor / Maxim DS18B20
One-Wire Digital Temperature sensors to monitor two freezers used as beer storage coolers.
The temperatures are available from a 16x2 RGB LCD and by accessing a small text based web page
generated in response to a "GET" command.

One-Wire data channel is on digital pin 2, which is pulled up to +5v with a 4K7 resistor.

The ethernet interface is the Wiznet W5100 based Ethernet Shield.

If you are using multiple Arduino/Ethernet shields, each one must have a unique MAC address and
IP Address.

*/

// include the library code:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
#include <SPI.h>
#include <Ethernet.h>
#include <OneWire.h>

// Debug defines
//  #define SerialDebug

/********** LCD Stuff **********/
// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// These constants make it easy to set the backlight color
const int BLOFF  = 0x0;
const int RED    = 0x1;
const int YELLOW = 0x3;
const int GREEN  = 0x2;
const int TEAL   = 0x6;
const int BLUE   = 0x4;
const int VIOLET = 0x5;
const int WHITE  = 0x7;


const int  cols          = 16;
const int  rows          = 2;
const byte nbsp          = 0x20; // space character
const byte zeroChar      = 0x30; // zero character
const char degChar       = 0xdf; // degree character
bool       addFSpace     = FALSE;
bool       showRed       = FALSE;
bool       showBlue      = FALSE;
bool       showAddresses = FALSE;
int        rowNum        = 0;


/********** Ethernet / Web Stuff **********/
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:

byte mac[] = { 0xDE, 0xAD, 0xBA, 0xBE, 0x00, 0x04 };
IPAddress ip(192,168,1,178);
const int BUFSIZE = 100; // buffer for internet request

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(80);

/********** OneWire Stuff ************/
const int  dsDataPin  = 2; // One Wire data Bus
const int  maxSensors = 2; // total number of DS18B20 sensors
OneWire  ds(dsDataPin); // OneWire bus on pin 2

typedef struct
{
  char  *sensorName;
  char  *xmlName;
  byte  addr[8];
  float degF;
  int   tooHot;   // if temperature is above this value, display is red 
  int   tooCold;  // if temperature is below this value, display is blue
} Sensor;

Sensor ds18[maxSensors] =
{
  {"Cooler 1 ", "Cooler1", {0x28,0x88,0xA4,0xF4,0x03,0x00,0x00,0xE2}, 0.0, 90, 80},
  {"Cooler 2 ", "Cooler2", {0x28,0xC0,0xDF,0x7E,0x03,0x00,0x00,0xE9}, 0.0, 90, 80}
};

int cnt = 0;


void setup()
{
  // set up the LCD's number of columns and rows: 
  lcd.begin(cols, rows);
  lcd.setBacklight(WHITE);
  // Print a message to the LCD.

// Open serial communications and wait for port to open:
  Serial.begin(9600);
 #if defined SerialDebug
  Serial.println(F("Checking for OneWire Devices"));
 #endif

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
#if defined SerialDebug
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
#endif
}

void getOneWire(void)
{
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];

#if defined SerialDebug
  Serial.print(F("ROM "));
  Serial.print(cnt);
  Serial.print(F(" = "));
  for( i = 0; i < 8; i++)
  {
    Serial.write(nbsp);
    if(ds18[cnt].addr[i] < 16){Serial.write(zeroChar);} // prepend hex 0-15 with a 0
    Serial.print(ds18[cnt].addr[i], HEX);
  }

  if (OneWire::crc8(ds18[cnt].addr, 7) != ds18[cnt].addr[7]) {
      Serial.println(F("CRC is not valid!"));
      return;
  }
  Serial.println();
#endif

  type_s = 0;

  ds.reset();
  ds.select(ds18[cnt].addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end
  
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(ds18[cnt].addr);    
  ds.write(0xBE);         // Read Scratchpad

#if defined SerialDebug
  Serial.print(F("  Data = "));
  if(present < 16){Serial.write(zeroChar);} // prepend hex 0-15 with a 0
  Serial.print(present, HEX);
  Serial.write(nbsp);
#endif
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
#if defined SerialDebug
    if(data[i] < 16){Serial.write(zeroChar);} // prepend hex 0-15 with a 0
    Serial.print(data[i], HEX);
    Serial.write(nbsp);
#endif
  }
#if defined SerialDebug
  Serial.print(F(" CRC="));
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();
#endif
  // convert the data to actual temperature
  unsigned int raw = (data[1] << 8) | data[0];
  unsigned char t_mask[4] = {0x7, 0x3, 0x1, 0x0};
  byte cfg = (data[4] & 0x60) >> 5;
  raw &= ~t_mask[cfg];

  ds18[cnt].degF = ((((float)raw / 16.0) * 1.8) + 31.0); //the raw value is Celsius, convert to Fahrenheit

#if defined SerialDebug
  Serial.print("  Temperature = ");
  Serial.print(ds18[cnt].degF);
  Serial.println(" Fahrenheit");
#endif

 if( ++cnt >= maxSensors){cnt = 0;}

}

void checkEthernet(void)
{
  char clientline[BUFSIZE];
  int index = 0;

 // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    
    Serial.println("new client");

    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected())
    {
      if (client.available())
      {
        char c = client.read();
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r')
        {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZE) 
            index = BUFSIZE -1;
          
          // continue to read more data!
          continue;
        }
        
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
        
        // Print it out for debugging
        
        Serial.println(clientline);


        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank)
        {
          if((strstr(clientline, "GET / ") != 0) ||
             (strstr(clientline, "index.htm") != 0) ||
             (strstr(clientline, "index.html") != 0))
          {
            // send a standard http response header
            client.println(F("HTTP/1.1 200 OK"));
            client.println(F("Content-Type: text/html"));
            client.println(F("Connnection: close"));
            client.println();
            client.println(F("<!DOCTYPE HTML>"));
            client.println("<html><head>");
                    // add a meta refresh tag, so the browser pulls again every 5 seconds:
            client.println(F("<meta http-equiv=\"refresh\" content=\"5\">"));
            client.println(F("</head>"));
            client.println(F("<body><table border=\"5\" align=\"center\"><tr>"));
            
            for (int n=0; n<maxSensors; n++)
            {
              if(n<=maxSensors)
              {
                client.print(F("<td align=\"center\" valign=\"center\"><font size=\"10\">&nbsp;&nbsp;"));
                if((int)ds18[n].degF > ds18[n].tooHot)
                {
                  client.print(F("<font color=\"red\">"));
                }else if((int)ds18[n].degF < ds18[n].tooCold){
                  client.print(F("<font color=\"blue\">"));
                }else{
                  client.print(F("<font color=\"green\">"));
                }
                client.print(ds18[n].sensorName);
                client.print(F("&nbsp;&nbsp;<br />"));
                client.print(ds18[n].degF, 0);
                client.println(F("&deg;F</font></font></td>"));
              }
            }
            client.println(F("</tr></table></body>"));
            client.println(F("</html>"));
            break;
          }else if (strstr(clientline, "buttcrack.xml") !=0){
#if defined SerialDebug
            Serial.print(F("Sending buttcrack.xml..."));
#endif
            client.println(F("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"));
            client.println(F("<!--Arduino Buttcrack-->"));
            client.println(F("<buttcrack>"));
            for (int n = 0; n < maxSensors; n++)
            {
              client.print(F("<"));
              client.print(ds18[n].xmlName);
              client.print(F(">"));
              client.print(ds18[n].degF,0);
              client.print(F("</"));
              client.print(ds18[n].xmlName);
              client.println(F(">"));
            }
            client.println(F("</buttcrack>"));
            delay(1);
            client.stop();
          }else{
            // everything else is a 404
            client.println(F("HTTP/1.1 404 Not Found"));
            client.println(F("Content-Type: text/html"));
            client.println();
            client.println(F("<h2>File Not Found</h2>"));
            delay(1);
            client.stop();
          }
          if (c == '\n') {
            // you're starting a new line
            currentLineIsBlank = true;
          } else if (c != '\r') {
          // you've gotten a character on the current line
            currentLineIsBlank = false;
          }
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
#if defined SerialDebug
    Serial.println(F("client disonnected"));
#endif
  }
}

void displayLCDTemp()
{

  if(maxSensors==0)
  {
    lcd.setBacklight(BLOFF);
    delay(500);
    lcd.setBacklight(RED);
    lcd.print(F("Sensor Failure!!"));
    lcd.setCursor(0, 1);
    lcd.print(F("Sensor Failure!!"));
  }else{
    showRed  = FALSE;
    showBlue = FALSE;
    for(int n=rowNum;n<maxSensors;n++)
    {
      if(ds18[n].degF > ds18[n].tooHot)
      {
        showRed  = TRUE;
        showBlue = FALSE;
      }else if(ds18[n].degF < ds18[n].tooCold){
        showBlue = TRUE;
      }
    }
    if(showRed)
    {
      lcd.setBacklight(BLOFF);
      delay(500);
      lcd.setBacklight(RED);
    }else if(showBlue){
      lcd.setBacklight(BLOFF);
      delay(500);
      lcd.setBacklight(BLUE);
    }else{
      lcd.setBacklight(GREEN);
    }
    for(int n=rowNum;n<maxSensors;n++)
    {
      if(ds18[n].degF>=100)
      {
        addFSpace=TRUE;
        break;
      }else{
        addFSpace=FALSE;
      }
    }
    if((rowNum+rows) >= maxSensors){rowNum = maxSensors-rows;}
    for(int n=rowNum,cnt=0;cnt<rows;cnt++,n++)
    {
      lcd.setCursor(0, cnt);
      lcd.print(ds18[n].sensorName);
      if(ds18[n].degF < 100 && addFSpace)
      {
        lcd.print(F(" =  "));
      }else{
        lcd.print(F(" = "));
      }
      lcd.print(ds18[n].degF,0);
      lcd.print(degChar);
      lcd.print(F("    "));
#if defined SerialDebug
      Serial.print(F("n = "));
      Serial.print(n);
      Serial.print(F(" cnt = "));
      Serial.print(cnt);
      Serial.print(F(" name = "));
      Serial.print(ds18[n].sensorName);
      Serial.print(F(" temp = "));
      Serial.println(ds18[n].degF,0);
#endif
    }
  }
}


void checkButtons()
{
  uint8_t buttons = lcd.readButtons();
  
  switch(buttons)
  {
    case BUTTON_UP:
      if(rowNum > 0){rowNum--;}
#if defined SerialDebug
      Serial.print(F("UP Button Pushed "));
      Serial.print(F("rowNum = "));
      Serial.println(rowNum);
#endif
      break;
        
    case BUTTON_DOWN:
      if(rowNum < maxSensors-1){rowNum++;}
#if defined SerialDebug
      Serial.println(F("DOWN Button Pushed"));
      Serial.print(F("rowNum = "));
      Serial.println(rowNum);
#endif
      break;
      
    case BUTTON_RIGHT:
#if defined SerialDebug
      Serial.println(F("RIGHT Button Pushed"));
#endif
      break;
      
    case BUTTON_LEFT:
#if defined SerialDebug
      Serial.println(F("LEFT Button Pushed"));
#endif
      break;
      
    case BUTTON_SELECT:
#if defined SerialDebug
      Serial.println(F("SELECT Button Pushed"));
#endif
      rowNum = 0;
      break;
  }
}

#if defined SerialDebug
void displayAddresses(void)
{

/********** Get Sensor Addresses **********
   Used to get initial values for Sensor structure. Enable SerialDebug, and obtain DS18B20 addresses
   from the serial terminal. Plug those values into the Sensor's "addr" array, re-compile and upload
   with SeralDebug disabled. Note that when SerialDebug is enabled, response times on buttons and
   display are greatly reduced, so be sure to disable SerialDebug when not needed.
******************************************/

 byte addr[8];
 int cntx = 0;
 
  while ( ds.search(addr))
  {
    Serial.print(F("Sensor "));
    Serial.print(cntx);
    Serial.print(F("= {"));
    for( int i = 0; i < 8; i++)
    {
      Serial.print(F("0x"));
      if(addr[i] < 16){Serial.write(0x30);} // prepend hex 0-15 with a 0
      Serial.print(addr[i], HEX);
      if(i < 7){Serial.print(F(","));}
    }
    Serial.println(F("}"));
    cntx++;
    delay(500);
  }
  Serial.print(cntx);
  Serial.print(F(" Sensor"));
  if(cntx == 1)
  {
    Serial.println(F(" detected"));
  }else{
     Serial.println(F("s detected"));
 }
  
  ds.reset_search();
}
#endif

void loop()
{
  getOneWire();
  checkEthernet();
  checkButtons();
#if defined SerialDebug
  displayAddresses();
#endif
  displayLCDTemp();
//  delay(500);
}
The first thing you need to do is enable the "SerialDebug" define, and run the code while examining the serial monitor. The "displayAddresses" code will then interrogate the microlan for all of the ds18b20s that are connected, and show each address as a byte array. Take each address and plug it into the appropriate location in the ds18 Sensor array. You can add as many sensors as you want, just make sure that the maxSensors const equals the number of sensors in the array. You can then re-compile with SerialDeug off (saves about 2K of codespace and speeds things up) and re-run the code.

If you have more than 2 sensors, you can use the "UP"and "DOWN" buttons to vertically scroll the sensors on the display. The display should only change color if one of the sensors currently displayed is out of bounds.

Just be aware that the web page is pretty simple, and may just try to print all of the sensor info on a single line (I'll fix that later).
jimmayhugh is offline
 
Reply With Quote Quick reply to this message
Old 07-09-2012, 02:46 AM   #6
Kyled93
Feedback Score: 1 reviews
Recipes 
 
Join Date: Mar 2011
Location: Fort Lauderdale, Florida
Posts: 234
Liked 9 Times on 8 Posts

Default

Subscribed! Awesome idea.

Do you have the page listed under a DNA server so you can access it from anywhere?

__________________
Kyled93 is offline
 
Reply With Quote Quick reply to this message
Old 07-09-2012, 03:12 AM   #7
jimmayhugh
Turgid Member
HBT_LIFETIMESUPPORTER.png
Feedback Score: 0 reviews
 
jimmayhugh's Avatar
Recipes 
 
Join Date: Feb 2011
Location: Las Vegas, NV
Posts: 590
Liked 49 Times on 43 Posts
Likes Given: 2

Default

Quote:
Originally Posted by Kyled93 View Post
Subscribed! Awesome idea.

Do you have the page listed under a DNA server so you can access it from anywhere?
Nah, it's not like I can leave my job if my beer's getting warm
It's on an internal subnet right now, although I do have the ability to twitter an alert if I want to add the code.

I'm primarily interested in checking it without having to roll up the security shutter to my garage just to check the temperature. I'm also interested in building another to monitor and log my brewstand temperatures onto an SD card during a session.
__________________
TeensyNet Networked Controller
ButtCrack Brewery
And This Is Why We Drink...
jimmayhugh is offline
 
Reply With Quote Quick reply to this message
Old 07-09-2012, 03:18 AM   #8
jimmayhugh
Turgid Member
HBT_LIFETIMESUPPORTER.png
Feedback Score: 0 reviews
 
jimmayhugh's Avatar
Recipes 
 
Join Date: Feb 2011
Location: Las Vegas, NV
Posts: 590
Liked 49 Times on 43 Posts
Likes Given: 2

Default

FYI, I just created two new temp probes using a flat flexible telephone extension cable that was lying around. It was a flat 4-wire cable, and the two probe cables are about 10ft each, and are working just fine.

__________________
TeensyNet Networked Controller
ButtCrack Brewery
And This Is Why We Drink...
jimmayhugh is offline
 
Reply With Quote Quick reply to this message
Old 07-09-2012, 02:42 PM   #9
jgourd
Feedback Score: 0 reviews
Recipes 
 
Join Date: Feb 2010
Location: Louisiana
Posts: 816
Liked 18 Times on 13 Posts
Likes Given: 3

Default

Subscribed. I'm about to embark on a similar project.

__________________
jgourd is offline
 
Reply With Quote Quick reply to this message
Old 07-09-2012, 04:15 PM   #10
gswartley
HBT_LIFETIMESUPPORTER.png
Feedback Score: 0 reviews
 
gswartley's Avatar
Recipes 
 
Join Date: Oct 2010
Location: Bowmansville, PA
Posts: 102
Liked 4 Times on 4 Posts

Default

Subscribed. I love projects like this and can't wait to get started on some of my own.

__________________

Gregg G^3 Swartley

gswartley is offline
 
Reply With Quote Quick reply to this message
Reply



Quick Reply
Message:
Options
Thread Tools


Similar Threads
Thread Thread Starter Forum Replies Last Post
Idea for Kegerator temperature monitor FourJ DIY Projects 0 04-26-2012 04:29 AM
Interesting Temperature Monitor with SMS Jeepninja DIY Projects 3 12-07-2011 05:59 PM
Arduino for kegerator temperature control. Orfy DIY Projects 19 04-21-2010 12:18 PM
1-Wire Temperature Monitor Vinic DIY Projects 12 01-12-2010 06:01 PM
Temperature Monitor N5629 DIY Projects 5 03-31-2009 04:54 PM