Client and webserver at the same time

Hello all,

Recently I received a ethernet shield for arduino.
It’s the arduino ethernet shield (not R3!) with the wiznet 5100 chip.
As fas as I can see the wiznet can have 4 simultaneous connections.

My question is; is it possible to have both a NTP client and a webserver on the same shield?
I would like to be able to get the time as a client from a NTP server and also show some data using the webserver functionality.
The time server is to make sure time is synced well and also makes sure summer and winter time are correct.

I have searched here and there, there was a topic, but no answer:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1244630088

Can anyone help me out with this?
I am using arduino V1.0

Thank you!
Robert

It is possible. Here is a subject that is more current: http://arduino.cc/forum/index.php/topic,80137.15.html

Some test code that might be of interest.

//zoomkat 12-08-11, combined client and server
//simple button GET with iframe code
//for use with IDE 1.0
//open serial monitor and send an g to test client and
//see what the arduino client/server receives
//web page buttons make pin 4 high/low
//use the \ slash to escape the " in the html 
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
IPAddress ip(192,168,1,102); // ip in lan
IPAddress gateway(192,168,1,1); // internet access via router
IPAddress subnet(255,255,255,0); //subnet mask
IPAddress myserver(208,104,2,86); // zoomkat web page
EthernetServer server(84); //server port
EthernetClient client;
String readString; 

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

void setup(){

  pinMode(4, OUTPUT); //pin selected to control
  Ethernet.begin(mac, ip, subnet, gateway); 
  server.begin();
  Serial.begin(9600); 
  Serial.println("server/client 1.0 test 12/08/11"); // keep track of what is loaded
  Serial.println("Send an g in serial monitor to test client"); // what to do to test client
}

void loop(){
  // check for serial input
  if (Serial.available() > 0) 
  {
    byte inChar;
    inChar = Serial.read();
    if(inChar == 'g')
    {
      sendGET(); // call sendGET function
    }
  }  

  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 

            //now output HTML data header
          if(readString.indexOf('?') >=0) { //don't send new page
            client.println("HTTP/1.1 204 Zoomkat");
            client.println();
            client.println();  
          }
          else {
            client.println("HTTP/1.1 200 OK"); //send new page
            client.println("Content-Type: text/html");
            client.println();

            client.println("<HTML>");
            client.println("<HEAD>");
            client.println("<TITLE>Arduino GET test page</TITLE>");
            client.println("</HEAD>");
            client.println("<BODY>");

            client.println("<H1>Zoomkat's simple Arduino 1.0 button</H1>");

            client.println("<a href=\"/?on\" target=\"inlineframe\">ON</a>"); 
            client.println("<a href=\"/?off\" target=\"inlineframe\">OFF</a>"); 

            //client.println("<IFRAME name=inlineframe src=\"res://D:/WINDOWS/dnserror.htm\" width=1 height=1\">");
            client.println("<IFRAME name=inlineframe style=\"display:none\" >");          
            client.println("</IFRAME>");

            client.println("</BODY>");
            client.println("</HTML>");
          }

          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on") >0)//checks for on
          {
            digitalWrite(4, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            digitalWrite(4, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
} 

//////////////////////////
void sendGET() //client function to send/receie GET request data.
{
  if (client.connect(myserver, 80)) {
    Serial.println("connected");
    client.println("GET /~shb/arduino.txt HTTP/1.0");
    client.println();
  } 
  else {
    Serial.println("connection failed");
    Serial.println();
  }

  while(client.connected() && !client.available()) delay(1); //waits for data
  while (client.connected() || client.available()) { //connected or data available
    char c = client.read();
    Serial.print(c);
  }

  Serial.println();
  Serial.println("disconnecting.");
  Serial.println("==================");
  Serial.println();
  client.stop();

}

Thanks both for your reply!

Edit:
Zoomkat your example is working fine!
I have altered it to work with a NTP server as well :slight_smile:

//zoomkat 12-08-11, combined client and server
//simple button GET with iframe code
//for use with IDE 1.0
//open serial monitor and send an g to test client and
//see what the arduino client/server receives
//web page buttons make pin 4 high/low
//use the \ slash to escape the " in the html 
//address will look like http://192.168.1.102:80 when submited
//for use with W5100 based ethernet shields


//NTP server implementation added by RobertJP

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
IPAddress ip(192,168,178,100); // ip in lan
IPAddress gateway(192,168,178,1); // internet access via router
IPAddress subnet(255,255,255,0); //subnet mask
IPAddress myserver(208,104,2,86); // zoomkat web page
EthernetServer server(80); //server port
EthernetClient client;
String readString; 

//////////////////////NTP declarations
IPAddress timeServer(77,245,91,218); // time.nist.gov NTP server
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 
EthernetUDP Udp; // A UDP instance to let us send and receive packets over UDP
unsigned int localPort = 8888; //Port to listen for UDP packets

void setup(){
  pinMode(4, OUTPUT); //pin selected to control
  Ethernet.begin(mac,ip,gateway,gateway,subnet);
  server.begin();
  Serial.begin(9600); 
  Serial.println("server/client 1.0 test 12/08/11"); // keep track of what is loaded
  Serial.println("Send an g in serial monitor to test client"); // what to do to test client
  Udp.begin(localPort);
  Serial.println("UDP started");
}

void loop(){
  // check for serial input
  if (Serial.available() > 0) 
  {
    byte inChar;
    inChar = Serial.read();
    if(inChar == 'g')
    {
      sendGET(); // call sendGET function
    }
  }  

  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 

            //now output HTML data header
          if(readString.indexOf('?') >=0) { //don't send new page
            client.println("HTTP/1.1 204 Robert");
            client.println();
            client.println();  
          }
          else {
            client.println("HTTP/1.1 200 OK"); //send new page
            client.println("Content-Type: text/html");
            client.println();

            client.println("<HTML>");
            client.println("<HEAD>");
            client.println("<TITLE>Arduino GET test page</TITLE>");
            client.println("</HEAD>");
            client.println("<BODY>");

            client.println("<H1>Zoomkat's simple Arduino 1.0 button</H1>");

            client.println("<a href=\"/?on\" target=\"inlineframe\">ON</a>"); 
            client.println("<a href=\"/?off\" target=\"inlineframe\">OFF</a>"); 

            //client.println("<IFRAME name=inlineframe src=\"res://D:/WINDOWS/dnserror.htm\" width=1 height=1\">");
            client.println("<IFRAME name=inlineframe style=\"display:none\" >");          
            client.println("</IFRAME>");

            client.println("</BODY>");
            client.println("</HTML>");
          }

          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on") >0)//checks for on
          {
            digitalWrite(4, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            digitalWrite(4, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
} 

void sendGET()
{
  Serial.println("Send GET");
  sendNTPpacket(timeServer); // send an NTP packet to a time server
   // wait to see if a reply is available
  delay(1000);  
  if ( Udp.parsePacket() ) { 
    Serial.println("Got it!"); 
    // 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;  
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);               

    // 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);                               


    // print the hour, minute and second:
    Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
    Serial.print(1+(epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
    Serial.print(':');  
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    Serial.print(':'); 
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch %60); // print the second
  }
  Serial.println("Done!");
}

// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(IPAddress& address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  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
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:         
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

Zoomkat your example is working fine, but not with a NTP server.

Where is it failing? What is the last message before the fail?

See edit :)

I changed the Ethernet.begin() to this;

Ethernet.begin(mac, ip);

Thus removed the gateway and subnet :)

I am wondering though: would this also be possible with a ENC28J60?

Good deal. I did not notice the error in the Ethernet.begin() parameters.

@zoomkat: You should change those parameters to the V1.0 format. There is a dns server ip required!

Ethernet.begin(mac,ip,dns,gateway,subnet);

or if your router takes care of dns, then

Ethernet.begin(mac,ip,gateway,gateway,subnet);

Hehe it's running smooth here, I am a happy man :)

I am wondering though: would this also be possible with a ENC28J60? It's much cheaper and I'm thinking of an embedded application. Ethernet shield is a little overkill if it can be done with a ENC28J60 $)

Anything can be done with ENC28J60. It's just a LOT harder. Wiznet is great because TCP/IP is in hardware.

SurferTim: Good deal. I did not notice the error in the Ethernet.begin() parameters.

@zoomkat: You should change those parameters to the V1.0 format. There is a dns server ip required!

Ethernet.begin(mac,ip,dns,gateway,subnet);

or if your router takes care of dns, then

Ethernet.begin(mac,ip,gateway,gateway,subnet);

I need to make a simple DNS example, but in the final posted code, the below substitution still works on my arduino since the time server IP address is used. One thing I did notice is the code produces a time that is one hour off from the bottom link.

  //Ethernet.begin(mac,ip,gateway,gateway,subnet);
  Ethernet.begin(mac,ip);

http://www.time.gov/timezone.cgi?UTC/s/0

Anything can be done with ENC28J60. It's just a LOT harder. Wiznet is great because TCP/IP is in hardware.

True. There is a NTP and HTTP example, but not combined. Got an example?

As soon as my enc28j60 breakout arrives, I will have a look too :)

RobertJP:

Anything can be done with ENC28J60. It's just a LOT harder. Wiznet is great because TCP/IP is in hardware.

True. There is a NTP and HTTP example, but not combined. Got an example?

Nope. You'd have to make it :)

That said, I am in the midst of adding NTP to NanodeUIP but it's not up yet.