Ethernet shield - as a network sensor node

Since the web server example for Ethernet shield was published a few month ago, http://www.nuelectronics.com/estore/?p=12 and http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1206024602/0, I've got a few emails regarding to how to use the Ethernet shield as a web client.

In a sense, a web client application is more appropriate for a small device such as the Ethernet shield on Arduino. Based on the TCP/IP protocol, it is the web client (such as a web browser) who initializes the TCP connections, requests data from or sends data to the web server. Therefore the ethernet shield running in web client mode can be used as a distributed sensor node in the network. It can send periodic or interrupt driven sensor data to a web server. The web server can then record, process and show the sensor data from one or multiple web client.

Therefore I developed a new version ethernet shield library, web client examples and web server (using Apache web server) PHP script to implement this web client application. You can find detailed description in my blog - http://www.nuelectronics.com/estore/?p=14

Here is a picture that shows list of web clients, running as distributed network sensor nodes, which send sensor data to a webserver.

Hi, I posted a comment on your website but it seems there is nobody to answer.
I hope I will have more chance here :wink:

My question was : how client example can work without IP address of the server? For example, if my server is at http://www.myserver.com, I'm not sure he has a fixed IP. So would it be possible to supply web name of the server instead of IP address.
Thank you.

If you have other ideas when using this example with a non-fixed IP server ....

My question was : how client example can work without IP address of the server? For example, if my server is at http://www.myserver.com, I'm not sure he has a fixed IP. So would it be possible to supply web name of the server instead of IP address.
Thank you.

If you have other ideas when using this example with a non-fixed IP server ....

I am not nuelectronics but IMHO you may find it much easier ensuring that your webserver has a fixed address then trying to resolve a dynamic address from within the Arduino.

If you do need to resolve an IP address from the server name, you could add DNS client code into your sketch. The protocol is not highly complicated (if you are familiar with implementing internet protocols) and I would think that one could add the code to your sketch to handle the DNS messages.
But I think this would not be a good approach. Your sketch has to handle the DNS messages and cope with the possibility that the server IP address may change between requests. This will probably consume a fair amount of memory and this is a scarce resource in the arduino.

If you can't ensure that the Server IP address is fixed, perhaps you can use an external dynamic DNS service that translates a fixed IP address known to the clients into the actual dynamic IP address of the server.

I am curious to know what prevents you from ensuring that your web servers address remains fixed.

I don't know if my server will keep its IP or not, but I tried this in local with wamp and it works. But when I tried with an external server (my personal web site) it fails.

I looked in the sketch and whith an ethernet analyzer and it seems that arduino never receives ARP answer to its request.

Someone else has tried this ?
IMHO arp doesn't work if both devices (arduino and my server) are not in the same subnet, is that right ?

Is there a solution ?

Regards,

I don't know if my server will keep its IP or not, but I tried this in local with wamp and it works. But when I tried with an external server (my personal web site) it fails.

I looked in the sketch and whith an ethernet analyzer and it seems that arduino never receives ARP answer to its request.

Someone else has tried this ?
IMHO arp doesn't work if both devices (arduino and my server) are not in the same subnet, is that right ?

Is there a solution ?

Regards,

Your gateway or router should handle and return the ARP request for an IP address on a different subnet. How are you connected to the internet?

Your gateway or router should handle and return the ARP request for an IP address on a different subnet.

Nope, ARP is a layer 2 (ethernet) protocol that maps ethernet addresses to IP addresses on the LAN. ARP does not get routed.

An IP device will a) know it's own subnet (from IP address + netmask) and optionally but typically b) have a list of routers to get to other networks, usually including a default router (or default gateway).

In the case of IP traffic destined for a non-local network, the device will send the packet to the appropriate router (gateway) address and let the router worry about delivering it. If you see an ARP associated with initiating communication with a non-local IP, that ARP request should be for the router, not the destination IP.

If that ARP request you sniffed is for the non-local IP, I would look at the IP, netmask, and default router configuration of the node that originated the traffic.

-j

Your gateway or router should handle and return the ARP request for an IP address on a different subnet.

Nope, ARP is a layer 2 (ethernet) protocol that maps ethernet addresses to IP addresses on the LAN. ARP does not get routed.

An IP device will a) know it's own subnet (from IP address + netmask) and optionally but typically b) have a list of routers to get to other networks, usually including a default router (or default gateway).

In the case of IP traffic destined for a non-local network, the device will send the packet to the appropriate router (gateway) address and let the router worry about delivering it. If you see an ARP associated with initiating communication with a non-local IP, that ARP request should be for the router, not the destination IP.

If that ARP request you sniffed is for the non-local IP, I would look at the IP, netmask, and default router configuration of the node that originated the traffic.

-j

I was actually trying to say the same thing in fewer words, but I guess it did not come out clearly.

try this: Your gateway or router should handle and return the ARP request (as a local ethernet address on the router) for an IP address on a different subnet.

I'm unable to get through my local router as well. I have a static IP on my webserver, and I believe that the ARP response isn't getting through my router. I've got no way to tell though, because I honestly don't know what I'm doing.

All works fine on a local network, but once I change the local IP to the webserver IP, nothing gets returned. After doing some serial traces, I found that it gets stuck at the ARP_SENT part of client_process. In this bit of code, the var plen always equals 0 so it never gets to the next step and times out.

if(client_state == ARP_SENT){        
plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);

            // destination ip address was found on network
        if ( plen!=0 )
        {
            if ( es.ES_arp_packet_is_myreply_arp ( buf ) ){
              
                      Serial.println("destination ip address was found on network");
                client_state = ARP_REPLY;
                        syn_ack_timeout=0;
                        return;
            }
            
            }
              delay(10);
            syn_ack_timeout++;
            
            
            if(syn_ack_timeout== 100) {  //timeout, server ip not found
                  client_state = IDLE;
                  client_data_ready =0;
                  syn_ack_timeout=0;
                  return;
            }      
    }

Is there something I need to do to my router?

Hy Barry,

I draw same conclusion and I succeed in building a solution to reach an external server but I still have problems (after severals seconds or minutes it doesn't work anymore.... but I have exactly same problem with Original sketch with a local server).

To get this solution, I used Wireshark which is a Ethernet Analyser Software. And I use it to see what does my PC (and my browser) when it try connect to save.php page on this exernal server.

Then I reproduce communications to enable my arduino to connect to this page. And this works... for several minutes (in the best cases) ...

Skecth sources & modified library : here

I modified into library :

Add arp_packet_is_requested function to reply to ARP request into ip_arp_udp_tcp.c

uint8_t arp_packet_is_requested ( uint8_t *buf )
{
       uint8_t i;

       // if packet type is not arp packet exit from function
       if( buf[ ETH_TYPE_H_P ] != ETHTYPE_ARP_H_V || buf[ ETH_TYPE_L_P ] != ETHTYPE_ARP_L_V)
               return 0;
       // check arp request opcode
       if ( buf[ ARP_OPCODE_H_P ] != ARP_OPCODE_REQUEST_H_V || buf[ ARP_OPCODE_L_P ] != ARP_OPCODE_REQUEST_L_V )
               return 0;
       // if destination ip address in arp packet not match with avr ip address
        for(i=0; i<4; i++){
            if(buf[ARP_DST_IP_P+i] != ipaddr[i]){
                return 0;
            }
        }
       return 1;
}

Add arp_packet_is_requested wrapper function into etherShield.cpp

You don't want to send out an ARP request for a remote IP actually, instead Have the sketch sent out an ARP request for the gateway and send packet there.

mac address of the router, but IP address of wherever its going.

pseudoblogeus,

Thanks. I was able to do exactly what I wanted with a tiny modification to the script you linked to.

KyleK :

You don't want to send out an ARP request for a remote IP actually, instead Have the sketch sent out an ARP request for the gateway and send packet there.

mac address of the router, but IP address of wherever its going.

I'm not sure to have undertsand what you wrote (my English is not as perfect as I would) but what you describe is exactly what I wanted to do (i.e. reply to ARP request send by my Gateway. So I don't see why I should send an ARP reply to my external server since this ARP request will never come from this server (from the point of view of my ARDUINO) but from my gateway).

barrysfarm : What are modifications you have done ? Does it works during a long period (1 hour and more ?) ?

Instead of sending every 10 seconds, it initiates the connection when a button is pushed. I had it running for about 12 hours yesterday, but I turned it off around when i went to bed because the arduino was getting hot. (I'm running it off a wall wart, and it looks like that's common),

I hit the button about 50 times over the course of the day and it worked every time, however when i unplugged the router (accidently) the IP address reset and the shield couldn't connect.

Well, it's quite good news.
I'm happy to see it has help you.

Here's what I have been doing. I have a small web server on my LAN that acts as a relay. It runs the following when a request comes in:

<?php
function getResource($url){
   $chandle = curl_init();
   curl_setopt($chandle, CURLOPT_URL, $url);
   curl_setopt($chandle, CURLOPT_RETURNTRANSFER, 1);
   $result = curl_exec($chandle);
   curl_close($chandle);
 return $result;
 }




$url = $_GET['url'];

echo getResource($url);

?>

It is a little annoying that you need an extra server, but it works.

I'm not sure to have undertsand what you wrote (my English is not as perfect as I would) but what you describe is exactly what I wanted to do (i.e. reply to ARP request send by my Gateway. So I don't see why I should send an ARP reply to my external server since this ARP request will never come from this server (from the point of view of my ARDUINO) but from my gateway).

barrysfarm : What are modifications you have done ? Does it works during a long period (1 hour and more ?) ?

Thanks for the change in the code. It now works also for me.
But unfortunately also for max. 1 hour or so. Then it stops and the save.php doens't update anymore...

Very strange!! Why is it working not the whole time?

I'm am using the Nuelectronics Ethernetshield.

Is there someone that has this 100% working?

Thanks in advance,

Atmoz

When I look at the serial console I get this:

** IDLE **
Send ACK to SYNACK **
Envoie les donnes au serveur
Send ACk **
Send FinACk **
Send ACk **
Send FinACk **
Send ACk **
** IDLE **
Send ACK to SYNACK **
Envoie les donnes au serveur
Send ACk **
Send FinACk **
Send ACk **
Send FinACk **
Send ACk **
** IDLE **
Send ACK to SYNACK **
Envoie les donnes au serveur
Send ACk **
Send FinACk **
Send ACk **
Send FinACk **
Send ACk **
** IDLE **
Send ACK to SYNACK **
Envoie les donnes au serveur
Send ACk **
Send FinACk **
Arp Reply ** <------- here "save.php" on the server doesn't get updated anymore
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **
Arp Reply **

The code that I'm using is this one:

#include "etherShield.h"


#define TEMP_PIN  14      // DS18B20 de data pin pullup met 4k7 Ohm
 
// ARDUINO
// Addresse MAC de l'arduino
static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x24}; 
// Adresse IP de l'arduino
static uint8_t myip[4] = {192,168,1,86};
static char client_ip[] = "192.168.1.86";
// PORT Ethernet Client
static uint16_t my_port = 1200;
 
// SERVEUR
// Adresse IP du serveur 
static uint8_t dest_ip[4]={88,88,199,105}; // <-- Put IP of your Server
// Adresse MAC du serveur ou du routeur si le serveur n'est pas dans le même sous réseau (exemple, un serveur sur internet)
static uint8_t dest_mac[6]={0x00,0x1d,0x68,0xf5,0x60,0xd6}; // <-- Put MAC Adresse of your personnal router
 
// Machine a état da la partie cliente
enum CLIENT_STATE {IDLE, ARP_SENT, ARP_REPLY, SYNC_SENT};
 
static CLIENT_STATE client_state;
 
static uint8_t client_data_ready;
 
static uint8_t syn_ack_timeout = 0;
 
static uint16_t watchdog = 0;
 
 void OneWireReset(int Pin);
void OneWireOutByte(int Pin, byte d);
byte OneWireInByte(int Pin);


int HighByte, LowByte, TReading, SignBit, Tc_100, whole, sign, fract;


void OneWireReset(int Pin) // reset.  Should improve to act as a presence pulse
{
    digitalWrite(Pin, LOW);
    pinMode(Pin, OUTPUT); // bring low for 500 us
    delayMicroseconds(500);
    pinMode(Pin, INPUT);
    delayMicroseconds(500);
}


void OneWireOutByte(int Pin, byte d) // output byte d (least sig bit first).
{
  byte n;

  for(n=8; n!=0; n--)
  {
     if ((d & 0x01) == 1)  // test least sig bit
     {
        digitalWrite(Pin, LOW);
        pinMode(Pin, OUTPUT);
        delayMicroseconds(5);
        pinMode(Pin, INPUT);
        delayMicroseconds(60);
     }
     else
     {
        digitalWrite(Pin, LOW);
        pinMode(Pin, OUTPUT);
        delayMicroseconds(60);
        pinMode(Pin, INPUT);
     }

     d=d>>1; // now the next bit is in the least sig bit position.
  }
}

byte OneWireInByte(int Pin) // read byte, least sig byte first
{
   byte d, n, b;

   for (n=0; n<8; n++)
   {
       digitalWrite(Pin, LOW);
       pinMode(Pin, OUTPUT);
       delayMicroseconds(5);
       pinMode(Pin, INPUT);
       delayMicroseconds(5);
       b = digitalRead(Pin);
       delayMicroseconds(50);
       d = (d >> 1) | (b<<7); // shift d to right and insert b in most sig bit position
   }
   return(d);
}


void ReadTemps(){

 OneWireReset(TEMP_PIN);
 OneWireOutByte(TEMP_PIN, 0xcc);
 OneWireOutByte(TEMP_PIN, 0x44); // perform temperature conversion, strong pullup for one sec

 OneWireReset(TEMP_PIN);
 OneWireOutByte(TEMP_PIN, 0xcc);
 OneWireOutByte(TEMP_PIN, 0xbe);

 LowByte = OneWireInByte(TEMP_PIN);
 HighByte = OneWireInByte(TEMP_PIN);
 TReading = (HighByte << 8) + LowByte;
 SignBit = TReading & 0x8000;  // test most sig bit
 if (SignBit) // negative
 {
   TReading = (TReading ^ 0xffff) + 1; // 2's comp
 }
 Tc_100 = (6 * TReading) + TReading / 4;                       // multiply by (100 * 0.0625) or 6.25

 whole = Tc_100 / 100;                                         // separate off the whole and fractional portions
 
}


void getCurrentTemp(char *temp)
{  
 // int HighByte, LowByte, TReading, Tc_100, sign, whole, fract;

  OneWireReset(TEMP_PIN);
  OneWireOutByte(TEMP_PIN, 0xcc);
  OneWireOutByte(TEMP_PIN, 0x44); // perform temperature conversion, strong pullup for one sec

  OneWireReset(TEMP_PIN);
  OneWireOutByte(TEMP_PIN, 0xcc);
  OneWireOutByte(TEMP_PIN, 0xbe);

  LowByte = OneWireInByte(TEMP_PIN);
  HighByte = OneWireInByte(TEMP_PIN);
  TReading = (HighByte << 8) + LowByte;
  sign = TReading & 0x8000;  // test most sig bit
  if (sign) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  whole = Tc_100 / 100;  // separate off the whole and fractional portions
  fract = Tc_100 % 100;

      if(sign) temp[0]='-';
      else              temp[0]='+';
      
      
      temp[1]= (whole-(whole/100)*100)/10 +'0' ;
      temp[2]= whole-(whole/10)*10 +'0';
      
      temp[3]='.';
      temp[4]=fract/10 +'0';
      temp[5]=fract-(fract/10)*10 +'0';
      
      temp[6] = '\0';
}      




 
 
 
#define BUFFER_SIZE 500
static uint8_t buf[BUFFER_SIZE+1];
 
char sensorData[10];
 
EtherShield es=EtherShield();
 
// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage(uint8_t *buf);
int8_t analyse_cmd(char *str);
 
 
void client_process(void);
 
// ********** SETUP ********** //
void setup(){
      // Initialise la liaison série pour le Terminal     
      Serial.begin(9600);
      Serial.println("~~~ Debut SETUP ~~~");
 
 
 
      // Initialise le enc28j60 (Contrôleur Ethernet)
      es.ES_enc28j60Init(mymac);
      es.ES_enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
      delay(10);
 
      /* Magjack leds configuration, see enc28j60 datasheet, page 11 */
      // LEDA=greed LEDB=yellow
      //
      // 0x880 is PHLCON LEDB=on, LEDA=on
      // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x880);
      delay(500);
      //
      // 0x990 is PHLCON LEDB=off, LEDA=off
      // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x990);
      delay(500);
      //
      // 0x880 is PHLCON LEDB=on, LEDA=on
      // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x880);
      delay(500);
      //
      // 0x990 is PHLCON LEDB=off, LEDA=off
      // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x990);
      delay(500);
      //
      // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
      // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
      es.ES_enc28j60PhyWrite(PHLCON,0x476);
      delay(100);
 
      //init the ethernet/ip layer:
      es.ES_init_ip_arp_udp_tcp(mymac,myip,80);
 
      // intialize variable;
      syn_ack_timeout =0;
      client_data_ready = 0;
      client_state = IDLE;
 
      Serial.println("~~~ Fin SETUP ~~~");
}
 
// ********** MAIN LOOP ********** //
void loop()
{
      if(client_data_ready==0)
      {
            // Lecture de la T° toute les 10 secondes
 
            delay(1000UL); // 10 sec.
            client_data_ready = 1; // Indique que le client est prêt a transmettre ses données
      }

        getCurrentTemp(sensorData);
      client_process();
        
 
}
 
// ********** Generation de la Req. Client ********** //
uint16_t gen_client_request(uint8_t *buf )
{
      Serial.println("Envoie les donnes au serveur");
      uint16_t plen;
      byte i;
      plen= es.ES_fill_tcp_data_p(buf,0, PSTR ( "GET /ethershield_log/save.php?pwd=secret&client=" ) );
      for(i=0; client_ip[i]!='\0'; i++)
      {
            buf[TCP_DATA_P+plen]=client_ip[i];
            plen++;
      }
      plen= es.ES_fill_tcp_data_p(buf,plen, PSTR ( "&status=" ) );
        for(i=0; sensorData[i]!='\0'; i++){

                buf[TCP_DATA_P+plen]=sensorData[i];
                plen++;
        }      





      plen= es.ES_fill_tcp_data_p(buf, plen, PSTR ( " HTTP/1.0\r\n" ));
      plen= es.ES_fill_tcp_data_p(buf, plen, PSTR ( "Host: www.luma.nl\r\n" )); //<-- Put your web site adresse
      plen= es.ES_fill_tcp_data_p(buf, plen, PSTR ( "User-Agent: AVR ethernet\r\n" ));
      plen= es.ES_fill_tcp_data_p(buf, plen, PSTR ( "Accept: text/html\r\n" ));
      plen= es.ES_fill_tcp_data_p(buf, plen, PSTR ( "Keep-Alive: 300\r\n" ));
      plen= es.ES_fill_tcp_data_p(buf, plen, PSTR ( "Connection: keep-alive\r\n\r\n" ));
 
      return plen;
}
 
//*****************************************************************************************
//
// Function : client_process
// Description : send temparature to web server, this option is disabled by default.
// YOU MUST install webserver and server script before enable this option,
// I recommented Apache webserver and PHP script.
// More detail about Apache and PHP installation please visit http://www.avrportal.com/
//
//*****************************************************************************************
// ********** Gere la communication Ethernet du Client ********** //

further in next post...

void client_process ( void )
{
      uint16_t plen;
      uint8_t i;
 
      // Si le client n'est pas prêt on ne fait rien et on sort de la fonction
      if (client_data_ready == 0)  return; 
 
      if(client_state == IDLE)
      {
            Serial.println("** IDLE **");
            // demande d'établissement de connexion
            es.ES_tcp_client_send_packet (
            buf,
            80,
            1200,
            TCP_FLAG_SYN_V, // flag
            1,              // (bool)maximum segment size
            0,              // (bool)clear sequence ack number
            0,              // 0=use old seq, seqack : 1=new seq,seqack no data : new seq,seqack with data
            0,              // tcp data length
            dest_mac,
            dest_ip
            );
 
            client_state = SYNC_SENT;
            return;
      }
 
 
      if(client_state == SYNC_SENT)
      {
            //Serial.println("** SYNC_SENT **");
            plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);
 
            // no new packet incoming
            if ( plen == 0 )
            {
                  delay(1UL); // 1ms
                  watchdog++;
                  if (watchdog==5000)
                  {
 
                  client_data_ready =0;
 
                        watchdog = 0;
                        client_state = IDLE;
                        Serial.println("Watchdog !!!!");
                  }
                  return;
            }
            else
            {
                  watchdog = 0;
            }
 
            // check ip packet send to avr or not?
            if (es.ES_arp_packet_is_requested(buf)==1) // This is ARP request for AVR
            {
                  es.ES_make_arp_answer_from_request(buf); // Reply AVR
                  Serial.println("Arp Reply **");
            }
            else
            {
                  if ( es.ES_eth_type_is_ip_and_my_ip(buf,plen)==0)
                  {
                        return;
                  }
            }
 
            // check SYNACK flag, after AVR send SYN server response by send SYNACK to AVR
            if ( buf [ TCP_FLAGS_P ] == ( TCP_FLAG_SYN_V | TCP_FLAG_ACK_V ) )
            {
                  Serial.println("Send ACK to SYNACK **");
                  // send ACK to answer SYNACK
                  es.ES_tcp_client_send_packet (
                  buf,
                  80,
                  1200,
                  TCP_FLAG_ACK_V,                 // flag
                  0,                                              // (bool)maximum segment size
                  0,                                              // (bool)clear sequence ack number
                  1,                                              // 0=use old seq, seqack : 1=new seq,seqack no data : new seq,seqack with data
                  0,                                              // tcp data length
                  dest_mac,
                  dest_ip
                  );
 
                  // setup http request to server
                  plen = gen_client_request( buf );
 
                  // send http request packet
                  // send packet with PSHACK (PUSH ACK : Force transmission of Data)
                  es.ES_tcp_client_send_packet (
                  buf,
                  80,                              // destination port
                  1200,                            // source port
                  TCP_FLAG_ACK_V | TCP_FLAG_PUSH_V,// flag
                  0,                               // (bool)maximum segment size
                  0,                               // (bool)clear sequence ack number
                  0,                               // 0=use old seq, seqack : 1=new seq,seqack no data : >1 new seq,seqack with data
                  plen,                            // tcp data length
                  dest_mac,
                  dest_ip
                  );
                  return;
            }
 
            // after AVR send http request to server, server response by send data with PSHACK to AVR
            // AVR answer by send ACK and FINACK to server
            if ( buf [ TCP_FLAGS_P ] == (TCP_FLAG_ACK_V|TCP_FLAG_PUSH_V) )
            {
                  Serial.println("Send ACk **");
                  plen = es.ES_tcp_get_dlength( (uint8_t*)&buf );
 
                  // send ACK to answer PSHACK from server
                  es.ES_tcp_client_send_packet (
                  buf,
                  80,                                             // destination port
                  1200,                                   // source port
                  TCP_FLAG_ACK_V,                  // flag
                  0,                                              // (bool)maximum segment size
                  0,                                              // (bool)clear sequence ack number
                  plen,                                           // 0=use old seq, seqack : 1=new seq,seqack no data : >1 new seq,seqack with data
                  0,                              // tcp data length
                  dest_mac,
                  dest_ip
                  );;
 
 
                  // send finack to disconnect from web server
                  Serial.println("Send FinACk **");
                  es.ES_tcp_client_send_packet (
                  buf,
                  80,                                             // destination port
                  1200,                                   // source port
                  TCP_FLAG_FIN_V|TCP_FLAG_ACK_V,                  // flag
                  0,                                              // (bool)maximum segment size
                  0,                                              // (bool)clear sequence ack number
                  0,                                           // 0=use old seq, seqack : 1=new seq,seqack no data : >1 new seq,seqack with data
                  0,
                  dest_mac,
                  dest_ip
                  );
 
                  return;
            }
 
            // answer FINACK from web server by send ACK to web server
            if ( buf [ TCP_FLAGS_P ] == (TCP_FLAG_ACK_V|TCP_FLAG_FIN_V) )
            {
                  Serial.println("Send ACk **");
                  // send ACK with seqack = 1
                  es.ES_tcp_client_send_packet(
 
                  buf,
                  80,                                             // destination port
                  1200,                                   // source port
                  TCP_FLAG_ACK_V,                 // flag
                  0,                                              // (bool)maximum segment size
                  0,                                              // (bool)clear sequence ack number
                  1,                                              // 0=use old seq, seqack : 1=new seq,seqack no data : >1 new seq,seqack with data
                  0,
                  dest_mac,
                  dest_ip
                  );
                  client_state = IDLE;            // return to IDLE state
                  client_data_ready =0;            // client data sent
            }
      }       
}