Go Down

Topic: Excellent Arduino Ethernet Shield Web Server Tutorial (Read 205205 times) previous topic - next topic

oric_dan

Tim, I did some more tests today using your PUTTY scheme.

To confirm your results, for the WebServerST sketch, the server will hang if the timeout counter is disabled, but will timeout and recover if it's enabled. So, that's very good.

Also, as you know, zoomkat's scheme will pass the PUTTY test, as will my modification of it. But I'll leave the timeout code in there, in any case.

SurferTim

The only reservation I have about your/zoomkat's code is it does leave characters in the w5100 socket rx buffer. I have lost many sockets doing that. My server code empties the rx buffer (including any POST data in the request body) before sending a response. That pretty much insures a clean close without losing a socket. But that just me...  :D

oric_dan

Thanks, knowledge is king. Now to learn about sockets, :-).

zoomkat

The below meta refresh test code is for checking the turnaround time for a client/server interaction. The server code doesn't seem prone to hanging up on its own from what I can tell. The code has a counter to count the interactions.


Code: [Select]

// zoomkat's meta refresh data frame test page 8/17/13
// use http://192.168.1.102:84 in your brouser for main page
// http://192.168.1.102:84/data static data page
// http://192.168.1.102:84/datastart meta refresh data page
// for use with W5100 based ethernet shields
// set the refresh rate to 0 for fastest update
// use STOP for single data updates

#include <SPI.h>
#include <Ethernet.h>

const int analogInPin0 = A0;
const int analogInPin1 = A1;
const int analogInPin2 = A2;
const int analogInPin3 = A3;
const int analogInPin4 = A4;
const int analogInPin5 = A5;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // arduino ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port
unsigned long int x=0; //set refresh counter to 0
String readString;

//////////////////////

void setup(){
  Serial.begin(9600);
    // disable SD SPI if memory card in the uSD slot
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  server.begin();
  Serial.println("meta refresh data frame test 8/17/13"); // so I can keep track of what is loaded
}

void loop(){
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
         if (readString.length() < 100) {
          readString += c;
         }
        //check if HTTP request has ended
        if (c == '\n') {

          //check get atring received
          Serial.println(readString);

          //output HTML data header
          //client.println("HTTP/1.1 200 OK");
          //client.println("Content-Type: text/html");
          //client.println();
         
          client.print(F("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));

          //generate data page
          if(readString.indexOf("data") >0) {  //checks for "data" page
            x=x+1; //page upload counter
            client.print("<HTML><HEAD>");
            //meta-refresh page every 1 seconds if "datastart" page
            if(readString.indexOf("datastart") >0) client.print("<meta http-equiv='refresh' content='1'>");
            //meta-refresh 0 for fast data
            if(readString.indexOf("datafast") >0) client.print("<meta http-equiv='refresh' content='0'>");
            client.print("<title>Zoomkat's meta-refresh test</title></head><BODY><br>");
            client.print("page refresh number: ");
            client.print(x); //current refresh count
            client.print("<br><br>");
           
              //output the value of each analog input pin
            client.print("analog input0 is: ");
            client.print(analogRead(analogInPin0));
           
            client.print("<br>analog input1 is: ");
            client.print(analogRead(analogInPin1));
                       
            client.print("<br>analog input2 is: ");
            client.print(analogRead(analogInPin2));
           
            client.print("<br>analog input3 is: ");
            client.print(analogRead(analogInPin3));
                                   
            client.print("<br>analog input4 is: ");
            client.print(analogRead(analogInPin4));
           
            client.print("<br>analog input5 is: ");
            client.print(analogRead(analogInPin5));
            client.print("<br></BODY></HTML>");
           }
          //generate main page with iframe
          else
          {
            client.print(F("<HTML><HEAD><TITLE>Zoomkat's frame refresh test</TITLE></HEAD>"
            "Zoomkat's Arduino frame meta refresh test 8/17/13"
            "<BR><BR>Arduino analog input data frame:<BR>"
            "&nbsp;&nbsp;<a href='/datastart' target='DataBox' title=''yy''>META-REFRESH</a>"
            "&nbsp;&nbsp;&nbsp;&nbsp;<a href='/data' target='DataBox' title=''xx''>SINGLE-STOP</a>"
            "&nbsp;&nbsp;&nbsp;&nbsp;<a href='/datafast' target='DataBox' title=''zz''>FAST-DATA</a><BR>"
            "<iframe src='/data' width='350' height='250' name='DataBox'>"
            "</iframe><BR></HTML>"));
          }
          delay(1);
          //stopping client
          client.stop();
          //clearing string for next read
          readString="";
        }
      }
    }
  }
}

Google forum search: Use Google Search box in upper right side of this page.
Why I like my 2005 Rio Yellow Honda S2000  https://www.youtube.com/watch?v=pWjMvrkUqX0

SurferTim

#34
Aug 24, 2013, 11:03 am Last Edit: Aug 24, 2013, 01:25 pm by SurferTim Reason: 1
I wrote a socket test sketch a few months ago. It is a web server, plus does a dns request for Google and ntp request every 30 seconds. It shows the socket status at different points during each. It shows for each socket
Socket#<number>:<status byte> <local port> D:<remote ip>(remote port)

The common status byte codes are
0x00 = available (not used)
0x14 = server waiting for client
0x17 = server with client
0x22 = udp

Insure network settings are correct, including a valid dns server.
Code: [Select]
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <utility/w5100.h>
#include <Dns.h>

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,2,2);
IPAddress gateway(192,168,2, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress dnServer(192,168,2,1);

IPAddress timeServer;
EthernetServer server(80);
EthernetUDP Udp;
DNSClient dnsC;

long timer = 0L;
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

void setup() {
 Serial.begin(9600);

 pinMode(4, OUTPUT);
 digitalWrite(4, HIGH);
       
 Ethernet.begin(mac, ip, dnServer, gateway, subnet);

 Serial.println("Start");
 server.begin();
 ShowSockStatus();
 timer = millis();
 dnsC.begin(dnServer);

 if(dnsC.getHostByName("pool.ntp.org",timeServer)) {
   Serial.print("\r\nNTP address = ");
   Serial.println(timeServer);            
 }  
 else Serial.println("dns fail");
 ShowSockStatus();
}

void loop()
{
 IPAddress remoteAddr;
 //Check if a web client has attached.
 checkServer();
       
 if ((millis() - timer) > 30000) {
   if(dnsC.getHostByName("www.google.com",remoteAddr)) {
     Serial.print(F("\r\nIP address = "));
     Serial.println(remoteAddr);            
   }  
   else Serial.println(F("dns fail"));
         
   ShowSockStatus();
   delay(10);          
   timer = millis();
   checkTime();
 }
}

void ShowSockStatus()
{
 for (int i = 0; i < MAX_SOCK_NUM; i++) {
   Serial.print(F("Socket#"));
   Serial.print(i);
   uint8_t s = W5100.readSnSR(i);
   Serial.print(F(":0x"));
   Serial.print(s,16);
   Serial.print(F(" "));
   Serial.print(W5100.readSnPORT(i));
   Serial.print(F(" D:"));
   uint8_t dip[4];
   W5100.readSnDIPR(i, dip);
   for (int j=0; j<4; j++) {
     Serial.print(dip[j],10);
     if (j<3) Serial.print(".");
   }
   Serial.print(F("("));
   Serial.print(W5100.readSnDPORT(i));
   Serial.println(F(")"));
 }
}

unsigned long sendNTPpacket(EthernetUDP & Udp, IPAddress& address)
{
 memset(packetBuffer, 0, NTP_PACKET_SIZE);
 packetBuffer[0] = 0b11100011;   // LI, Version, Mode
 packetBuffer[1] = 0;     // Stratum, or type of clock
 packetBuffer[2] = 6;     // Polling Interval
 packetBuffer[3] = 0xEC;  // Peer Clock Precision
 packetBuffer[12]  = 49;
 packetBuffer[13]  = 0x4E;
 packetBuffer[14]  = 49;
 packetBuffer[15]  = 52;
 Udp.beginPacket(address, 123); //NTP requests are to port 123
 Udp.write(packetBuffer,NTP_PACKET_SIZE);
 Udp.endPacket();
}

void checkTime()
{
 Udp.begin(8888);
 sendNTPpacket(Udp, timeServer); // send an NTP packet to a time server
 Serial.println("\r\nTime check");
 ShowSockStatus();
 // wait to see if a reply is available
 delay(1000);  

 if ( Udp.parsePacket() ) {  
   // We've received a packet, read the data from it
   Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

   //the timestamp starts at byte 40 of the received packet and is four bytes,
   // or two words, long. First, esxtract the two words:

   unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
   unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
   // combine the four bytes (two words) into a long integer
   // this is NTP time (seconds since Jan 1 1900):
   unsigned long secsSince1900 = highWord << 16 | lowWord;  

   // now convert NTP time into everyday time:
   Serial.print("Unix time = ");
   // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
   const unsigned long seventyYears = 2208988800UL;    
   // subtract seventy years:
   unsigned long epoch = secsSince1900 - seventyYears;  
   // print Unix time:
   Serial.println(epoch);  
 }  
 else Serial.println("No NTP packet received");

 Udp.stop();
 ShowSockStatus();
}

void checkServer()
{
 EthernetClient client = server.available();
 if(client) {
   boolean currentLineIsBlank = true;
   boolean currentLineIsGet = true;
   int tCount = 0;
   char tBuf[64];
   int r,t;
   char *pch;

   Serial.println(F("\r\nServer client"));
   ShowSockStatus();

   Serial.print(F("\r\nClient request: "));

   // this controls the timeout
   int loopCount = 0;

   while (client.connected()) {
     while(client.available()) {
       // if packet, reset loopCount
       loopCount = 0;
       char c = client.read();
       if(currentLineIsGet && tCount < 63)
       {
         tBuf[tCount] = c;
         tCount++;
         tBuf[tCount] = 0;          
       }

       if (c == '\n' && currentLineIsBlank) {
         // send a standard http response
         Serial.println(tBuf);
         Serial.print(F("POST data: "));
         while(client.available()) Serial.write(client.read());
         Serial.println();

         pch = strtok(tBuf,"?");

         while(pch != NULL)
         {
           if(strncmp(pch,"t=",2) == 0)
           {
             t = atoi(pch+2);
             Serial.print(F("t="));
             Serial.println(t,DEC);            
           }

           if(strncmp(pch,"r=",2) == 0)
           {
             r = atoi(pch+2);
             Serial.print(F("r="));              
             Serial.println(r,DEC);
           }


           pch = strtok(NULL,"& ");
         }
         Serial.println(F("Sending response"));
         client.print(F("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<html>"));
         client.println(F("<body><H1>TEST</H1>"));
         client.println(F("<form method=GET>T: <input type=text name=t><br>"));
         client.println(F("R: <input type=text name=r><br><input type=submit></form>"));


         client.println(F("</body></html>"));
         client.stop();
       }
       else if (c == '\n') {
         currentLineIsBlank = true;
         currentLineIsGet = false;
       }
       else if (c != '\r') {
         currentLineIsBlank = false;
       }
     }

     loopCount++;

     // if 10000ms has passed since last packet
     if(loopCount > 10000) {
       // close connection
       client.stop();
       Serial.println(F("\r\nTimeout"));
     }

     // delay 1ms for timeout timing
     delay(1);
   }
   Serial.println(F("done"));
 }
}

edit: The timeServer ip will be assigned by dns from pool.ntp.org.
The available (0x00) and server waiting (0x14) sockets will show the remote ip and ports settings from the previous use of that socket.

neo3

Hi,

I use in my house an Arduino UNO with Ethernet Shield connected to the router that controls the lights of the house through the computer and smartphone by socket commands.

I am now trying to create a mini Webserver by Arduino to control the lights by Smart TV also through the browser, and I'm following the instructions in Part 5 of the tutorial.

I adapted the code from the tutorial with the code that I already used, but I have questions in some parts:

Code: [Select]
void loop() {

EthernetClient client = server.available();

// SE receber um caracter...

delay(50);
if (client) {

boolean currentLineIsBlank = true;

// guarda o caracter na string 'msg'
msg[0]=msg[1];
msg[1]=msg[2];
msg[2]=msg[3];
msg[3]=msg[4];
msg[4]= client.read();


char c = msg[4]; // Lê 1 byte (caractere) do cliente
HTTP_req += c;  // Salva o pedido HTTP 1 caractere de cada vez


if (msg[4]=='#')
{
   
   LOOKS AND EXECUTE SOCKET COMMANDS
   
} // Fim IF msg[4]
else
{
while (client.connected())
{
   if (client.available())
   {
if (c == '\n' && currentLineIsBlank)
{
// Envia um cabeçalho de resposta padrão HTTP
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
// Envia a página
client.println("<!DOCTYPE html>");
client.println("<html>");
client.println("<head>");
client.println("<title>Arduino - Luzes da Casa</title>");
client.println("</head>");
client.println("<body>");
client.println("<center>");
client.println("<h1>Luzes da Casa</h1>");
client.println("<p>Clique para Acender ou Apagar a luz.</p>");
client.println("<form method=\"get\">");
ProcessButton(client);
client.println("</form>");
client.println("</center>");
client.println("</body>");
client.println("</html>");
Serial.print(HTTP_req);
HTTP_req = "";    // Termina o pedido, limpa a String
break;
}

if (c == '\n')
{
   currentLineIsBlank = true;
}
else if (c != '\r')
{
   currentLineIsBlank = false;
}
   } // Fim (client.available())
} // Fim while (client.connected())
delay(1);      // Dar tempo ao navegador para receber os dados
client.stop(); // Fecha a conexão
} // Fim ELSE
} // Fim client
} // Fim Loop

// Mudar LUZ e enviar de volta para a caixa HTML
void ProcessButton(EthernetClient cl)
{
if (HTTP_req.indexOf("luz=Quarto") > 0)
{
  if (digitalRead(4) == LOW)
  {
  digitalWrite(4, HIGH);
  }
  else
  {
  digitalWrite(4, LOW);
  }
}

if (HTTP_req.indexOf("luz=Sala") > 0)
{
  if (digitalRead(5) == LOW)
  {
  digitalWrite(5, HIGH);
  }
  else
  {
  digitalWrite(5, LOW);
  }
}

cl.println F("<input type=\"submit\" value=\"Quarto\" onclick=\"submit();\" name=\"luz\" style=\"height:300px; width:300px; font-size:50px\">");
cl.println F("<input type=\"submit\" value=\"Sala\" onclick=\"submit();\" name=\"luz\" style=\"height:300px; width:300px; font-size:50px\">");
}


When making a socket connection, the Arduino would not be stuck in "While" blocking of analyzing the messages to be sent? And if I delete the "While", the Arduino would not be resubmitting the page every time it goes by Loop?

And in "client.stop ();", when making a socket connection, it would also be closed?

oric_dan

If you mean Part 5 of this tutorial, you will notice [as I mentioned a few posts ago], his <input> elements are wrong. He needs either a </input> or " />" to close them, and make them correct [at least I think so]. If you do what he does with multiple controls, and not just one, then they interact and the control logic gets mucked up.

http://startingelectronics.com/tutorials/arduino/ethernet-shield-web-server-tutorial/web-server-LED-control/

neo3


If you mean Part 5 of this tutorial, you will notice [as I mentioned a few posts ago], his <input> elements are wrong. He needs either a </input> or " />" to close them, and make them correct [at least I think so]. If you do what he does with multiple controls, and not just one, then they interact and the control logic gets mucked up.

http://startingelectronics.com/tutorials/arduino/ethernet-shield-web-server-tutorial/web-server-LED-control/


Now I put the " />".

But when making a socket connection, the Arduino would not be stuck in "While" blocking of analyzing the messages to be sent? And if I delete the "While", the Arduino would not be resubmitting the page every time it goes by Loop?

And in "client.stop ();", when making a socket connection, it would also be closed?

oric_dan

Tim will have to answer those questions. I'm just learning this stuff, like the " />", :-).

neo3

I changed the code, I wonder if will work the Webserver and Socket connection:

Code: [Select]
...

void loop() {
     
      EthernetClient client = server.available();
   
     // SE receber um caracter...
       
      delay(50);
      if (client) {
       
        boolean currentLineIsBlank = true;
        while (client.connected())
         {
           if (client.available())
           {
              // guarda o caracter na string 'msg'
              msg[0]=msg[1];
              msg[1]=msg[2];
              msg[2]=msg[3];
              msg[3]=msg[4];
              msg[4]= client.read();
             
             
              char c = client.read(); // Lê 1 byte (caractere) do cliente
              HTTP_req += c;  // Salva o pedido HTTP 1 caractere de cada vez
             
     
             if (msg[4]=='#')
             {

                LOOKS AND EXECUTE SOCKET COMMANDS
       
             } // Fim IF msg[4]
             else
             {
               if (c == '\n' && currentLineIsBlank)
               {
                  // Envia um cabeçalho de resposta padrão HTTP
                  client.println("HTTP/1.1 200 OK");
                  client.println("Content-Type: text/html");
                  client.println("Connection: close");
                  client.println();
                  // Envia a página
                  client.println("<!DOCTYPE html>");
                  client.println("<html>");
                  client.println("<head>");
                  client.println("<title>Arduino - Luzes da Casa</title>");
                  client.println("</head>");
                  client.println("<body>");
                  client.println("<center>");
                  client.println("<h1>Luzes da Casa</h1>");
                  client.println("<p>Clique para Acender ou Apagar a luz.</p>");
                  client.println("<form method=\"get\">");
                  ProcessButton(client);
                  client.println("</form>");
                  client.println("</center>");
                  client.println("</body>");
                  client.println("</html>");
                  Serial.print(HTTP_req);
                  HTTP_req = "";    // Termina o pedido, limpa a String
                  break;
               }
               
               if (c == '\n')
               {
                 currentLineIsBlank = true;
               }
               else if (c != '\r')
               {
                 currentLineIsBlank = false;
               }
             } // Fim ELSE
           } // Fim (client.available())
        } // Fim while (client.connected())
       
        delay(1);      // Dar tempo ao navegador para receber os dados
        client.stop(); // Fecha a conexão
     } // Fim client
   } // Fim Loop
   
  // Mudar LUZ e enviar de volta para a caixa HTML
  void ProcessButton(EthernetClient cl)
  {
      if (HTTP_req.indexOf("luz=Quarto") > 0)
      {
          if (digitalRead(4) == LOW)
          {
              digitalWrite(4, HIGH);
          }
          else
          {
              digitalWrite(4, LOW);
          }
      }
     
      if (HTTP_req.indexOf("luz=Sala") > 0)
      {
          if (digitalRead(5) == LOW)
          {
              digitalWrite(5, HIGH);
          }
          else
          {
              digitalWrite(5, LOW);
          }
      }
     
      cl.println F("<input type=\"submit\" value=\"Quarto\" onclick=\"submit();\" name=\"luz\" style=\"height:300px; width:300px; font-size:50px\" />");
      cl.println F("<input type=\"submit\" value=\"Sala\" onclick=\"submit();\" name=\"luz\" style=\"height:300px; width:300px; font-size:50px\" />");
  }

...

SurferTim

Quote
I changed the code, I wonder if will work the Webserver and Socket connection:

You changed it so it will fail.

Did you try this code?
http://playground.arduino.cc/Code/WebServerST


SurferTim

How many characters will this String store before you run out of SRAM?
Code: [Select]
              HTTP_req += c;  // Salva o pedido HTTP 1 caractere de cada vez

neo3


How many characters will this String store before you run out of SRAM?
Code: [Select]
              HTTP_req += c;  // Salva o pedido HTTP 1 caractere de cada vez



This part of the code is equal to the Tutorial:
http://startingelectronics.com/tutorials/arduino/ethernet-shield-web-server-tutorial/web-server-LED-control/

The string "HTTP_req" will not copy the answer from "c" to examine the response?

SurferTim

Quote
The string "HTTP_req" will not copy the answer from "c" to examine the response?

Yes, until the Arduino runs out of SRAM, at which time the SRAM will overflow (wrap around) and crash. Actually, you will probably have a stack collision before it wraps around.

My point was that is not really an excellent web server tutorial. There are some good example sections in that code, but if it doesn't work for you, it isn't excellent, is it?

neo3


Quote
The string "HTTP_req" will not copy the answer from "c" to examine the response?

Yes, until the Arduino runs out of SRAM, at which time the SRAM will overflow (wrap around) and crash. Actually, you will probably have a stack collision before it wraps around.

My point was that is not really an excellent web server tutorial. There are some good example sections in that code, but if it doesn't work for you, it isn't excellent, is it?



What tutorial do you recommend?

Currently I use the Arduino with socket commands through of the smartphone and laptop, but I want also put a Webserver to control by browser of the Smart TV.

Go Up