Ethernet Shield Unreliable?

So I got my Ethernet shield up and running, but it seems very unreliable. I have tried to connect to both google and a local server with the same results.

Here is my code for connecting:

boolean httpRequest(String request, void (*_requestCompleteFunction)(byte), long retryConnectTime, boolean async)
{
  byte connection = -1;
  
  // find unused connection
  for (byte i = 0; i < MAX_NUM_CONNECTIONS; i++)
  {
    if (!requestPending[i])
    {
      connection = i;
      break;
    }
  }
  
  if (connection == -1)
  {
    logError("Max number of connections exceeded");
    return false;
  }
  
  Serial.println("Connecting on " + String(connection) + "...");

  // stop previous connection
  client[connection].stop();
  
  boolean cncted = true;
  long startTime = millis();
  int count = 0;
  
  // always attempt at least twice and continue attempting for 5 seconds
  while (count < 2 || millis() - startTime < retryConnectTime)
  {
    cncted = client[connection].connect(server, 80);
    count ++;
    
    if (cncted)
      break;
  }
  
  if (cncted)
  {
    Serial.println("Connecetd!");

    client[connection].println("GET " + request);
    client[connection].println();
    
    requestResponse[connection] = "";
    requestCompleteFunction[connection] = _requestCompleteFunction;
    requestPending[connection] = true;
    requestResponseStarted[connection] = false;

    return true;
  }
  else
  {
    logError("Failed to connect to server " + String(server[0]) + '.' + String(server[1]) + '.' + String(server[2]) + '.' + String(server[3]));
    return false;
  }
}

Here is the code for reading responses:

void loop()
{
  for (byte i = 0; i < MAX_NUM_CONNECTIONS; i++)
  {
    if (requestPending[i])
    {
      if (client[i].connected())
      {
        if (client[i].available())
        {
          char c = char(client[i].read());
          
          requestResponseStarted[i] = true;
          requestResponse[i] += c;
          
          Serial.print(c);
        }
      }
      else
      {
        if (requestResponseStarted[i])
        {
          (*requestCompleteFunction[i])(i);
          
          requestPending[i] = false;
          client[i].stop();
        }
      }
    }
  }
}

Here are my symptoms:

  • Ethernet.begin(mac, ip); Ethernet.localIP(); sometimes gives me 0.0.0.0

  • client.connect(server, 80); Does one of the following:

  • Connects immediately

  • The first attempt "times out" after 5 seconds. Next attempts connect immediately or fail

  • The first few attempts will fail before one succeeds

  • After connecting, client.connected() always returns true, but the response will:

  • Return immediately

  • Never return

  • Take a very long time to return

  • Only part of it will return

Am I doing something wrong or is that just the nature of the thing?
Thanks,

  • Mike

WebClientTest.ino (4.76 KB)

Post your code in a code box. What type of ethernet shield do you have (there are now several different ones), and what board are you using?

Three things I have found:

  1. If you have the ethernet shield that also has the SD, you need to disable the SD or it can interfere with the ethernet. You do this by putting these lines in your setup routine:
    pinMode(4,OUTPUT);
    digitalWrite(4,HIGH);

  2. The ethernet card can hang if you overwhelm it with writes. If you use Ethernet::print() it can sometimes trigger one write operation per byte. Using Ethernet::write() avoids this, causing one write for each write statement.

  3. The LED pin (13) on an UNO is also used by the ethernet card and must not be used.

zoomkat:
Post your code in a code box. What type of ethernet shield do you have (there are now several different ones), and what board are you using?

I have this "Arduino Ethernet shield with micro SD connector" from Adafruit.

I attached my code to the first post but here it is again:

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

byte mac[] = {0x90, 0xA2, 0xDA, 0x00, 0x78, 0xE3};
byte ip[] = {192,168,1,27};
byte subnet[] = {255,255,255,0};
byte gateway[] = {192,168,1,1};

byte server[] = {74,125,227,80};//{192,168,1,11};

#define MAX_NUM_CONNECTIONS 5

EthernetClient client[MAX_NUM_CONNECTIONS];
boolean requestPending[MAX_NUM_CONNECTIONS];
String requestResponse[MAX_NUM_CONNECTIONS];
boolean requestResponseStarted[MAX_NUM_CONNECTIONS];
void (*requestCompleteFunction[MAX_NUM_CONNECTIONS])(byte);



boolean httpRequest(String request, void (*_requestCompleteFunction)(byte), long retryConnectTime, boolean async)
{
  byte connection = -1;
  
  // find unused connection
  for (byte i = 0; i < MAX_NUM_CONNECTIONS; i++)
  {
    if (!requestPending[i])
    {
      connection = i;
      break;
    }
  }
  
  if (connection == -1)
  {
    logError("Max number of connections exceeded");
    return false;
  }
  
  Serial.println("Connecting on " + String(connection) + "...");
  
  // stop previous connection
  client[connection].stop();
  
  boolean cncted = true;
  long startedTime = millis();
  int count = 0;
  
  while (count < 2 || millis() - startedTime < retryConnectTime)
  {
    cncted = client[connection].connect(server, 80);
    count ++;
    
    if (cncted)
      break;
  }
  
  if (cncted)
  {
    Serial.println("Connecetd!");
    
    client[connection].println("GET " + request);
    client[connection].println();
    
    requestResponse[connection] = "";
    requestCompleteFunction[connection] = _requestCompleteFunction;
    requestPending[connection] = true;
    requestResponseStarted[connection] = false;
    
    if (!async)
    {
      while (client[connection].connected())
      {
        if (client[connection].available())
        {
          requestResponse[connection] += char(client[connection].read());
          delay(1);
        }
      }
      
      client[connection].stop();
      (*requestCompleteFunction[connection])(connection);
    }
    
    return true;
  }
  else
  {
    logError("Failed to connect to server " + String(server[0]) + '.' + String(server[1]) + '.' + String(server[2]) + '.' + String(server[3]));
    return false;
  }
}



void setup()
{
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  
  Serial.begin(9600);
  
  Serial.println("Initializing Ethernet...");
  Ethernet.begin(mac, ip);
  Serial.println("Ethernet initialized!");
  Serial.println(Ethernet.localIP());
  
  httpRequest("/api/getBeers.php?s=kegerator111111yo1dog", &getBeersRequestReturn, 5000, true);
  //httpRequest("/api/getBeers.php?s=kegerator111111yo1dog", &getBeersRequestReturn, 5000, true);
  //httpRequest("/api/getBeers.php?s=kegerator111111yo1dog", &getBeersRequestReturn, 5000, true);
}

void loop()
{
  for (byte i = 0; i < MAX_NUM_CONNECTIONS; i++)
  {
    if (requestPending[i])
    {
      if (client[i].connected())
      {
        if (client[i].available())
        {
          char c = char(client[i].read());
          
          requestResponseStarted[i] = true;
          requestResponse[i] += c;
          
          Serial.print(c);
        }
      }
      else
      {
        if (requestResponseStarted[i])
        {
          (*requestCompleteFunction[i])(i);
          
          requestPending[i] = false;
          client[i].stop();
        }
      }
    }
  }
}


void getBeersRequestReturn(byte connection)
{
  Serial.println(String(connection) + ": " + requestResponse[connection]);
  /*boolean invalid = true;
  
  if (requestResponse[connection][0] == '1')
  {
    invalid = false;
    
    int index1 = 1;
    int index2 = 1;
    while ((index2 = requestResponse[connection].indexOf(',', index1)) > -1)
    {
      String id = requestResponse[connection].substring(index1, index2);

      index1 = index2 + 1;
      index2 = requestResponse[connection].indexOf(',', index1);
      
      if (index2 == -1)
      {
        invalid = true;
        break;
      }
      
      String brewery = requestResponse[connection].substring(index1, index2);
      
      index1 = index2 + 1;
      index2 = requestResponse[connection].indexOf(',', index1);
      
      if (index2 == -1)
      {
        invalid = true;
        break;
      }
      
      String beer = requestResponse[connection].substring(index1, index2);
      
      index1 = index2 + 1;
      
      Serial.println(id + ": " + brewery + " - " + beer);
    }
  }
  
  if (invalid)
    logError("Invalid getBeers request response: " +  requestResponse[connection]);*/
}

void newPourRequestReturn(byte connection)
{
  if (requestResponse[connection][0] != '1')
    logError("Invalid getBeers request response: " +  requestResponse[connection]);
}

void logError(String error)
{
  String str = /*String(day()) + '/' + String(month()) + ' ' + String(hour()) + ':' + String(minute()) + ';' + String(second()) + ": " +*/ error;
  Serial.println("ERROR: " + str);
}

DCContrarian:
Three things I have found:

  1. If you have the ethernet shield that also has the SD, you need to disable the SD or it can interfere with the ethernet. You do this by putting these lines in your setup routine:
    pinMode(4,OUTPUT);
    digitalWrite(4,HIGH);

  2. The ethernet card can hang if you overwhelm it with writes. If you use Ethernet::print() it can sometimes trigger one write operation per byte. Using Ethernet::write() avoids this, causing one write for each write statement.

  3. The LED pin (13) on an UNO is also used by the ethernet card and must not be used.

  1. I have done so, but it does not seem to have any effect.

  2. Write only accepts a single byte or character. How am I supposed to use this function? Can you give me an example?

  3. I am not using any pins on the UNO.

Another thing I noticed; It seems to work great the first couple of times after I power up the board (works, hit reset, works, hit reset, works...) But after the 3rd or so time it starts having problems. The chip gets pretty hot, is it a heat issue? This might also be coincidental.

#define MAX_NUM_CONNECTIONS 5

The Wiznet chip on that shield only supports 4 simultaneous connections.

Woops, that was supposed to be a four. Either way, These problems occur with only a single connection.

Do you have the same issues if the Arduino is acting as a server? Mine never has any issues running as a server, and when it ran as a client, it never had any issues connecting to a server on my local network.

What happens if you change the #define to 1?

That won't make a difference. My test only makes one request and only uses one connection which I confirmed.

Final test suggestion. Change the call to httpRequest() to make the connection synchronous, instead. Does it make a difference?

Also, if you could tell us anything about your network and server, that might be interesting.

FWI I wrote all the code you see.

Most of the time the problems occur while attempting to connect to the server and async does not matter at that point.

Below is some client test code you can try to see if your ethernet shield still hangs after a couple of request.

//zoomkat 12-08-11
//simple client test
//for use with IDE 1.0
//open serial monitor and send an e to test
//for use with W5100 based ethernet shields

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan assigned to arduino
//byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
//byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
byte myserver[] = { 208, 104, 2, 86 }; // zoomkat web page server IP address
EthernetClient client;
//////////////////////

void setup(){

  Ethernet.begin(mac, ip);
  //Ethernet.begin(mac, ip, subnet, gateway);
  Serial.begin(9600); 
  Serial.println("Better client test 12/01/11"); // so I can keep track of what is loaded
  Serial.println("Send an e in serial monitor to test"); // what to do to test
}

void loop(){
  // check for serial input
  if (Serial.available() > 0) //if something in serial buffer
  {
    byte inChar; // sets inChar as a byte
    inChar = Serial.read(); //gets byte from buffer
    if(inChar == 'e') // checks to see byte is an e
    {
      sendGET(); // call sendGET function below when byte is an e
    }
  }  
} 

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

void sendGET() //client function to send/receive GET request data.
{
  if (client.connect(myserver, 80)) {  //starts client connection, checks for connection
    Serial.println("connected");
    client.println("GET /~shb/arduino.txt HTTP/1.0"); //download text
    client.println(); //end of get request
  } 
  else {
    Serial.println("connection failed"); //error message if no client connect
    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(); //gets byte from ethernet buffer
    Serial.print(c); //prints byte to serial monitor 
  }

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

}

This is problematic:

client[connection].println("GET " + request);
    client[connection].println();

It's practically a bug in the ethernet library, but client.print and println shouldn't be used with strings, it causes the strings to be sent out one byte at a time which can overflow the send buffer in the ethernet shield. You need to use client.write(), which means you need to use something other than a string.

Can you give me an example on how to use client.write()?

How would I make the following request using that function:
GET /api/getBeers.php?s=kegerator111111yo1dog

Edit:
While I am sure this is one of the problems, my main one is that half the time it cannot connect to my local server or google. I ping both servers with my desktop (same network) everytime and the desktop can always connect. The first attempt stalls for about 10 seconds or so then fails along with the second attempt.

client.write("GET /api/getBeers.php?s=kegerator111111yo1dog HTTP/1.0\r\n\r\n");

GetBeers? Kegerator? What a good idea!! :slight_smile:

Edit: Those "/r/n" should have been backslashes, not a forward slash. My bad. Guess it was the beer...

If you want to use variables, I use sprintf into a character array.

char outBuf[128];
String kegNumber = "kegerator111111yo1dog";

sprintf(outBuf,"GET /api/getBeers.php?s=%s HTTP/1.0\r\n\r\n",kegNumber);
client.write(outBuf);

client.write only accepts a byte or char.

yo1dog:
client.write only accepts a byte or char.

Just a minor detail. V0022 accepts a zero-terminated string, V1.0 does not.

char outBuf[128];
String kegNumber = "kegerator111111yo1dog";

sprintf(outBuf,"GET /api/getBeers.php?s=%s HTTP/1.0\r\n\r\n",kegNumber);
client.write((uint8_t*)outBuf, strlen(outBuf));

0022:

  virtual void write(uint8_t);
  virtual void write(const char *str);
  virtual void write(const uint8_t *buf, size_t size);

1.0:

  virtual size_t write(uint8_t);
  virtual size_t write(const uint8_t *buf, size_t size);

So, with either version, you can also call write() with an array of chars/bytes.

Did not see that it accepted a pointer and size. Thanks. Why is this not in the documentation? Guess I need to start looking at the source.

Why is this not in the documentation?

Because writing code is way more fun than writing documentation.

A very true statement indeed.