Control and Maintain Temperature via Internet using Arduino


Equipped with Ethernet interface, Arduino controls a boiler to maintain the set temperature and publish environmental data via Internet on Pachube, and the web interface allows  setting the temperature  remotely via Internet.


The project is designed to control an heating system in order to keep the temperature in a room above a certain value, which can be set from Internet or a Personal Computer by LAN, because ours is not a simple thermostat but a complex unit managed by Arduino, which can connect to a local network with ethernet interface.  The thermostat allows many functions, including the publication  temperature measured at regular intervals (set by firmware), as well as the state of the boiler (on or off). The datas are published on the website http://www.pachube.com, which provides a database which stores user data, also makes available a series of gadgets to be able to view this data directly into web pages.


The Circuit

We start our description of the hardware on this card from the temperature sensor: it is a DS18B20 equipped with a logic that can express in binary format, two bytes, the temperature measured by its sensor element and the data on the surveys are made available through a serial communication channel called one-wire.
The Ethernet module is a WIZNET network adapter that can be interfaced via the SPI bus (Serial Peripheral Interface) 4-wire.
The module is a WIZ811MJ ethernet (IEEE 802.3/802.3) and W5100-based chip with full magnetic filters ethernet connector and 2 LEDs for status signaling. The module communicates with the microcontroller by SPI. It works with 3.3 Vcc and the 3V3 are provided by Arduino.
The connector J3 is that counts the largest number of connections: it comes from the control signal of the transistor T1, the controller of the relay RL1 (in parallel with the coil there is the LED LD2, signaling,  the excitement of the relay). Through J3 the Arduino reads status of buttons P1 and P2 and the input IN, all with pull-up resistor. The yellow LED LD1 is used to give the state mode or assist the setting of operation modes made with the P1 button. This allows you to choose between modes of operation of the system: sensor, manual or thermostat. Pressing once set the system in the sensor mode (automatic) and that this operation is indicated by a flashing dell’LD1, two pressures set the sistem in manual mode (command from web page), which is indicated by two flashes Led. Finally, three pressures set the system in thermostat mode (slave input IN), and this is communicated by three consecutive flashes dell’LD1. Pressing the button P1 returns the sistem in the first mode.
As for P2, currently not used and is reserved for future developments.








R1: 4,7 kohm
R2: 10 kohm
R3: 470 ohm
R4: 4,7 kohm
R5: 4,7 kohm
R6: 4,7 kohm
R7: 4,7 kohm
R8: 4,7 kohm
U1: WIZNET
U2: DS18B20
P1: Microswitch
P2: Microswitch
LD1: led 3 mm yellow
LD2: led 3 mm red
T1: BC547
RL1: Relé 5V
D1: 1N4007
- Screw connector 2 via
- Screw connector 3 via
- Strip male 6 via(2 pz.)
- Strip male 8 via (2 pz.)
- Strip female 10 via (4 pz.)
- PCB



How to use

Opening in the browser the thermostat IP address  (defined by the Arduino firmware) you can access the page and set the temperature of the system, the functions or active manually the boiler.
In the web page in memory dell’Arduino is also inserted a reference to the graphs of the gadget sites,  in the graph bottom of the page where you can choose which display period (from 1 minute to 1 year).
The web page contains the top, some buttons and boxes for the settings. There is a refresh button and below there are an indication of the status of the boiler (OFF or ON):  the system can be used for both heating systems both cooling and air conditioning: it depends on how connected the relay. In fact, with the exchange between C and NO it controls the boiler, while that between C and NC controls the air conditioner, this is because until the temperature is less than the threshold, the relay is energized and closes NO to NC, so it can turn on a boiler or other heating system, while when the temperature exceeds the set threshold, the relay returns to rest and NC close to C, going to control the cooling system.
We continue with the analysis of web page talking about the radio buttons under the present condition of the boiler, which are three and let you set how our system should work: clicking on the relay sensor is controlled based on temperature detected by the Dallas sensor, while opting for manual you can make the relay turn on and off using the On-screen buttons Boiler (relay activated), and Off Boiler (relay deactivated). Finally, opting for thermostat, the relay will be controlled according to the condition of our input IN interface card, this mode allows you to enslave the functioning of our system to an existing thermostat is substantially repeated in the environment and to relay the condition of the thermostat itself.
Below the radio buttons that define how, is the section of the temperature setting: Click in the box, you can set at will (within limits) the temperature at which the system should take, the setting made applies here by clicking the Set temperature.

We conclude the description of the web page with the chart, which summarizes the time courses of temperature measured by the sensor and the display may relate to a period of time to choose between 1 minute and 1 year.
As mentioned, the public system at that site www.pachube.com the value of the measured temperature, in a personal page reserved for each registered user, this site provides a service for storage and display of data from various sources, ranging from information gathered from sensors in environmental data, various measures, then everything is shared, or made accessible to registered users of the community.
In order to publish data on www.pachube.com you must first register and create an account, then take your chosen username and validated by the site and write in the special section of the firmware on the feed, without informing the user name of firmware, our system will not be able to access the site and publish the data.
Delivery of data for the upgrade can be done with a frequency set by firmware, bearing in mind the limits allowed by the site (about 50 accesses per hour) for obvious reasons, because if any application tries to access in real time site for uploading the data, at some point the bandwidth of the connection used by it and would soon be saturated with its server would have some problem to run.
Well, having said that we can conclude by pointing out that clicking on the word pachube that is below the temperature graph, you enter the page on www.pachube.com mail account used by the system to publish data on the page shown address of the corresponding link, containing the account name.


The Sketch
The sketch contains 3 .PDE files. This is the main program.

Download ZIP File Download the Thermostat sketch





Source Code:
/* Thermostat with arduino

 created 2011
 by Boris Landoni

 This example code is in the public domain.

http://www.s2ptech.blogspot.com

http://www.futurashop.it

 */

#include <SPI.h>
#include <Ethernet.h>
#include <avr/pgmspace.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#define SHARE_FEED_ID              5102     // this is your Pachube feed ID that you want to share to
#define REMOTE_FEED_ID             256      // this is the ID of the remote Pachube feed that you want to connect to
#define REMOTE_FEED_DATASTREAMS    4        // make sure that remoteSensor array is big enough to fit all the remote data streams
#define UPDATE_INTERVAL            20000    // if the connection is good wait 10 seconds before updating again - should not be less than 5
#define RESET_INTERVAL             10000    // if connection fails/resets wait 10 seconds before trying again - should not be less than 5
#define PACHUBE_API_KEY            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // fill in your API key
#define ONE_WIRE_BUS 4
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

int puls1 = 5;                 // pulsante per il cambio di modalità di funzionamento
int puls2 = 2;                 // pulsante per il cambio di modalità di funzionamento
int in    = 3;                 // ingresso del termostato

prog_char string_0[] PROGMEM  = "<html><head><title>Boris's Project</title><META HTTP-EQUIV=\"Refresh\" CONTENT=\"3600\"></head>";
prog_char string_1[] PROGMEM  = "<body><center><br><form> <input type=button value=\"Refresh \" onClick=\"window.location.reload()\"> </form><br><br><h2>Remote Control Thermostat   ";
prog_char string_2[] PROGMEM  = " C</h2><form method=GET><font size= 5>Boiler ";
prog_char string_3[] PROGMEM  = "</font><br><font size= 3>Input Thermostat ";
prog_char string_4[] PROGMEM  = "</font><br><table><td><input type=\"radio\" name=\"statourl\" value=\"1\" onClick=\"this.form.submit()\"  ";
prog_char string_5[] PROGMEM  = "></td><td>Sensor</td><td><input type=\"radio\" name=\"statourl\" value=\"2\" onClick=\"this.form.submit()\" ";
prog_char string_6[] PROGMEM  = "></td><td>Manual</td><td><input type=\"radio\" name=\"statourl\" value=\"3\" onClick=\"this.form.submit()\" ";
prog_char string_7[] PROGMEM  = "></td><td>Thermostat</td><br></table> </form><form method=GET>";
prog_char string_8[] PROGMEM  = "<table><tr><td><input type=submit name=b11 value=\"Boiler ON\"></td><td><input type=submit name=b10 value=\"Boiler OFF\"></td><td>";
prog_char string_9[] PROGMEM  = "</td></tr></table></form><form method=GET>Temperature: <input type=\"text\" size=\"2\" name=temp value=#* MAXLENGTH=2>";
prog_char string_10[] PROGMEM  = "<input type=\"submit\" value=\"Set temperature\"><br></form>";
prog_char string_11[] PROGMEM = "<br><OBJECT classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"";
prog_char string_12[] PROGMEM = "codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab";
prog_char string_13[] PROGMEM = "#version=6,0,0,0\" WIDTH=\"200\" HEIGHT=\"100\" id=\"gauge\"><PARAM NAME=\"movie\"";
prog_char string_14[] PROGMEM = "VALUE=\"http://apps.pachube.com/scaredycat/gauge.swf?xml_source=";
prog_char string_15[] PROGMEM = "http%3A//apps.pachube.com/scaredycat/getData.php";
prog_char string_16[] PROGMEM = "%3Fm%3D0%26f%3D5102%26s%3D0%26u%3D30%26l%3D0%26n%3D5%26t%3D";
prog_char string_17[] PROGMEM = "Temperatura%26w%3Dfalse%26c1%3D33FF33%26c2%3DEFE415%26c3%3DEF8B15%26c4%3DFF3333%26";
prog_char string_18[] PROGMEM = "in%3Dtrue\" /><PARAM NAME=\"quality\" VALUE=\"high\" /><param name=\"wmode\" value=\"transparent\">";
prog_char string_19[] PROGMEM = "<param name=\"allowScriptAccess\" value=\"sameDomain\" /><EMBED src=\"";
prog_char string_20[] PROGMEM = "http://apps.pachube.com/scaredycat/gauge.swf?xml_source=";
prog_char string_21[] PROGMEM = "http%3A//apps.pachube.com/scaredycat/getData.php";
prog_char string_22[] PROGMEM = "%3Fm%3D0%26f%3D5102%26s%3D0%26u%3D30%26l%3D0%26n%3D5%26t%3D";
prog_char string_23[] PROGMEM = "Temperatura%26w%3Dtrue%26c1%3D33FF33%26c2%3DEFE415%26c3%3DEF8B15%26c4%3DFF3333%26in%3Dtrue\"";
prog_char string_24[] PROGMEM = "quality=\"high\" wmode=\"transparent\" WIDTH=\"200\" HEIGHT=\"100\"";
prog_char string_25[] PROGMEM = "NAME=\"gauge\" allowScriptAccess=\"sameDomain\" swLiveConnect=\"true\"";
prog_char string_26[] PROGMEM = "TYPE=\"application/x-shockwave-flash\" PLUGINSPAGE=\"";
prog_char string_27[] PROGMEM = "http://www.macromedia.com/go/getflashplayer\"></EMBED></OBJECT>";
prog_char string_28[] PROGMEM = "<br><br><script type=\"text/javascript\" src=\"http://www.google.com/jsapi\">";
prog_char string_29[] PROGMEM = "</script><script language=\"JavaScript\" src=\"http://apps.pachube.com/google_viz/viz_multi.js\">";
prog_char string_30[] PROGMEM = "</script><script language=\"JavaScript\">createViz([5102,5102],[0,1],800,400,[\"ff0000\",\"0000ff\"],0,40);</script>";
prog_char string_31[] PROGMEM = "";
prog_char string_32[] PROGMEM = "";
prog_char string_33[] PROGMEM = "<font size= 2>Powered by Open-Electronics.org - Boris Landoni</font>"; //please don't remove <img src="http://www.open-electronics.org/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley">
prog_char string_34[] PROGMEM = "";
prog_char string_35[] PROGMEM = "";
prog_char string_36[] PROGMEM = "";
prog_char string_37[] PROGMEM = "";
prog_char string_38[] PROGMEM = "";
prog_char string_39[] PROGMEM = "";
prog_char string_40[] PROGMEM  = "<br></center></body></html>";
prog_char string_41[] PROGMEM  = "<font color=\"red\">ON</font>";
prog_char string_42[] PROGMEM  = "OFF";
prog_char string_43[] PROGMEM  = "checked";
prog_char string_44[] PROGMEM  = "on";
prog_char string_45[] PROGMEM  = "off";

PROGMEM const char *string_table[] = // change "string_table" name to suit
{
string_0,
string_1,
string_2,
string_3,
string_4,
string_5,
string_6,
string_7,
string_8,
string_9,
string_10,
string_11,
string_12,
string_13,
string_14,
string_15,
string_16,
string_17,
string_18,
string_19,
string_20,
string_21,
string_22,
string_23,
string_24,
string_25,
string_26,
string_27,
string_28,
string_29,
string_30,
string_31,
string_32,
string_33,
string_34,
string_35,
string_36,
string_37,
string_38,
string_39,
string_40,
string_41,
string_42,
string_43,
string_44,
string_45
};

char buffer[150]; // make sure this is large enough for the largest string it must hold

byte mac[] = { 0xDE, 0xAD, 0xBA, 0xAF, 0xFA, 0xAD };
byte ip[] = { 192, 168, 0, 172 };
byte gateway[] = { 192, 168, 0, 1 };
byte subnet[] = { 255, 255, 255, 0 };
byte remoteServer[] = { 173,203,98,29 };            // pachube.com
float remoteSensor[REMOTE_FEED_DATASTREAMS];        // we know that feed 256 has floats - this might need changing for feeds without floats
String inString = String(35);
Server server(80);
const int led = 6;
const int rele = 7;
String temp="";
String statourl="";
int tam=0;
int st1=42,st2=42,st3=42,st4=42,st5=43,st6=44;
//float tempCold;
unsigned long pubblica;
unsigned long lampeggio;
int stato=1; // se 0 controllo con sensore temperatura integrato, se 1 controllo manuale, se 2 controllo con termostato esterno
float tempC=0;

void setup()
{
      pinMode(puls1, INPUT);
      pinMode(puls2, INPUT);
      pinMode(in, INPUT);
      pinMode(led, OUTPUT);
      pinMode(rele, OUTPUT);
      Serial.begin(9600);
      Ethernet.begin(mac, ip,gateway,subnet);
      server.begin();
      Serial.println("Serial READY");
      Serial.println("Ethernet READY");
      Serial.println("Server READY");
        sensors.begin();
        Serial.print("Found ");
        Serial.print(sensors.getDeviceCount(), DEC);
        Serial.println(" devices.");
        Serial.print("Parasite power is: ");
        if (sensors.isParasitePowerMode()) Serial.println("ON");
        else Serial.println("OFF");
        if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
        Serial.print("Device 0 Address: ");
        printAddress(insideThermometer);
        Serial.println();
        // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
        sensors.setResolution(insideThermometer, 9);
        Serial.print("Device 0 Resolution: ");
        Serial.print(sensors.getResolution(insideThermometer), DEC);
        Serial.println();

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

void printWebPage(Client *client)
{
    int tmp=0;
    int temperatura=0;
      // send a standard http response header
      client->println("HTTP/1.1 200 OK");
      client->println("Content-Type: text/html");
      client->println();

      strcpy_P(buffer, (char*)pgm_read_word(&(string_table[0]))); // Necessary casts and dereferencing, just copy.
      client->println( buffer );
      //Serial.println( buffer );
      for (int i = 1; i < 41; i++)
      {
          if (i==9)
          {
              strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i])));  //butto tutto nell'array buffer
              for (tmp=0 ; tmp < sizeof(buffer); tmp++)
              {
                if (buffer[tmp]=='#')
                {
                  //Serial.println( "trovato ##### " );
                  temperatura = EEPROM.read(0);
                  Serial.print("dato in mem : ");
                  Serial.println(temperatura);
                  buffer[tmp] =  char ((temperatura/10)+48);
                  tmp++;
                  buffer[tmp] =  char ((temperatura % 10)+48);
                }

              }
          }
          else
          {
            strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
          }
          client->println( buffer );

          switch(i){

              case 1:
                  itoa (tempC, buffer, 10); client->print( buffer ); Serial.print( buffer ); client->print( "," ); Serial.print( "," ); itoa ((int(tempC * 100) % 100), buffer, 10); client->print( buffer ); Serial.print( buffer ); break;
              case 2:
                  if(digitalRead(rele)) st4=41;
                  else st4=42;
                  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[st4]))); client->println( buffer ); Serial.println( buffer ); break;
              case 3:
                  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[st6]))); client->println( buffer ); Serial.println( buffer ); break;
              case 4:
                if (stato==1){
                  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[st5]))); client->println( buffer ); Serial.println( buffer ); break;
                  }
              case 5:
                if (stato==2){
                  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[st5]))); client->println( buffer ); Serial.println( buffer ); break;
                  }
              case 6:
                if (stato==3){
                  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[st5]))); client->println( buffer ); Serial.println( buffer ); break;
                  }
          }
          delay(30);
      }

}

void verificaingressi()
{
  int temperatura = EEPROM.read(0);
  if (!digitalRead(puls1))
      {
        stato=stato+1;
        if (stato>3)
        {
          stato=1;
        }
       EEPROM.write(1, stato);
       digitalWrite(led,HIGH);
       delay(1000);
       digitalWrite(led,LOW);
       delay(1000);
       lampeggio=4000; //così lampeggia poi subito
      }

            if (stato==1)
      {
        if (tempC<temperatura )
        {
           digitalWrite(rele,HIGH);
           //st4=41;
        }
        else
        {
          if (tempC>(temperatura+1))
          {
            digitalWrite(rele,LOW);
            //st4=42;
          }
        }
      }

        if (!digitalRead(in))
        {
           if (stato==3) digitalWrite(rele,HIGH);
           st6=44;
        }
        else
        {
            if (stato==3) digitalWrite(rele,LOW);
            st6=45;
        }
}

void loop()
{

      stato = EEPROM.read(1);

      verificaingressi();
      sensors.requestTemperatures(); // Send the command to get temperatures

      tempC = sensors.getTempC(insideThermometer);
      if (millis() < pubblica) pubblica = millis();

      if ((millis() - pubblica) > UPDATE_INTERVAL){
          pubblica = millis();  

          Serial.println("Pubblico");
          Serial.print("Temp C    : ");
          Serial.println(tempC);
          if (tempC<50 && tempC>-20)
          {
            pachube_in_out(tempC);
          }
      }

      if (millis() < lampeggio)  lampeggio = millis();
      if ((millis() - lampeggio) > 5000){    //ogni 5 sec faccio lampeggiare il led giallo
          lampeggio = millis();
          for (int i = 0; i < stato; i++)
          {
                   //Serial.println("lamp");
                   digitalWrite(led,HIGH);
                   delay(250);
                   digitalWrite(led,LOW);
                   delay(250);
          }
      }

      Client client = server.available();

      int led=0;
      int tmp=0;
      if (client) {

      // an http request ends with a blank line
      boolean current_line_is_blank = true;
      while (client.connected()) {

      if (client.available()) {

      char c = client.read();
      if (inString.length() < 35) {
      inString.concat(c);

      }
      if (c == '\n' && current_line_is_blank) {

          if(inString.indexOf("b10")>=0){
            digitalWrite(led,LOW);
            stato=2;
          }
          if(inString.indexOf("b11")>=0){
            digitalWrite(led,HIGH);
            stato=2;
          }         

          if(inString.indexOf("b40")>=0){
            digitalWrite(led,LOW);
          }
          if(inString.indexOf("b41")>=0){
            digitalWrite(led,HIGH);
          }
          if(digitalRead(rele)) st4=41;
          else st4=42;  

          if (inString.indexOf("temp")>=0){
            char tempArr[20];
            temp=inString.substring(inString.indexOf('p')+2, inString.indexOf(" H"));
            temp.toCharArray(tempArr, 5);
            EEPROM.write(0, atoi(tempArr));
            tmp = EEPROM.read(0);
            stato=1;
          }

          if(inString.indexOf("statourl")>=0){
             char statoArr[20];
            statourl=inString.substring(inString.indexOf('l')+2, inString.indexOf(" H"));
            statourl.toCharArray(statoArr, 5);
            stato=(atoi(statoArr));
          }     

      EEPROM.write(1, stato);
      verificaingressi();
      printWebPage( &client);
      break;
      }
      if (c == '\n') {
      // we're starting a new line
      current_line_is_blank = true;
      } else if (c != '\r') {
      // we've gotten a character on the current line
      current_line_is_blank = false;
      }
      }
      }
      // give the web browser time to receive the data
      delay(1);
      inString = "";
      client.stop();
      }
}









1 Comments
Disqus
Fb Comments
Comments :

1 comment:

  1. Hi,

    Nice project.
    Can i get the eagle schematics & pcb files .

    Regards
    Franky

    ReplyDelete