Ethernet & SD - SD working once.

Hello,

I am pretty new to Arduino, I am trying to set up a simple web server reading its contents from an sd card.

The first time I connect through a browser everything works as expected, the sd file is printed and the client disconnects. The second time I try to load the page, the SD fails to read the file, however the page loads returning the error message.

Below is my code, I would appreciate it if somebody could point out what the problem is.

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

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,2,177);

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(80);

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
  
  pinMode(10, OUTPUT);
}


void loop() {
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");

    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {            
        char c = client.read();

        if (c == '\n' && currentLineIsBlank) {

          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connnection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
  
          if (!SD.begin(4)) {
            Serial.println("Card failed, or not present");
            client.println("Card Fail");
          } else {
              
            File dataFile = SD.open("Wake.txt");

            if (dataFile) {
              while (dataFile.available()) {
                client.write(dataFile.read());
              }
            dataFile.close();
            }
              
          }
          
          client.println("</html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
    Serial.println("client disonnected");
  }
}

I am using an Arduino Uno with the Ethernet Shield and a 1GB Mico SD card.

          if (!SD.begin(4)) {
            Serial.println("Card failed, or not present");
            client.println("Card Fail");
          }

This part belongs in setup(), not loop().

The second time I try to load the page, the SD fails to read the file, however the page loads returning the error message.

What error message would that be?

Thanks for the quick reply.

No actual error message, what I meant was the "Card Failed, or not present."

Edit:

After moving the if statement to setup() it is working fine on refresh, could you explain to me why?

SD::begin() looks like this:

boolean SDClass::begin(uint8_t csPin) {
  /*

    Performs the initialisation required by the sdfatlib library.

    Return true if initialization succeeds, false otherwise.

   */
  return card.init(SPI_HALF_SPEED, csPin) &&
         volume.init(card) &&
         root.openRoot(volume);
}

So, I presume that initializing the card more than once, or initializing the volume more than once, or opening the root more than once (especially as you don't close it), or more than one of the operations fails.

Moving the call to setup() means that you don't do any of those things more than once.

Brilliant thanks for the help, and for explaining it.

You may find the some reset actions will cause either the w5100 or the sd card to fail the begin() function unless you disable one while setting up the other. I use something like this:

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

   // disable w5100 SPI while setting up SD
   pinMode(10,OUTPUT);
   digitalWrite(10,HIGH);

   Serial.print("Starting SD...");
   if(!SD.begin(4)) Serial.println("failed");
   else Serial.println("ok");
   // SD.begin() returns with its SPI disabled. Good SD library!

   Serial.print("Starting w5100...");

   if(!Ethernet.begin(mac)) Serial.println("failed");
   else Serial.println("ok");

   // Ethernet.begin() returns with the SPI enabled (bug?), so disable it. Bad Ethernet library!
   digitalWrite(10,HIGH);

   // rest of your setup
}

I think (but am not at all sure) that I am getting a similar problem... here is my code, with the results from some tests as comments near the front

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
EthernetServer* server;
typedef unsigned char byte;
typedef unsigned char* addr;
#define BZ 250
byte buffer[BZ+1];
/*

on reload...
starting ethernet
bytes loaded 0
starting server
setup done

on reset...
starting ethernet
bytes loaded 0
starting server
setup done

on reload...
starting ethernet
bytes loaded 24
starting server
setup done
client started
GET / HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,#/#;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cache-Control: max-age=0
bytes xmit 12481

on reset...
starting ethernet
bytes loaded 24
starting server
setup done

*/

short load(byte id) {
  char fn[] = "@.BIN";
  fn[0] = '@' + id;
  File istream = SD.open(fn,FILE_READ);
  short sz = BZ;
  if(istream) {
    addr to = &buffer[0];
    memset(to,0,sz);
    short d = istream.read();
    while(sz && d != -1) {
      *to = d;
      ++to;
      --sz;
      d = istream.read();
    }
    istream.close();
  }
  return BZ - sz;
}

short xmit(EthernetClient ostream, byte fid) {
  char fname[8] = "*.TXT";
  fname[0] = fid;
  File istream = SD.open(fname,FILE_READ);
  short sz = 0;
  if(istream) {
    byte data = istream.read();
    while(data != 0xFF) {
      ++sz;
      if(ostream)
        ostream.print((char)data);
      data = istream.read();
    }
    istream.close();
  }
  return sz;
}

void setup() {
  Serial.begin(9600);
  unsigned char mac[] = // hidden for security reasons
  unsigned char addr[] = // hidden for security reasons
  unsigned char gate[] = // hidden for security reasons
  unsigned char dnsa[] = // hidden for security reasons
  unsigned char dnsb[] = // hidden for security reasons
  unsigned char mask[] = {255,255,255,  0};
  pinMode(11,INPUT);
  pinMode(12,INPUT);
  pinMode(13,INPUT);
  SD.begin(4); // to use onboard SD card reader
  Serial.println("starting ethernet");
  Ethernet.begin(mac,addr,dnsa,gate,mask);
  delay(1000);
  Serial.print("bytes loaded ");
  Serial.println(load(1));
  Serial.println("starting server");
  server = new EthernetServer(80);
  server->begin();
  Serial.println("setup done");
}
void loop() {
  EthernetClient client = server->available();
  if(client) {
    Serial.println("client started");
    while(client.available()) {
      char c = client.read();
      Serial.print(c);
    }
    short sz = 0;
    sz += xmit(client,'H');
    sz += xmit(client,'1');
    sz += xmit(client,'C');
    sz += xmit(client,'2');
    sz += xmit(client,'J');
    sz += xmit(client,'3');
    char* t = "elves[CLOCK].C = new Date();";
    client.println(t); sz += strlen(t);
    t = "sPage();";
    client.println(t); sz += strlen(t);
    sz += xmit(client,'4');
    Serial.print("bytes xmit ");
    Serial.println(sz);
    delay(10);
    client.stop();
  }
}

I am using a Mega2560 with the standard(?) Ethernet card with the microSD reader built in and have jumpered:
Mega pin 50 to card pin 12
Mega pin 51 to card pin 11
Mega pin 52 to card pin 13

My symptom is pure intermittent: sometimes it works and sometimes it doesn't, with no determinable set of conditions to indicate which. Given the above comments, I'll try fiddling with pin 10 and see whether the symptom goes away... difficult to distinguish between 'fixed' and 'less frequent' in this sort of case.

I've had a go with this approach and it seems to work in a simple case, but I still have a problem with the actual program I'm writing (rather than a simple exercise to try to identify what is going on). The symptom I get is that shortly after doing an EthernetServer.begin() I get what appears to be a reboot: in the middle of a Serial.println() just after the begin() call, the 'printing' is interrupted and my setup() restarts.
I'm working on producing something simple that consistently reproduces the problem, but in the mean time, can someone more knowledgeable than me advise...

  1. exactly how the ethernet library knows which are MOSI, MISO, SCK and SS ... because it seems to use the system defaults for MOSI, MISO and SCK, but always pin 10 for SS
  2. if it is possible that both the SD library and the Ethernet library can be grabbing MOSI, etc, at the same time 'behind the scenes'... or do they only operate when I specifically call them, in which case I can make sure the other one is disabled
  3. which way round is SS enable/disable: enable=LOW, disable=HIGH ?
  4. why pin 53 (the default SS on the Mega) should be set to OUTPUT even if it isn't connected to anything (mentioned in a different thread) and whether it matters if it is set to HIGH of LOW
  5. how the xmit() function, copying from SD file to Enet client, works at all (and it does, trust me) unless the two libraries are careful about selecting the SPI bus at the byte by byte level
    And, of course, finally, where (if?) there is some comprehensive documentation so I don't have to clutter this thread with dumb questions.

#1 - The board you select on the IDE determines what pins are SPI lines. However, on newer ethernet shields, the digital pins are not used for the ethernet shield/SD interfaces. The ICSP pins are used.

#2 - Only if you did not initialize them correctly as suggested above.

#3 - HIGH = disabled, LOW = enabled

#4 - If the default SS pin is not set to OUTPUT, the SPI becomes a slave rather than master.

#5 - and it does, trust me. The SS pins are handled in the low level library routines if the setup was correct. Here is the code for the w5100 write routine. The setSS enables the SPI, and resetSS disables the SPI. The SD library function operates the same way.

uint8_t W5100Class::write(uint16_t _addr, uint8_t _data)
{
  setSS();  
  SPI.transfer(0xF0);
  SPI.transfer(_addr >> 8);
  SPI.transfer(_addr & 0xFF);
  SPI.transfer(_data);
  resetSS();
  return 1;
}

Just thought I'd add a note. Found the problem. The real cause was a damaged pointer (isn't that always the problem in C), but it was hidden by the buffering in Serial.print(). Found it eventually by putting in some delays to allow the Serial buffer to flush so I could spot where the real problem was. Lesson for me is to make allowances for timing when doing debugging.