Arduino - Networked Temperature Monitor

Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum

Help Support Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.

jimmayhugh

Turgid Member
HBT Supporter
Joined
Feb 6, 2011
Messages
1,003
Reaction score
237
Location
Las Vegas
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:

RedImage.png


If the temp was too cold it would turn blue:
BlueImage.png


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


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:

WebScreen.png


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.
 
Hi

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

Bob
 
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.
 
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).
 
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 :drunk:
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. :D

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. :rockin:
 
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.:mug:
 
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.:mug:

Neat
 
Today i'm futzing around with a 4x20 non-RGB I2C display. Since I have a few extra pins, I added an RGB LED and some extra code. I'm going to try to make a "master" file that will compile with different boards (regular LCD, I2C LCD, RGB I2C LCD, 16x2, 20x4, 40x4, sd card, Uno Mega,etc), that can be configured via defines and then compiled for a specific configuration. Some thing are just not doable (sd card with ethernet and standard LCD shield on an Uno for instance) because of pin conflicts, or just running out of pins.

This is a Arduino Uno / Ethernet Shield Combo with a breadboard / screw shield sandwiched in between:

20x4_I2C.png
 
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.


Hi

Unless you are running very fast edges (why would you?) or very long cables (like 10's of feet) reflections are un-likey to be an issue. Bulk capacitance it going to kill you first.

Bob
 
Hi

Unless you are running very fast edges (why would you?) or very long cables (like 10's of feet) reflections are un-likey to be an issue. Bulk capacitance it going to kill you first.

Bob

I just made 4 probe cables using surplus telephone cords, and ran them through a standard multi-line adapter:
FourPhoneProbes.png


Three of them are about 10ft long, and the coil on the table is a 50ft coil, and they all seem to work fine.

If you're planning to use the telephone cords to make probe cables, be aware that the phone cable is essentially a crossover-style cable, as the connectors are not both wired the same, so make sure that you use the same end connector for each cable. I use the red wire for VDD, black for VSS and the green and yellow wires are twisted together and used for the data line.
 
If you're planning to use the telephone cords to make probe cables, be aware that the phone cable is essentially a crossover-style cable, as the connectors are not both wired the same, so make sure that you use the same end connector for each cable. I use the red wire for VDD, black for VSS and the green and yellow wires are twisted together and used for the data line.

This is how I built my probes and ran into this exact problem, not realizing that they were crossover-style. Took me a while to figure out why that sensor wasn't working. This is a great tip for anyone looking to do this. My sensors work great. I used a wall jack with modular plugs in my enclosure for attachment.
 
I also have a 3.2" 320x240 graphic LCD that can be used in portrait or landscape mode, with small or larger fonts, that can display up to 21 probes, each with settable parameters and colors.

Landscape with large font:
GLCD_Landscape.png


Portrait with small font:
GLCD_Portrait.png


Here's the current code for the Graphical LCD ONLY:

Code:
/********** ITDB02 Networked Temperature **********
This program requires the ITDB02_Graph16 library.
It uses the OneWire Library and an Arduino-compatable
ethernet shield The display can be used with small or
large fonts, and in portrait or landscape mode.

The OneWire runs on digital pin 9 to avoid a conflict
with the LCD software.
**************************************************/

/********** Various Defines **********/

// #define SerialDebug    // Used for debugging with the Serial Terminal, disable for normal use
// #define GetAddresses   // Used to get Sensor Addresses 
// #define EthernetDebug  // Used to debug Ethernet problems

// #define P15X16         // define 15 column by 16 row display in portrait mode
// #define P30X21         // define 30 column by 21 row display in portrait mode
// #define L40X16         // define 40 column by 20 row display in landscape mode
// #define L20X12         // define 20 column by 15 row display in landscapr mode

#include <Wire.h>
#include <ITDB02_Graph16.h>
#include <SPI.h>
#include <Ethernet.h>
#include <OneWire.h>

// Declare which fonts we will be using
#if defined P30X21
extern uint8_t SmallFont[];
char *blankLine = "                              ";
#endif

#if defined L40X16
extern uint8_t SmallFont[];
char *blankLine = "                                        ";
#endif

#if defined P15X16
extern uint8_t BigFont[];
char *blankLine = "               ";
#endif

#if defined L20X12
extern uint8_t BigFont[];
char *blankLine = "                    ";
#endif

//myGLCD(RS,WR,CS,RST,ALE,mode);
ITDB02 myGLCD(A1,A2,A0,A4,A5,2);   //

const byte nbsp            = 0x20; // space character
const byte zeroChar        = 0x30; // zero character
const char degChar         = 0xdf; // degree character
const byte orientLandscape = 0x01;
const byte orientPortrait  = 0x00;

// if set to TRUE, blank lines are printed for lines with no probe
// if set to FALSE, lines with no probe are marked in yellow
bool showBlankLine = TRUE;

/********** Ethernet / Web Stuff **********
 Enter a MAC address and IP address for your controller below.
 The IP address will be dependent on your local network:
 The Ethernet hardware utilizes digital pins 10, 11, 12, and 13.
 ********** Ethernet / Web Stuff **********/
byte mac[] = { 0xDE, 0xAD, 0xBA, 0xBE, 0x00, 0x04 };
IPAddress ip(192,168,1,175);
const int BUFSIZE = 100; // buffer for internet request
const int rowSize = 4;

// 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  = 9; // One Wire data Bus
OneWire  ds(dsDataPin); // OneWire bus on pin 2
bool showCelsius = FALSE;

typedef struct
{
  bool  sensorActive; // set to "TRUE" if you put a sensor address in the "addr" array.
  char  *sensorName;
  char  *xmlName;
  byte  addr[8];
  float deg;
  int   tooCold;  // if temperature is below this value, display is blue
  int   tooHot;   // if temperature is above this value, display is red 
} Sensor;

#if defined P15X16
const int  maxSensors = 16; // total number of DS18B20 sensors
Sensor ds18[maxSensors] =
{

  {FALSE, "Sensor  1", "Sensor1",  {0,0,0,0,0,0,0,0}, 0.0, 90, 100},
  {FALSE, "Sensor  2", "Sensor2",  {0,0,0,0,0,0,0,0}, 0.0, 70, 80},
  {FALSE, "Sensor  3", "Sensor3",  {0,0,0,0,0,0,0,0}, 0.0, 70, 90},
  {FALSE, "Sensor  4", "Sensor4",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  5", "Sensor5",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  6", "Sensor6",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  7", "Sensor7",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  8", "Sensor8",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  9", "Sensor9",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 10", "Sensor10", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 11", "Sensor11", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 12", "Sensor12", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 13", "Sensor13", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 14", "Sensor14", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 15", "Sensor15", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 16", "Sensor16", {0,0,0,0,0,0,0,0}, 0.0, 45, 55}
};
#endif

#if defined L20X12
const int  maxSensors = 12; // total number of DS18B20 sensors
Sensor ds18[maxSensors] =
{
  {FALSE, "Sensor  1", "Sensor1",  {0,0,0,0,0,0,0,0}, 0.0, 90, 100},
  {FALSE, "Sensor  2", "Sensor2",  {0,0,0,0,0,0,0,0}, 0.0, 70, 80},
  {FALSE, "Sensor  3", "Sensor3",  {0,0,0,0,0,0,0,0}, 0.0, 70, 90},
  {FALSE, "Sensor  4", "Sensor4",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  5", "Sensor5",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  6", "Sensor6",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  7", "Sensor7",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  8", "Sensor8",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  9", "Sensor9",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 10", "Sensor10", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 11", "Sensor11", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 12", "Sensor12", {0,0,0,0,0,0,0,0}, 0.0, 45, 55}
};
#endif

#if defined P30X21
const int  maxSensors = 21; // total number of DS18B20 sensors
Sensor ds18[maxSensors] =
{
  {FALSE, "Sensor      1", "Sensor1",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      2", "Sensor2",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      3", "Sensor3",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      4", "Sensor4",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      5", "Sensor5",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      6", "Sensor6",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      7", "Sensor7",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      8", "Sensor8",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      9", "Sensor9",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     10", "Sensor10", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     11", "Sensor11", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     12", "Sensor12", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     13", "Sensor13", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     14", "Sensor14", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     15", "Sensor15", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     16", "Sensor16", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     17", "Sensor17", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     18", "Sensor18", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     19", "Sensor19", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     20", "Sensor20", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     21", "Sensor21", {0,0,0,0,0,0,0,0}, 0.0, 45, 55}
};
#endif

#if defined L40X16
const int  maxSensors = 16; // total number of DS18B20 sensors
Sensor ds18[maxSensors] =
{
  {FALSE, "Sensor                  1", "Sensor1",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  2", "Sensor2",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  3", "Sensor3",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  4", "Sensor4",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  5", "Sensor5",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  6", "Sensor6",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  7", "Sensor7",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  8", "Sensor8",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                  9", "Sensor9",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                 10", "Sensor10", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                 11", "Sensor11", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                 12", "Sensor12", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                 13", "Sensor13", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                 14", "Sensor14", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                 15", "Sensor15", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor                 16", "Sensor16", {0,0,0,0,0,0,0,0}, 0.0, 45, 55}
};
#endif

/********** IMPORTANT!! **********
BE SURE TO SET THIS VALUE TO THE
   NUMBER OF ACTIVE SENSORS!!
*********** IMPORTANT!! **********/
int activeSensors = 0;
int cnt = 0;

void setup()
{
  
#if defined SerialDebug || defined GetAddresses || defined EthernetDebug
  Serial.begin(9600);
  delay(1000);
  Serial.println(F("Serial Debug running"));
#endif

#if defined P15X16 || defined P30X21
  myGLCD.InitLCD(PORTRAIT);
#endif

#if defined L40X16 || defined L20X12
  myGLCD.InitLCD(LANDSCAPE);
#endif

#if defined P15X16 || defined L20X12
  myGLCD.setFont(BigFont);
#endif

#if defined L40X16 || defined P30X21
  myGLCD.setFont(SmallFont);
#endif

#if defined SerialDebug
  Serial.println(F("InitLCD"));
#endif

  myGLCD.clrScr();
  myGLCD.setBackColor(0,0,0);
  myGLCD.fillScr(0,0,0);

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

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>"));
            if(activeSensors == 0)
            {
              client.println(F("<td align=\"center\" valign=\"center\"><font size=\"10\" color=\"red\">&nbsp;&nbsp;No Sensors Selected&nbsp;&nbsp;</font></td></tr></table></body></html>"));
              break;
            }else{
              for (int n=0; n<activeSensors; n++)
              {
                if(n<=maxSensors)
                {
                  client.print(F("<td align=\"center\" valign=\"center\"><font size=\"10\">&nbsp;&nbsp;"));
                  if((int)ds18[n].deg > ds18[n].tooHot)
                  {
                    client.print(F("<font color=\"red\">"));
                  }else if((int)ds18[n].deg < 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].deg, 0);
                  if (showCelsius == TRUE)
                  {
                    client.print(F("&deg;C"));
                  }else{
                    client.print(F("&deg;F"));
                  }
                  client.println(F("</font></font></td>"));
                }
                if((n+1) % rowSize == 0)
                {
                  client.println(F("</tr><tr>"));
                }
              }
              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 < activeSensors; n++)
            {
              client.print(F("<"));
              client.print(ds18[n].xmlName);
              client.print(F(">"));
              client.print(ds18[n].deg,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 || defined EthernetDebug
    Serial.println(F("client disonnected"));
#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
  if (ds18[cnt].sensorActive == TRUE)
  {
  type_s = 0;

  ds.reset();
  ds.select(ds18[cnt].addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end
  
  delay(750);     // 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];
  if( showCelsius == TRUE)
  {
    ds18[cnt].deg = ((float)raw / 16.0);
  }else{
    ds18[cnt].deg = ((((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].deg);
  if( showCelsius == TRUE)
  {
    Serial.println(" Celsius");
  }else{
    Serial.println(" Fahrenheit");
  }
#endif
  }
  if( ++cnt >= maxSensors){cnt = 0;}

}

void displayLCD(void)
{
#if defined SerialDebug
  Serial.println(F("Entering displayLCD()"));
#endif
// Clear the screen and draw the frame
//  myGLCD.clrScr();
  
  myGLCD.setColor(255,255,255);
  myGLCD.setBackColor(0, 0, 0);
#if defined P30X21
  for (int x=0, y=0;x<310;x+=15,y++)
#endif
#if defined P15X16
  for (int x=0, y=0;x<319;x+=20,y++)
#endif
#if defined L40X16
  for (int x=0, y=0;x<230;x+=15,y++)
#endif
#if defined L20X12
  for (int x=0, y=0;x<239;x+=20,y++)
#endif
  {
    if(ds18[y].sensorActive == TRUE)
    {
      if(ds18[y].deg > ds18[y].tooHot)
      {
        myGLCD.setBackColor(255, 0, 0);
      }else if(ds18[y].deg < ds18[y].tooCold){
        myGLCD.setBackColor(0, 0, 255);
      }else{
        myGLCD.setBackColor(0, 255, 0);
      }
#if defined P30X21
//      myGLCD.print(blankLine,0,x);
      myGLCD.print(ds18[y].sensorName,0,x);
      int temp = (int) ds18[y].deg;
      if(temp >= 100)
      {
        myGLCD.print(" = ", (13*8), x);
        myGLCD.printNumI(temp, (17*8), x);
      }else{
        myGLCD.print(" =  ", (13*8), x);
        myGLCD.printNumI(temp, (17*8), x);
      }
#endif
#if defined P15X16
//      myGLCD.print(blankLine,0,x);
      myGLCD.print(ds18[y].sensorName,0,x);
      int temp = (int) ds18[y].deg;
      if(temp >= 100)
      {
        myGLCD.print(" = ", (8*16), x);
        myGLCD.printNumI(temp, (11*16), x);
      }else{
        myGLCD.print(" =  ", (9*16), x);
        myGLCD.printNumI(temp, (12*16), x);
      }
#endif
#if defined L40X16
//      myGLCD.print(blankLine,0,x);
      myGLCD.print(ds18[y].sensorName,0,x);
      int temp = (int) ds18[y].deg;
      if(temp >= 100)
      {
        myGLCD.print(" = ", (24*8), x);
        myGLCD.printNumI(temp, (28*8), x);
      }else{
        myGLCD.print(" =  ", (25*8), x);
        myGLCD.printNumI(temp, (29*8), x);
      }
#endif
#if defined L20X12
//      myGLCD.print(blankLine,0,x);
      myGLCD.print(ds18[y].sensorName,0,x);
      int temp = (int) ds18[y].deg;
      if(temp >= 100)
      {
        myGLCD.print(" = ", (9*16), x);
        myGLCD.printNumI(temp, (12*16), x);
      }else{
        myGLCD.print(" =  ", (9*16), x);
        myGLCD.printNumI(temp, (13*16), x);
      }
#endif
    }else{
      if(showBlankLine == FALSE)
      {
        myGLCD.setColor(255,0,0);
        myGLCD.setBackColor(255, 255, 0);
        myGLCD.print(ds18[y].sensorName,0,x);
#if defined P30X21
        myGLCD.print(" NOT ACTIVE ", (13*8), x);
#endif
#if defined L40X16
        myGLCD.print(" NOT ACTIVE ", (25*8), x);
#endif
#if defined P15X16
        myGLCD.print(" OFF ", (9*16), x);
#endif
#if defined L20X12
        myGLCD.print(" NOT ACTIVE", (9*16), x);
#endif
        myGLCD.setColor(255,255,255);
      }else{
        myGLCD.setBackColor(0,0,0);
        myGLCD.setColor(255,255,255);
        myGLCD.print(blankLine, 0, x);
      }
    }
  }
}

#if defined SerialDebug || defined GetAddresses
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()
{
#if defined SerialDebug || defined GetAddresses 
  displayAddresses();
#endif
  getOneWire();
  checkEthernet();
  displayLCD();
}
 
OK more fooling around :ban:

I wrote a network-only (no LCD display) monitor that can be updated with new sensors via web pages, and saves the sensor structures info to the Arduino EEPROM.

When a brand-new Arduino Uno is downloaded with this code, the first page that show up should look like this:
NWT001.png


Pressing the "UPDATE" button brings up this screen:
NWT002.png


The upper table has the available structures, while the lower table shows the currently detected sensors. Select a structure to modify and press the "Submit" button and the following screen appears:
NWT003.png


After you update your info, make sure the sensor is set to "Active", and press "Submit". The data will be written to the EEPROM, and a confirmation screen appears:
NWT004.png


After you've updated the sensors, you can then monitor the sensors via the network:
NWT005.png


This code is RAM-intensive,and has limited EEPROM on board, so it's currently limited to 10 sensors on the Uno. The Arduino Mega should be able to handle more sensors, and operate an LCD with room to spare. I'll let you know :rockin:

Here's the current code:
Code:
#include <SPI.h>
#include <Ethernet.h>
#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <OneWire.h>

byte mac[] = { 0xDE, 0xAD, 0xBA, 0xBE, 0xFE, 0xED };
IPAddress ip(192,168,1,146);

const int BUFSIZE = 300; // buffer for internet request
char clientline[BUFSIZE];
int index = 0;
const int newRow = 5;

// 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
int        maxSensors; // total number of DS18B20 sensors
OneWire  ds(dsDataPin); // OneWire bus on pin 2

const byte nbsp        = 0x20; // space character
const byte zeroChar    = 0x30; // zero character
const char degChar     = 0xdf; // degree character
bool       addFSpace   = FALSE;
bool       showCelsius = FALSE;
int        rowNum      = 0;

typedef struct
{
  bool  sensorActive; // set to "TRUE" if you put a sensor address in the "addr" array.
  char  sensorName[20];
  byte  addr[8];
  float deg;
  int   tooCold;  // if temperature is below this value, display is blue
  int   tooHot;   // if temperature is above this value, display is red 
} Sensor;

Sensor ds18[] =
{
  {FALSE, "Sensor      1",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      2",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      3",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      4",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      5",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      6",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      7",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      8",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      9",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     10",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55}
};

int activeSensors = 0;

int cnt = 0;

void setup()
{
// Open serial communications and wait for port to open:
  Serial.begin(9600);
    
  maxSensors= sizeof(ds18) / sizeof(Sensor);
  
  EEPROM_readAnything(cnt, ds18); // get sensor structures from EEPROM
  
  for(int cntx=0; cntx < maxSensors; cntx++)
  {
    if(ds18[cntx].sensorActive == TRUE){activeSensors++;}
  }

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

   Serial.print("server is at ");
   Serial.println(Ethernet.localIP());
}

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

  if (ds18[cnt].sensorActive)
  {
    type_s = 0;

    ds.reset();
    ds.select(ds18[cnt].addr);
    ds.write(0x44,1);         // start conversion, with parasite power on at the end
  
    delay(750);     // 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

    for ( i = 0; i < 9; i++) 
    {           // we need 9 bytes
      data[i] = ds.read();
    }

    // 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];
    if( showCelsius == TRUE)
    {
      ds18[cnt].deg = ((float)raw / 16.0);
    }else{
      ds18[cnt].deg = ((((float)raw / 16.0) * 1.8) + 31.0); //the raw value is Celsius, convert to Fahrenheit
    }
  }
  if( ++cnt >= maxSensors){cnt = 0;}
}

void sendHeader(EthernetClient client, bool refresh, int time)
{
  // 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(F("<html>"));
  if(refresh==TRUE)
  {
    // add a meta refresh tag, so the browser pulls again every (time) seconds:
    client.print(F("<head><meta http-equiv=\"refresh\" content=\""));
    client.print(time);
    client.println(F("\"></head>"));
  }
  client.println(F("<body><table border=\"5\" align=\"center\"><tr>"));
}

void sendFooter(EthernetClient client)
{
  client.println(F("</tr></table></body>"));
  client.println(F("</html>"));
}

void checkEthernet(void)
{

 // 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())
      {
        memset(clientline, 0, sizeof(clientline)); // clear the clientline
        if(client.readBytesUntil('/', clientline, BUFSIZE))
        {
          if(strcmp(clientline, "GET ") == 0 )
          {
            memset(clientline, 0, sizeof(clientline)); // clear the clientline
            if(client.readBytesUntil('HTTP', clientline, BUFSIZE))
            {
              Serial.print(F("GET Request - "));
              Serial.println(clientline);
              if(strstr(clientline, "index.htm") != 0)
              {
                sendHeader(client, TRUE, 5);
                showActiveSensors(client);
                sendFooter(client);
                break;
              }else if(strstr(clientline, "findsensors.htm") != 0){
                sendHeader(client, TRUE, 5);
                findSensors(client);
                sendFooter(client);
                break;
              }else if(strstr(clientline, "getsensor.htm") != 0){
                sendHeader(client, FALSE, 0);
                getSensors(client);
                sendFooter(client);
                break;
              }else if(strstr(clientline, ".ico") != 0){
                Serial.println(F("dumping .ico request"));
                break;
              }else if((strstr(clientline, " HTT") != 0) &&
                       (strstr(clientline, ".") == 0)){
                // no file named at all
                sendHeader(client, TRUE, 5);
                showActiveSensors(client);
                sendFooter(client);
                break;
              }else{
                noFileFound(client);
                break;
              }
            }
          }else if(strcmp(clientline, "POST ") != 0 ){
            Serial.print(F("POST Request - "));
            Serial.println(clientline);
            if(strstr(clientline, "updatesensor") != 0)
            {
              sendHeader(client, FALSE, 0);
              updateSensor(client);
              sendFooter(client);
            }else if(strcmp(clientline, "modifysensor") != 0)
            {
              sendHeader(client, FALSE, 0);
              modifySensor(client);
              sendFooter(client);
            }else{
              noFileFound(client);
            }
            break;
          }
        }
      }
    }
    endInternetSession(client);    
  }
}

void endInternetSession(EthernetClient client)
{
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println(F("client disonnected"));
}

void modifySensor(EthernetClient client)
{
  int sensor, cntx;
  showSensorForm(client, "getsensor.htm", "get", TRUE);
  memset(clientline, 0, sizeof(clientline)); // clear the clientline
  index=0;
  do{
    char c = client.read();
    // Serial.write(c);
    clientline[index] = c;
    if(++index >= BUFSIZE){index = 0;}
  }while(strstr(clientline, "\n\r") == 0);
  findString(client, "=");
  sensor = client.parseInt();
  client.println(F("<td>Sensor "));
  client.print(sensor);
  client.print(F("</td>"));
  findString(client, "E&");
  client.print(F("<td>"));
  if(strstr(clientline, "TRUE") != 0)
  {
    ds18[sensor].sensorActive = TRUE;
    client.print(F("TRUE"));
  }else{
    ds18[sensor].sensorActive = FALSE;
    client.print(F("FALSE"));
  }
  client.print(F("</td>"));
  findString(client, "=");
  findString(client, "&");
  clientline[index-1] = 0;
  cntx=0;
  do
  {
    if(clientline[cntx] == '+')
    {
      ds18[sensor].sensorName[cntx] = ' ';
    }else{
      ds18[sensor].sensorName[cntx] = clientline[cntx];
    }
    cntx++;
  }while(clientline[cntx] != 0);
  ds18[sensor].sensorName[cntx]=0; // terminate the string
  client.print(F("<td>"));
  client.print(ds18[sensor].sensorName);
  client.print(F("</td>")); 
  findString(client, "=");
  client.print(F("<td>"));
  for(cntx = 0; cntx < 8; cntx++)
  {
    ds18[sensor].addr[cntx] = client.parseInt();
    client.print(ds18[sensor].addr[cntx]);
    if(cntx < 7)
      {
        client.print(F(", "));
        findString(client, "%2C");
      }
  }
  client.print(F("</td>"));
  findString(client, "=");
  client.print(F("<td>"));
  ds18[sensor].tooCold = client.parseInt();
  client.print(ds18[sensor].tooCold);
  client.print(F("</td>"));
  findString(client, "tooHot");
  client.print(F("<td>"));
  ds18[sensor].tooHot = client.parseInt();
  client.print(ds18[sensor].tooHot);
  for(int i=0; i < 512; i++){EEPROM.write(i, 0);} //clear EEPROM
  EEPROM_writeAnything(0, ds18); // save sensor info to EEPROM
  EEPROM_readAnything(0, ds18); // get sensor info from EEPROM
  client.print(F("</td></tr><tr>"));
  client.print(F("<td colspan=\"6\" align=\"center\"> Saved to EEPROM Press Go to change another: <input type=\"submit\" name=\"GO\"></td></tr>"));
  client.println(F("</form>"));
}

void findString(EthernetClient client, char *string)
{
  // search for a given string in the incoming stream
  memset(clientline, 0, sizeof(clientline)); // clear the clientline
  index=0;
  do{
    char c = client.read();
    // Serial.write(c);
    clientline[index] = c;
    if(++index >= BUFSIZE){index = 0;}
  }while((strstr(clientline, string) == 0));
}

void updateSensor(EthernetClient client)
{
  // Serial.println(F("entering updateSensor"));
  findString(client, "SUBMIT");
  if(strstr(clientline, "=Sensor") != 0)
  {
    char *loc = strstr(clientline, "=Sensor");
    if (loc[8] == '&')
    {
      loc[8]=0;
    }else{
      loc[9]=0;
    }
    showSensorForm(client, "modifySensor.htm", "post", TRUE);
    client.print(F("<td align=\"center\">Sensor # "));
    int sensor = atoi(&loc[7]);
    client.print(sensor+1);
    client.print(F("<input type=\"hidden\" name=\"sensor\" value=\""));
    client.print(sensor);
    client.print(F("\">"));
    client.println(F("</td>"));
    client.print(F("<td align=\"center\"><input type=\"radio\" name=\"active\" value=\"TRUE\""));
    if(ds18[sensor].sensorActive == TRUE){client.print(F(" checked "));}
    client.print(F("> TRUE <br /><input type=\"radio\" name=\"active\" value=\"FALSE\""));
    if(ds18[sensor].sensorActive == FALSE){client.print(F(" checked "));}
    client.print(F("> FALSE </td>"));
    client.print(F("<td><input type=\"text\" name=\"sensorName\" size=\"15\" value=\""));
    client.print(ds18[sensor].sensorName);
    client.print(F("\"></td><td><input type=\"text\" size=\"35\" name=\"sensorAddr\" value=\""));
    for(int y = 0; y < 8; y++)
    {
      client.print(ds18[sensor].addr[y]);
      if(y<=6){client.print(F(","));}
    }
    client.print(F("\"></td><td><input type=\"text\" name=\"tooCold\" size=\"3\" value=\""));
    client.print(ds18[sensor].tooCold);
    client.print(F("\"></td><td><input type=\"text\" name=\"tooHot\" size=\"3\" value=\""));
    client.print(ds18[sensor].tooHot);
    client.println(F("\"></td></tr><tr>"));
    client.println(F("<td colspan=\"6\" align=\"center\"> Select the fields to modify, and press SUBMIT: <input type=\"submit\" name=\"SUBMIT\"></td></tr>"));
    client.println(F("</form>"));
  }else{
    client.println(F("<td><font size=\"5\" color=\"red\">No Sensor Selected</font></td>"));
  }
  endInternetSession(client);
}

void noFileFound(EthernetClient client)
{
  // 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>"));
}

void findSensors(EthernetClient client)
{
 byte addr[8];
 int cntx = 0;
 
  while ( ds.search(addr))
  {
    client.print(F("<td>Sensor "));
    client.print(cntx);
    client.print(F("= {"));
    for( int i = 0; i < 8; i++)
    {
      client.print(addr[i]);
      if(i < 7){client.print(F(", "));}
    }
    client.println(F("}</td></tr><tr>"));
    cntx++;
    delay(500);
  }
  client.print(F("<td colspan=\"2\" align=\"center\">"));
  client.print(cntx);
  client.print(F(" Sensor"));
  if(cntx == 1)
  {
    client.println(F(" Detected"));
  }else{
     client.println(F("s Detected"));
  }
  client.println(F("</td></tr>"));
  ds.reset_search();
}

void showActiveSensors(EthernetClient client)
{
  if(activeSensors == 0)
  {
    client.println(F("<td align=\"center\" valign=\"center\"><font size=\"10\" color=\"red\">&nbsp;&nbsp;No Sensors Selected&nbsp;&nbsp;</font></td>"));
  }else{
    for (int n=0; n<activeSensors; n++)
    {
      if(n<=maxSensors)
      {
        client.print(F("<td align=\"center\" valign=\"center\"><font size=\"10\">&nbsp;&nbsp;"));
        if((int)ds18[n].deg > ds18[n].tooHot)
        {
          client.print(F("<font color=\"red\">"));
        }else if((int)ds18[n].deg < 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].deg, 0);
        if (showCelsius == TRUE)
        {
          client.print(F("&deg;C"));
        }else{
          client.print(F("&deg;F"));
        }
        client.println(F("</font></font></td>"));
        if(n == newRow){client.println(F("</tr><tr>"));}
      }
    }
  }
  showSensorForm(client, "getsensor.htm", "get", FALSE);
  client.print(F("<td align=\"center\" colspan=\"5\">Add or Update a Sensor: <button type=\"submit\">UPDATE</button></td></form>"));
}

void showSensorForm(EthernetClient client, char *filename, char *method, bool showLabels)
{
  if(showLabels == TRUE)
  {
    client.print(F("<td align=\"center\">SELECT</td>"));
    client.print(F("<td align=\"center\">ACTIVE</td>"));
    client.print(F("<td align=\"center\">SENSOR<br />NAME</td>"));
    client.print(F("<td align=\"center\">SENSOR ADDRESS</td>"));
    client.print(F("<td align=\"center\">TOO<br />COLD</td>"));
    client.print(F("<td align=\"center\">TOO<br />HOT</td>"));
  }
  client.print(F("</tr><tr>"));
  client.print(F("<form id=\"getSensor\" action=\"http://"));
  client.print(Ethernet.localIP());
  client.print(F("/"));
  client.print(filename);
  client.print(F("\" method=\""));
  client.print(method);
  client.println(F("\">"));
}

void getSensors(EthernetClient client)
{
  activeSensors = 0;
  showSensorForm(client, "updatesensor.htm", "post", TRUE);
  int x, y;
  for(x = 0; x < maxSensors; x++)
  {
    client.print(F("<td><input type=\"radio\" name=\"Sensor\" value=\"Sensor"));
    client.print(x);
    client.print(("\">Sensor&nbsp;"));
    if(x < 10){client.print(F("&nbsp;"));}
    client.print(x+1);
    client.print(F("</td><td align=\"center\">"));
    if(ds18[x].sensorActive==TRUE)
    {
      client.print(F("TRUE"));
      activeSensors++;
    }else{
      client.print(F("FALSE"));
    }
    client.print(F("</td><td>"));
    for(y = 0; y < strlen(ds18[x].sensorName); y++)
    {
      if(ds18[x].sensorName[y] == ' ')
      {
        client.print(F("&nbsp;"));
      }else{
        client.write(ds18[x].sensorName[y]);
      }
    }
    client.print(F("<td align=\"center\">"));
    for(y = 0; y < 8; y++)
    {
      client.print(ds18[x].addr[y]);
      if(y<=6){client.print(F(","));}
    }
    client.print(F("</td><td align=\"center\">"));
    client.print(ds18[x].tooCold);
    client.print(F("</td><td align=\"center\">"));
    client.print(ds18[x].tooHot);
    client.println(F("</td></tr><tr>"));
  }
  client.println(F("<td colspan=\"6\" align=\"center\"> Select a sensor to modify, and press SUBMIT: <input type=\"submit\" name=\"SUBMIT\"></td></tr>"));
  client.println(F("</form>"));
  client.println(F("</tr></table><br /><br /><table border=\"5\" align=\"center\"><tr>"));;
  findSensors(client);
}

void loop(void)
{
  getOneWire();
  checkEthernet();
  delay(500);
}
 
Pretty neat. Since you have the arduino and probes, why not also get some SSRs and control the temperature too?
 
Got the code running on the Mega2650 with a minimum of fuss. There are now 20 probe structures available, but more could probably be added.

It now supports the Adafruit I2C RGB-LCD display in either 16x2 or 20x4 mode. I chose this particular board since with a little bit of hardware diddling and some minor code changes, 6 more displays could be added to display up to 28 probes :mug:

I also added a COSM (formerly panchube) feed to allow online datalogging and display.

Here's the code. You'll have to get your own COSM account and plug in the relevant codes:
Code:
/*
LCD and Web Temperature monitor utilizes Dallas Semiconductor / Maxim DS18B20
One-Wire Digital Temperature sensors to monitor multiple containers, coolers, etc.
The temperatures are available from a various LCD displays 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.

*/

/*********** Various defines **********/
// #define SerialDebug    // Used to debug code with the serial terminal


// #define D16X2          // Used for Adafruit RGB-LCD 16 column by 2 row I2C display
// #define D20X4          // Used for Adafruit RGB-LCD 20 column by 4 row I2C display


#include <Wire.h>
#include <SPI.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
#include <Ethernet.h>
#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <OneWire.h>

/********** LCD Stuff **********/
// The shield uses the I2C SCL (pin 21) and SDA (pin 20) on the Arduino's Mega2560
// 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;

bool  showRed   = FALSE;
bool  showBlue  = FALSE;

#if defined D16X2
const int  cols       = 16;
const int  rows       = 2;
const int  sensorNameArraySize = cols - 6;
const int  sensorNameSize = sensorNameArraySize-1;
const char lcdToken[] = "D16X2";
char       LCDtoken[] = "     ";
#endif

#if defined D20X4
const int  cols           = 20;
const int  rows           =  4;
const int  sensorNameArraySize = cols - 6;
const int  sensorNameSize = sensorNameArraySize-1;
const char lcdToken[] = "D20X4";
char       LCDtoken[] = "     ";
#endif

const int   EEPROMsize     = 4096; //Mega2560
const int   EEPROMidAddr   = 0;
const int   EEPROMdsAddr   = 0x10;
const byte  EEPROMidVal    = 0x55;
int         eepromSpace;

/********** Internet Stuff **********/
byte mac[] = { 0xDE, 0xAD, 0xBA, 0xBE, 0xFE, 0xED };
IPAddress ip(192,168,1,146); // **local IP Address** insert your address


char cosmServer[] = "api.cosm.com";   // name address for cosm API
unsigned long lastConnectionTime = 0;          // last time you connected to the server, in milliseconds
boolean lastConnected = false;                 // state of the connection last time through the main loop
const unsigned long postingInterval = 5*1000; //delay between updates to Cosm.com

const int BUFSIZE = 300; // buffer for internet request
char clientline[BUFSIZE];
int index = 0;
const int newRow = 5;
#define APIKEY         "YOUR COSM KEY GOES HERE" // your cosm api key
#define FEEDID         **YOUR ID GOES HERE** // your feed ID
#define USERAGENT      "Mega Temp Monitor" // user agent is the project name

// 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   = 20; // total number of DS18B20 
const byte nbsp        = 0x20; // space character
const byte zeroChar    = 0x30; // zero character
const char degChar     = 0xdf; // degree character

OneWire    ds(dsDataPin);   // OneWire bus on pin 2

bool       addFSpace   = FALSE;
bool       showCelsius = FALSE;
int        rowNum      = 0;

typedef struct
{
  bool  sensorActive; // set to "TRUE" if you put a sensor address in the "addr" array.
  char  sensorName[sensorNameArraySize];
  byte  addr[8];
  float deg;
  int   tooCold;  // if temperature is below this value, display is blue
  int   tooHot;   // if temperature is above this value, display is red 
} Sensor;

#if defined D20X4
Sensor ds18[maxSensors] =
{
  {FALSE, "Sensor      1",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      2",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      3",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      4",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      5",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      6",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      7",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      8",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor      9",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     10",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     11",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     12",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     13",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     14",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     15",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     16",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     17",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     18",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     19",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor     20",  {0,0,0,0,0,0,0,0}, 0.0, 45, 55}
};
#endif

#if defined D16X2
Sensor ds18[maxSensors] =
{
  {FALSE, "Sensor  1", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  2", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  3", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  4", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  5", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  6", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  7", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  8", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor  9", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 10", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 11", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 12", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 13", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 14", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 15", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 16", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 17", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 18", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 19", {0,0,0,0,0,0,0,0}, 0.0, 45, 55},
  {FALSE, "Sensor 20", {0,0,0,0,0,0,0,0}, 0.0, 45, 55}
};
#endif

int activeSensors = 0;

int cnt = 0;

void setup()
{
// Open serial communications and wait for port to open:
  Serial.begin(9600);
    
  // set up the LCD's number of columns and rows: 
  lcd.begin(cols, rows);

#if defined SerialDebug
  eepromSpace = sizeof(ds18) / sizeof(byte);
  Serial.print(eepromSpace);
  Serial.println(F(" bytes in ds18 "));
  Serial.print(F("maxSensors = "));
  Serial.println(maxSensors);
#endif
  
//  delay(1000);
  
  EEPROM_readAnything(EEPROMidAddr, LCDtoken);
  lcd.clear();
  lcd.home();
  lcd.print(F("LCDtoken ="));
#if defined D16X2
  lcd.setCursor(0, 1);
#endif
#if defined D20X4
  lcd.setCursor(0, 2);
#endif
  lcd.print(LCDtoken);
  delay(1000);
  
  if(strcmp(lcdToken, LCDtoken) != 0) // should only happen once
  {
    lcd.clear();
    lcd.home();  
    lcd.setBacklight(RED);
    lcd.print(F("Clearing EEPROM"));
    EEPROM_writeAnything(EEPROMidAddr, lcdToken);
    for(int i = EEPROMdsAddr; i < eepromSpace; i++)
    {
      EEPROM.write(i, 0);
    }
#if defined SerialDebug
    Serial.println(F("EEPROM cleared"));
#endif
    EEPROM_writeAnything(EEPROMdsAddr, ds18);
    lcd.clear();
    lcd.home();  
    lcd.setBacklight(GREEN);
    lcd.print(F("EEPROM INITIALIZED"));
    delay(1000);
  }
  
  EEPROM_readAnything(EEPROMdsAddr, ds18); // get sensor structures from EEPROM
#if defined SerialDebug
  Serial.print(F("ds18 initialized - "));
  Serial.print(eeRead);
  Serial.println(F(" bytes read"));
#endif
  
  
  for(int cntx=0; cntx < maxSensors; cntx++)
  {
    if(ds18[cntx].sensorActive == TRUE){activeSensors++;}
  }

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

#if defined SerialDebug
   Serial.print(F("server is at "));
   Serial.println(Ethernet.localIP());
#endif

  lcd.clear();
  lcd.home();  
  lcd.setBacklight(WHITE);
  lcd.print(F("Server is at "));
#if defined D20X4
  lcd.setCursor(0, 2);
#endif
#if defined D16X2
  lcd.setCursor(0, 1);
#endif
  lcd.print(Ethernet.localIP());
  delay(2000);
}

void getOneWire(void)
{
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  
#if defined SerialDebug
  Serial.print(F("cnt = "));
  Serial.println(cnt);
#endif

  if (ds18[cnt].sensorActive)
  {
    type_s = 0;

    ds.reset();
    ds.select(ds18[cnt].addr);
    ds.write(0x44,1);         // start conversion, with parasite power on at the end
  
    delay(750);     // 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

    for ( i = 0; i < 9; i++) 
    {           // we need 9 bytes
      data[i] = ds.read();
    }

    // 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];
    if( showCelsius == TRUE)
    {
      ds18[cnt].deg = ((float)raw / 16.0);
    }else{
      ds18[cnt].deg = ((((float)raw / 16.0) * 1.8) + 31.0); //the raw value is Celsius, convert to Fahrenheit
    }
  }
    if( ++cnt >= maxSensors){cnt = 0;}
}

void sendHeader(EthernetClient client, bool refresh, int time)
{
  // 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(F("<html>"));
  if(refresh==TRUE)
  {
    // add a meta refresh tag, so the browser pulls again every (time) seconds:
    client.print(F("<head><meta http-equiv=\"refresh\" content=\""));
    client.print(time);
    client.println(F("\"></head>"));
  }
  client.println(F("<body><table border=\"5\" align=\"center\"><tr>"));
}

void sendFooter(EthernetClient client)
{
  client.println(F("</tr></table></body>"));
  client.println(F("</html>"));
}

void checkEthernet(void)
{

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

    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected())
    {
      if (client.available())
      {
        memset(clientline, 0, sizeof(clientline)); // clear the clientline
        if(client.readBytesUntil('/', clientline, BUFSIZE))
        {
          if(strcmp(clientline, "GET ") == 0 )
          {
            memset(clientline, 0, sizeof(clientline)); // clear the clientline
            if(client.readBytesUntil('HTTP', clientline, BUFSIZE))
            {
              Serial.print(F("GET Request - "));
              Serial.println(clientline);
              if(strstr(clientline, "index.htm") != 0)
              {
                sendHeader(client, TRUE, 5);
                showActiveSensors(client);
                sendFooter(client);
                break;
              }else if(strstr(clientline, "findsensors.htm") != 0){
                sendHeader(client, TRUE, 5);
                findSensors(client);
                sendFooter(client);
                break;
              }else if(strstr(clientline, "getsensor.htm") != 0){
                sendHeader(client, FALSE, 0);
                getSensors(client);
                sendFooter(client);
                break;
              }else if(strstr(clientline, ".ico") != 0){
                Serial.println(F("dumping .ico request"));
                break;
              }else if((strstr(clientline, " HTT") != 0) &&
                       (strstr(clientline, ".") == 0)){
                // no file named at all
                sendHeader(client, TRUE, 5);
                showActiveSensors(client);
                sendFooter(client);
                break;
              }else{
                noFileFound(client);
                break;
              }
            }
          }else if(strcmp(clientline, "POST ") != 0 ){
            Serial.print(F("POST Request - "));
            Serial.println(clientline);
            if(strstr(clientline, "updatesensor") != 0)
            {
              sendHeader(client, FALSE, 0);
              updateSensor(client);
              sendFooter(client);
            }else if(strcmp(clientline, "modifysensor") != 0)
            {
              sendHeader(client, FALSE, 0);
              modifySensor(client);
              sendFooter(client);
            }else{
              noFileFound(client);
            }
            break;
          }
        }
      }
    }
    endInternetSession(client);    
  }
}

void endInternetSession(EthernetClient client)
{
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println(F("client disonnected"));
}

void modifySensor(EthernetClient client)
{
  int sensor, cntx, eecnt;
  showSensorForm(client, "getsensor.htm", "get", TRUE);
  memset(clientline, 0, sizeof(clientline)); // clear the clientline
  index=0;
  do{
    char c = client.read();
    // Serial.write(c);
    clientline[index] = c;
    if(++index >= BUFSIZE){index = 0;}
  }while(strstr(clientline, "\n\r") == 0);
  findString(client, "=");
  sensor = client.parseInt();
  client.println(F("<td>Sensor "));
  client.print(sensor);
  client.print(F("</td>"));
  findString(client, "E&");
  client.print(F("<td>"));
  if(strstr(clientline, "TRUE") != 0)
  {
    ds18[sensor].sensorActive = TRUE;
    client.print(F("TRUE"));
  }else{
    ds18[sensor].sensorActive = FALSE;
    client.print(F("FALSE"));
  }
  client.print(F("</td>"));
  findString(client, "=");
  findString(client, "&");
  clientline[index-1] = 0;
  cntx=0;
  do
  {
    if(clientline[cntx] == '+')
    {
      ds18[sensor].sensorName[cntx] = ' ';
    }else{
      ds18[sensor].sensorName[cntx] = clientline[cntx];
    }
    cntx++;
  }while((clientline[cntx] != 0) && (cntx < sensorNameSize));
  Serial.print(cntx);
  Serial.println(F(" bytes written to sensorName"));
  do{
    ds18[sensor].sensorName[cntx] = ' ';
    Serial.print(F("space #"));
    Serial.println(cntx);
    cntx++;
  }while(cntx < sensorNameSize);
  cntx--;
  ds18[sensor].sensorName[sensorNameSize]=0; // terminate the string
  client.print(F("<td>"));
  client.print(ds18[sensor].sensorName);
  client.print(F("</td>")); 
  findString(client, "=");
  client.print(F("<td>"));
  for(cntx = 0; cntx < 8; cntx++)
  {
    ds18[sensor].addr[cntx] = client.parseInt();
    client.print(ds18[sensor].addr[cntx]);
    if(cntx < 7)
      {
        client.print(F(", "));
        findString(client, "%2C");
      }
  }
  client.print(F("</td>"));
  findString(client, "=");
  client.print(F("<td>"));
  ds18[sensor].tooCold = client.parseInt();
  client.print(ds18[sensor].tooCold);
  client.print(F("</td>"));
  findString(client, "tooHot");
  client.print(F("<td>"));
  ds18[sensor].tooHot = client.parseInt();
  client.print(ds18[sensor].tooHot);
  eecnt = EEPROM_writeAnything(EEPROMdsAddr, ds18); // save sensor info to EEPROM
  Serial.print(eecnt);
  Serial.println(F(" bytes written to EEPROM"));
  EEPROM_readAnything(EEPROMdsAddr, ds18); // get sensor info from EEPROM
  Serial.print(eecnt);
  Serial.println(F(" bytes read from EEPROM"));
  client.print(F("</td></tr><tr>"));
  client.print(F("<td colspan=\"6\" align=\"center\"> Saved to EEPROM Press Go to change another: <input type=\"submit\" name=\"GO\"></td></tr>"));
  client.println(F("</form>"));
}

void findString(EthernetClient client, char *string)
{
  // search for a given string in the incoming stream
  memset(clientline, 0, sizeof(clientline)); // clear the clientline
  index=0;
  do{
    char c = client.read();
    // Serial.write(c);
    clientline[index] = c;
    if(++index >= BUFSIZE){index = 0;}
  }while((strstr(clientline, string) == 0));
}

void updateSensor(EthernetClient client)
{
  // Serial.println(F("entering updateSensor"));
  findString(client, "SUBMIT");
  if(strstr(clientline, "=Sensor") != 0)
  {
    char *loc = strstr(clientline, "=Sensor");
    if (loc[8] == '&')
    {
      loc[8]=0;
    }else{
      loc[9]=0;
    }
    showSensorForm(client, "modifySensor.htm", "post", TRUE);
    client.print(F("<td align=\"center\">Sensor # "));
    int sensor = atoi(&loc[7]);
    client.print(sensor+1);
    client.print(F("<input type=\"hidden\" name=\"sensor\" value=\""));
    client.print(sensor);
    client.print(F("\">"));
    client.println(F("</td>"));
    client.print(F("<td align=\"center\"><input type=\"radio\" name=\"active\" value=\"TRUE\""));
    if(ds18[sensor].sensorActive == TRUE){client.print(F(" checked "));}
    client.print(F("> TRUE <br /><input type=\"radio\" name=\"active\" value=\"FALSE\""));
    if(ds18[sensor].sensorActive == FALSE){client.print(F(" checked "));}
    client.print(F("> FALSE </td>"));
    client.print(F("<td><input type=\"text\" name=\"sensorName\" size=\""));
    client.print(sensorNameSize);
    client.print(F("\" maxlength=\""));
    client.print(sensorNameSize-1);
    client.print(F("\" value=\""));
    client.print(ds18[sensor].sensorName);
    client.print(F("\"></td><td><input type=\"text\" size=\"35\" name=\"sensorAddr\" value=\""));
    for(int y = 0; y < 8; y++)
    {
      client.print(ds18[sensor].addr[y]);
      if(y<=6){client.print(F(","));}
    }
    client.print(F("\"></td><td><input type=\"text\" name=\"tooCold\" size=\"3\" value=\""));
    client.print(ds18[sensor].tooCold);
    client.print(F("\"></td><td><input type=\"text\" name=\"tooHot\" size=\"3\" value=\""));
    client.print(ds18[sensor].tooHot);
    client.println(F("\"></td></tr><tr>"));
    client.println(F("<td colspan=\"6\" align=\"center\"> Select the fields to modify, and press SUBMIT: <input type=\"submit\" name=\"SUBMIT\"></td></tr>"));
    client.println(F("</form>"));
  }else{
    client.println(F("<td><font size=\"5\" color=\"red\">No Sensor Selected</font></td>"));
  }
  endInternetSession(client);
}

void noFileFound(EthernetClient client)
{
  // 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>"));
}

void findSensors(EthernetClient client)
{
 byte addr[8];
 int cntx = 0;
 
  while ( ds.search(addr))
  {
    client.print(F("<td>Sensor "));
    client.print(cntx);
    client.print(F("= {"));
    for( int i = 0; i < 8; i++)
    {
      client.print(addr[i]);
      if(i < 7){client.print(F(", "));}
    }
    client.println(F("}</td></tr><tr>"));
    cntx++;
    delay(500);
  }
  client.print(F("<td colspan=\"2\" align=\"center\">"));
  client.print(cntx);
  client.print(F(" Sensor"));
  if(cntx == 1)
  {
    client.println(F(" Detected"));
  }else{
     client.println(F("s Detected"));
  }
  client.println(F("</td></tr>"));
  ds.reset_search();
}

void showActiveSensors(EthernetClient client)
{
  if(activeSensors == 0)
  {
    client.println(F("<td align=\"center\" valign=\"center\"><font size=\"10\" color=\"red\">&nbsp;&nbsp;No Sensors Selected&nbsp;&nbsp;</font></td>"));
  }else{
    for (int n=0, y=1; n<activeSensors; n++)
    {
      if(n<=maxSensors)
      {
        client.print(F("<td align=\"center\" valign=\"center\"><font size=\"10\"><div style=\"width: 250px\">"));
        if((int)ds18[n].deg > ds18[n].tooHot)
        {
          client.print(F("<font color=\"red\">"));
        }else if((int)ds18[n].deg < ds18[n].tooCold){
          client.print(F("<font color=\"blue\">"));
        }else{
          client.print(F("<font color=\"green\">"));
        }
        client.print(ds18[n].sensorName);
        client.print(F("</div><br />"));
        client.print(ds18[n].deg, 0);
        if (showCelsius == TRUE)
        {
          client.print(F("&deg;C"));
        }else{
          client.print(F("&deg;F"));
        }
        client.println(F("</font></font></td>"));
        if(y++ == newRow)
          {
            client.println(F("</tr><tr>"));
            y = 1;
          }
      }
    }
  }
  showSensorForm(client, "getsensor.htm", "get", FALSE);
  client.print(F("<td align=\"center\" colspan=\"5\">Add or Update a Sensor: <button type=\"submit\">UPDATE</button></td></form>"));
}

void showSensorForm(EthernetClient client, char *filename, char *method, bool showLabels)
{
  if(showLabels == TRUE)
  {
    client.print(F("<td align=\"center\">SELECT</td>"));
    client.print(F("<td align=\"center\">ACTIVE</td>"));
    client.print(F("<td align=\"center\">SENSOR<br />NAME</td>"));
    client.print(F("<td align=\"center\">SENSOR ADDRESS</td>"));
    client.print(F("<td align=\"center\">TOO<br />COLD</td>"));
    client.print(F("<td align=\"center\">TOO<br />HOT</td>"));
  }
  client.print(F("</tr><tr>"));
  client.print(F("<form id=\"getSensor\" action=\"http://"));
  client.print(Ethernet.localIP());
  client.print(F("/"));
  client.print(filename);
  client.print(F("\" method=\""));
  client.print(method);
  client.println(F("\">"));
}

void getSensors(EthernetClient client)
{
  activeSensors = 0;
  showSensorForm(client, "updatesensor.htm", "post", TRUE);
  int x, y;
  for(x = 0; x < maxSensors; x++)
  {
    client.print(F("<td><input type=\"radio\" name=\"Sensor\" value=\"Sensor"));
    client.print(x);
    client.print(("\">Sensor&nbsp;"));
    if(x < 10){client.print(F("&nbsp;"));}
    client.print(x+1);
    client.print(F("</td><td align=\"center\">"));
    if(ds18[x].sensorActive==TRUE)
    {
      client.print(F("TRUE"));
      activeSensors++;
    }else{
      client.print(F("FALSE"));
    }
    client.print(F("</td><td>"));
    for(y = 0; y < strlen(ds18[x].sensorName); y++)
    {
      if(ds18[x].sensorName[y] == ' ')
      {
        client.print(F("&nbsp;"));
      }else{
        client.write(ds18[x].sensorName[y]);
      }
    }
    client.print(F("<td align=\"center\">"));
    for(y = 0; y < 8; y++)
    {
      client.print(ds18[x].addr[y]);
      if(y<=6){client.print(F(","));}
    }
    client.print(F("</td><td align=\"center\">"));
    client.print(ds18[x].tooCold);
    client.print(F("</td><td align=\"center\">"));
    client.print(ds18[x].tooHot);
    client.println(F("</td></tr><tr>"));
  }
  client.println(F("<td colspan=\"6\" align=\"center\"> Select a sensor to modify, and press SUBMIT: <input type=\"submit\" name=\"SUBMIT\"></td></tr>"));
  client.println(F("</form>"));
  client.println(F("</tr></table><br /><br /><table border=\"5\" align=\"center\"><tr>"));;
  findSensors(client);
}

void displayLCDTemp()
{

  lcd.home();
  if(activeSensors==0)
  {    
    lcd.setBacklight(BLOFF);
    delay(500);
    lcd.setBacklight(RED);
#if defined D16X2
    lcd.print(F("Sensor Selected?"));
    lcd.setCursor(0, 1);
    lcd.print(F("Sensor Selected?"));
#endif
#if defined D20X4
    lcd.setCursor(0, 0);
    lcd.print(F("No Sensors Selected!"));
    lcd.setCursor(0, 1);
    lcd.print(F("No Sensors Selected!"));
    lcd.setCursor(0, 2);
    lcd.print(F("No Sensors Selected!"));
    lcd.setCursor(0, 3);
    lcd.print(F("No Sensors Selected!"));
#endif
  }else{
    showRed  = FALSE;
    showBlue = FALSE;
    for(int n=rowNum;n<activeSensors;n++)
    {
      if(ds18[n].deg > ds18[n].tooHot)
      {
        showRed  = TRUE;
        showBlue = FALSE;
      }else if(ds18[n].deg < 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<activeSensors;n++)
    {
      if(ds18[n].deg>=100)
      {
        addFSpace=TRUE;
        break;
      }else{
        addFSpace=FALSE;
      }
    }
    if((activeSensors > rows) && (rowNum+rows) >= activeSensors){rowNum = activeSensors-rows;}
    for(int n=rowNum,cnt=0;cnt<rows;cnt++,n++)
    {
      lcd.setCursor(0, cnt);
      if(ds18[n].sensorActive == TRUE)
      {
        lcd.setCursor(0, cnt);
        lcd.print(ds18[n].sensorName);
        if(ds18[n].deg < 100 && addFSpace)
        {
          lcd.print(F(" =  "));
        }else{
          lcd.print(F(" = "));
        }
        lcd.print(ds18[n].deg,0);
        lcd.print(degChar);
        if(addFSpace == FALSE){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].deg,0);
#endif
      }else{
        lcd.print(ds18[n].sensorName);
        lcd.print(F(" OFF   "));
#if defined SerialDebug
        Serial.print(ds18[n].sensorName);
        Serial.println(F(" Inactive"));
#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;
  }
}

void logData(void)
{
  EthernetClient client;
  String tempString = "";
  int  tempStringLength;
  
  if((millis() - lastConnectionTime < postingInterval) || (activeSensors == 0)) return;

  for(int id = 0; id < activeSensors; id++)
  {
    tempString += String(id);
    tempString += ",";
    tempString += String((int) ds18[id].deg);
    tempString += "\n";
  }
  tempStringLength = tempString.length();
  if (client.connect(cosmServer, 80))
  {
    Serial.println(F("connecting to cosm"));
    // send the HTTP PUT request:
    client.print(F("PUT /v2/feeds/"));
    client.print(FEEDID);
    client.println(F(".csv HTTP/1.1"));
    client.println(F("Host: api.cosm.com"));
    client.print(F("X-ApiKey: "));
    client.println(APIKEY);
    client.print(F("User-Agent: "));
    client.println(USERAGENT);
    client.print(F("Content-Length: "));
    client.println(tempStringLength);

    // last pieces of the HTTP PUT request:
    client.println(F("Content-Type: text/csv"));
    client.println(F("Connection: close"));
    client.println();

    // here's the actual content of the PUT request:
    client.println(tempString);
  }else {
    // if you couldn't make a connection:
    Serial.println(F("cosm connection failed"));
    Serial.println();
    Serial.println(F("disconnecting."));
  }
   // note the time that the connection was made or attempted:
  lastConnectionTime = millis();
  Serial.println(F("connection success"));
  Serial.println(F("disconnecting."));
  client.stop();
}
void loop(void)
{
  getOneWire();
  checkEthernet();
  checkButtons();
  displayLCDTemp();
  logData();
}
 
Hi Jimmay,

Without derailing the epicness of this thread, how did you learn all of this Arduino stuff? I've taken C classes in college, but I feel I'm missing the whole "hands on wires" experience. Any tips for getting into playing with boards/controllers/etc? What resources did you use? I can't help but think you were an EE major or something professionally related.

But this stuff, is simply stunning. Great work.
 
I have something similar to this controlling my system. It's more of a controller than a monitor though. I can set temps on my fermentation chambers or my hlt from my phone. I found heroku.com and pushed the web serving there. That way my phone (or any browser for that matter) can poll the heroku app, and the Arduino (actually a Fez Panda) can poll the heroku app at the same time.

Love seeing all the Arduino posts lately.
 
Hi Jimmay,

Without derailing the epicness of this thread, how did you learn all of this Arduino stuff? I've taken C classes in college, but I feel I'm missing the whole "hands on wires" experience. Any tips for getting into playing with boards/controllers/etc? What resources did you use? I can't help but think you were an EE major or something professionally related.

But this stuff, is simply stunning. Great work.


I was wondering the same, but also, what was the total cost of the parts for the project?
 
Im just getting started with Arduino and my Arduino/Android controlled brewing setup. The best way to get involved is to buy an arduino (around $30-$60) and start following the tutorials on arduino.cc (as well as sparkfun.com and adafruit.com) start with simple things like turning an LED on/off and the 1-wire temp probe is another great tutorial. Once you get the basics down, expanding to the more complex stuff is easier. Dive in with a starter kit to get you started.
 
Im just getting started with Arduino and my Arduino/Android controlled brewing setup. The best way to get involved is to buy an arduino (around $30-$60) and start following the tutorials on arduino.cc (as well as sparkfun.com and adafruit.com) start with simple things like turning an LED on/off and the 1-wire temp probe is another great tutorial. Once you get the basics down, expanding to the more complex stuff is easier. Dive in with a starter kit to get you started.

This...

Without derailing the epicness of this thread, how did you learn all of this Arduino stuff? I've taken C classes in college, but I feel I'm missing the whole "hands on wires" experience. Any tips for getting into playing with boards/controllers/etc? What resources did you use? I can't help but think you were an EE major or something professionally related.

I do not have a degree of any sort, but I was involved in hardware / software design in embedded systems for the better part of 30 years. I was lucky in that some good people recognized my knack for this stuff and gave me the opportunity to give it a try. I've worked on most of the early microprocessors (1802, 8008, 8080, Z-80, 6800, 6502, 8031, 8051) and have programmed in multiple languages (basic, pascal, ada, assembly, C, C++, etc). My last job was writing bare-metal code for touch-screen multi-game casino gaming machines for a well-known gaming company.

I have not been coding or designing for pay since 1999, but have kept my hand in with projects at home, including a distributed DVR using Linux\MythTv (7 simultaneous recording channels with 6 terabytes of storage), and web and mail servers that I use to host my and some close friends websites. The arduino caught my eye about 3 months ago, and I've been playing with it since.

As to cost, I really haven't been keeping track. I've bought several different boards to see what combination works best for me, and am still figuring that out :p
 
Ohmed out the I2C Expander board, breadboarded one up, and modified the driver library. Now I can control up to eight RGB LCD displays and 40 buttons with one Arduino Mega. :rockin:
 
Marking this so I remember. I built a small PCB board that takes a dual RJ45 jack and an on board DS18B20. Basically, I can use it as an inline sensor and run rj45 cabes between them.
 
Well, the 320x240 Shield I found and showed here is no longer available, and the library for it has disappeared. :mad:

I got mine working with the network updates and datalogging, but it's pretty much useless to anyone else. :(

oh well...

On a different note, I'm having a sample run of the addressable RGB-LCD-I2C boards done, so we'll see how that turns out.
 
This is the inline temp sensor I referred to in a previous post. I've debated making a slightly friendlier version with two single jacks on either end to make it easier to run inline. Finding a cheap jack was annoying, these were $1.58 each.

When time permits, my plan is to put a few of these in my fermentation chamber (ambient outside, bucket side, ice side and thermowell) to monitor multiple temps. I'm using a linear topology with actively driven sensors so it's easy to just connect them up with stock RJ45 cables. I also vaguely recall following some standard when I chose the pin connections.

RJ45
  • #1 is GND
  • #2 is +5V
  • #4 is DQ
  • #5 is GND

The resistor R1 is a 100 ohm per the guidelines here http://www.maxim-ic.com/app-notes/index.mvp/id/148

20120809-DSC_2052.jpg


schematic.png


20120809-DSC_2053.jpg
 
Because of this thread, I just ordered a full Inventor's Kit from Sparkfun!

Cheers to Jimmay!
 
I started off using the RJ45 cables as well. I took an 8-PORT Cat 5E UTP Patch Panel and wired it in parallel, essentially creating a linear topology, since none of the cables were >3m. I hooked up 8 sensors with no problems, except that the cat5e cable that I was using was too large to fit in the Brewer's Hardware Probe Ends. That's when I hit upon using the standard 4-conductor phone wire and RJ11 connectors. They were smaller, cheaper and easier to wire. With judicious placement of the probe wires, it still looks like a linear/stub setup, and I have not needed any additional resistors to read up to 8 probes.

If anything, I might do the improved CPU Bus Interface shown in appendix A of the document you supplied, and use a beefier independent 5V supply.

Of course, YMMV.
 
Last edited by a moderator:
I started off using the RJ45 cables as well. I took an 8-PORT Cat 5E UTP Patch Panel and wired it in parallel, essentially creating a linear topology, since none of the cables were >3m. I hooked up 8 sensors with no problems, except that the cat5e cable that I was using was too large to fit in the Brewer's Hardware Probe Ends. That's when I hit upon using the standard 4-conductor phone wire and RJ11 connectors. They were smaller, cheaper and easier to wire. With judicious placement of the probe wires, it still looks like a linear/stub setup, and I have not needed any additional resistors to read up to 8 probes.

If anything, I might do the improved CPU Bus Interface shown in appendix A of the document you supplied, and use a beefier independent 5V supply.

Of course, YMMV.

I like the patch panel idea, and you're correct about the cable lengths -- shouldn't be an issue. I'm also planning on using these things I made for my house which will end up with long runs.

I'm using the same probe ends and was able to fit the cable inside with a little slit in the cable (eventually it'll get adhesive heatshrink on it). I like the RJ11 idea though -- much less bulk for the connectors.

Let me know if you build the improved CPU interface, I'm curious if it makes an improvement in reliability. I have the required parts on my next order...
 
Last edited by a moderator:
I'm also planning on using these things I made for my house which will end up with long runs.

Yeah, if my wife didn't still have a business land-line coming into her home office, I would consider trying to use the house phone wiring as a test of a long-line 1-wire lan :drunk:
 
This is a 5" 800x480 Graphic LCD with resistive touchscreen.
GLCD-50.png

Got the monitor section working the way I want it, now on to SSR control. :ban:

The LCD and adapter shield are from Itead Studios, the EtherMega and protoshield from Freetronics via EpicTinker. The protoshield is used to re-position pins and house the DS18B20 interface and connector.

Some caveats:
Make sure you have a power supply capable of supplying at least 1 amp, power from the USB alone may not be sufficient, which caused the ethernet hookup to fail sporadically.

The software checks the on-board EEPROM for an initialized sensor array, and creates one if non-existent. Subsequent restarts use the EEPROM to initialize the sensor array. The EEPROM is also updated whenever you add a DS18B20 to the array, or modify the sensor name or temperature range.

This uses the same networked setup as previous versions, and has been setup for 12 sensors. You can modify the "high" and "wide" constants in the code (multiples of 8 work best) and the code will try to work out a "best fit" number of windows. Make sure you modify the "maxSensors" constant, and the sensor structures array to match the new count, and erase the EEPROM prior to the first run of the code.

This setup definitely requires a Mega2560 board or equivalent. I use the EtherMega, which has the W5100 baked right into the board, and uses the arduino ethernet library out of the box. This solves stacking problems caused by trying to add in a standard ethernet shield.

I mechanically moved the pins for the touchscreen from D2-D6 to D42-D46, and disconnected the pins for the on-board SD card on the LCD shield when I thought it was causing a conflict with the ethernet setup (turns out it was a power supply problem). My software reflects those changes, but may not be required.

The LCD shield requires plugging into all of the mega pins, and the 2x20 connector for the LCD board offers very little support, so some sort of support for the LCD board is suggested.

The 1.0.1 version of the Arduino IDE has a nifty String object library that has a major problem: the destructor doesn't properly remove unneeded Strings, and has massive memory leaks, This quickly consumes all available SRAM and causes really weird problems. This is avoided by doing everything the old fashioned way with character arrays and direct supervision. Use String objects at you own risk if modifying this code.

The UTFT library is available here. I've added code to the DefaultFonts.c file that adds a minus sign to the SevenSegment font for temperatures below zero. Because of the way that the font structure is set up, this added 3000 bytes of code that is unused yet unavailable. My copy is available here

The EEPROMWriteAnything library is here

You may see some commented-out code and references to the touchscreen. This code will not work properly if compiled, as it requires a modified library that I'm still working on.

Finally here's the code, last code release was 09/02/2012:
ITDB50_TempMonitor code.
 
Nice Im using the XLR headphone jacks for mine but I only have two so wiring directly into the arduino (no shield). Looking good
 
I ordered the high temp water proof DS18B20's from Adafruit Here since they were pre-made and I felt lazy. I extended the cable using Cat 3 phone line and terminated with 3-pin mini DINs. My initial use will be on my brew stand. I have found variation, up to 2-3 degrees, between sensors that are co-located so I need to iron out critical locations and use the mose accurate sensors there.

When building the probes using the Brewer's Hardware probe ends, what type of thernal expoxy are you using to fill the probes?
 
Got the DS2406+ samples from Maxim yesterday, gonna breadboard up one and see how it operates :)

On another note, I got a Raspberry Pi (RPi) a few days ago, and am working on integrating the RPi and an Arduino with an I2C interface, using the Arduino to handle the I/O intensive items, while using the RPi for the web/network/wireless control interface. I'd leave the 5" touchscreen for local control, and have a network interface.

I breadboarded a level-shifter for the Arduino-to-RPi I2C communications channel (the Arduino is 5v I/O, the RPi is 3v3), and got some rudimentary communication going just to verify the hardware.

This should be fun... :ban:
 
Back
Top