Ethernet shield with SD card / no writing, only reading works

Hi

I’m now for days trying to get a shield with W5100 and SD card to work correctly.
I have found an example which allows me to read from the SD card and publish the information from the SD card via a webserver.

/*

  • This sketch uses the microSD card slot on the Arduino Ethernet shield
  • to serve up files over a very minimal browsing interface
  • Some code is from Bill Greiman’s SdFatLib examples,
  • some is from the Arduino Ethernet WebServer example,
  • some is from Limor Fried (Adafruit),
  • some is from “jurs” for German Arduino forum,
  • so its probably under GPL
    */

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

/************ ETHERNET STUFF ************/
byte mac = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip = { 192, 168, 25, 222 };
EthernetServer server(80);

/************ SDCARD STUFF ************/
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

void error_P(const char* str) {
PgmPrint("error: ");
SerialPrintln_P(str);
if (card.errorCode()) {
PgmPrint("SD error: ");
Serial.print(card.errorCode(), HEX);
Serial.print(’,’);
Serial.println(card.errorData(), HEX);
}
while(1);
}

char* strupper( char* s )
// helper function char array to uppercase letters
{
for (char* p = s; *p; ++p)
*p = toupper( *p );
return s;
}

char* strlower( char* s )
// helper function char array to lowercase letters
{
for (char* p = s; *p; ++p)
*p = tolower( *p );
return s;
}

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

PgmPrint("Free RAM: ");
Serial.println(FreeRam());

// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance.
pinMode(10, OUTPUT); // set the SS pin as an output (necessary!)
digitalWrite(10, HIGH); // but turn off the W5100 chip!

if (!card.init(SPI_HALF_SPEED, 4)) error(“card.init failed!”);

// initialize a FAT volume
if (!volume.init(&card)) error(“vol.init failed!”);

PgmPrint(“Volume is FAT”);
Serial.println(volume.fatType(),DEC);
Serial.println();

if (!root.openRoot(&volume)) error(“openRoot failed”);

// list file in root with date and size
PgmPrintln(“Files found in root:”);
root.ls(LS_DATE | LS_SIZE);
Serial.println();

// Recursive list of all directories
PgmPrintln(“Files found in all dirs:”);
root.ls(LS_R);

Serial.println();
PgmPrintln(“Done”);

// Debugging complete, we start the server!
Ethernet.begin(mac, ip);
server.begin();
}

void ListFiles(EthernetClient client, uint8_t flags) {
// This code is just copied from SdFile.cpp in the SDFat library
// and tweaked to print to the client output in html!
dir_t p;

root.rewind();
client.println("

    ");
    while (root.readDir(p) > 0) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;

    // skip deleted entry and entries for . and …
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == ‘.’) continue;

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;

    // print any indent spaces
    client.print("

  • <a href="");
    for (uint8_t i = 0; i < 11; i++) {
    if (p.name == ’ ') continue;

    • if (i == 8) {*
    • client.print(’.’);*
    • }*
      _ client.print((char)p.name*);_
      _
      }_
      _
      client.print("">");*_

    * // print file name with possible blank fill*
    * for (uint8_t i = 0; i < 11; i++) {
    _ if (p.name == ’ ') continue;
    if (i == 8) {
    client.print(’.’);
    }
    client.print((char)p.name);
    }*_

    * client.print("");*

    * if (DIR_IS_SUBDIR(&p)) {
    _ client.print(’/’);
    }
    // print modify date/time if requested*

    * if (flags & LS_DATE) {*
    * root.printFatDate(p.lastWriteDate);
    client.print(’ ‘);
    root.printFatTime(p.lastWriteTime);
    }
    // print size if requested*

    * if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {*
    * client.print(’ ');
    client.print(p.fileSize);
    }
    client.println("

  • ");
    }
    client.println("
");
}
// How big our line buffer should be. 100 is plenty!
#define BUFSIZ 100*

void loop()
{
* char clientline[BUFSIZ];
int index = 0;*_

* EthernetClient client = server.available();*
* if (client) {*
* // an http request ends with a blank line*
* boolean current_line_is_blank = true;*

* // reset the input buffer*
* index = 0;*

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

* // If it isn’t a new line, add the character to the buffer*
* if (c != ‘\n’ && c != ‘\r’) {*
* clientline[index] = c;*
* index++;*
* // are we too big for the buffer? start tossing out data*
* if (index >= BUFSIZ)*
* index = BUFSIZ -1;*

* // continue to read more data!*
* continue;*
* }*

* // got a \n or \r new line, which means the string is done*
* clientline[index] = 0;*

* // Print it out for debugging*
* Serial.println(clientline);*

* // Look for substring such as a request to get the root file*
* if (strstr(clientline, "GET / ") != 0) {*
* // send a standard http response header*
* client.println(“HTTP/1.1 200 OK”);*
* client.println(“Content-Type: text/html”);*
* client.println();*

* // print all the files, use a helper to keep it clean*
* client.println(“

Files:

”);*
* ListFiles(client, LS_SIZE);
_ } else if (strstr(clientline, “GET /”) != 0) {
// this time no space after the /, so a sub-file!*

char *filename;_

* filename = clientline + 5; // look after the “GET /” (5 chars)*
* // a little trick, look for the " HTTP/1.1" string and*
* // turn the first character of the substring into a 0 to clear it out.*
* (strstr(clientline, " HTTP"))[0] = 0;*

* // print the file we want*
* Serial.println(filename);*
* if (! file.open(&root, filename, O_READ)) {
_ client.println(“HTTP/1.1 404 Not Found”);
client.println(“Content-Type: text/html”);
client.println();
client.println(“

File Not Found!

”);
break;
}*_

* Serial.println(“Opened!”);*
* strlower(filename); *
* client.println(“HTTP/1.1 200 OK”);*
* if (strstr(filename,".htm")!=NULL)*
* client.println(“Content-Type: text/html”);*
* else if (strstr(filename,".jpg")!=NULL)*
* client.println(“Content-Type: image/jpg”);*
* else *
* client.println(“Content-Type: text/plain”);*
* client.println();*

* int16_t c;
_ while ((c = file.read()) >= 0) {
// uncomment the serial to debug (slow!)
//Serial.print((char)c);
client.print((char)c);
}
file.close();*_

* } else {*
* // everything else is a 404*
* client.println(“HTTP/1.1 404 Not Found”);*
* client.println(“Content-Type: text/html”);*
* client.println();*
* client.println(“

File Not Found!

”);*
* }*
* break;*
* }*
* }*
* // give the web browser time to receive the data*
* delay(1);*
* client.stop();*
* }*
}
[/quote]
however when I try to write to a logfile, it doesn’t work.
> file logfile
> logfile = SD.open(“datalogger2.txt”, FILE_WRITE);
> if (! logfile) {
> Serial.print(“couldnt create file”);
> }
> logfile.println(“blabla123”);
> logfile.close();
any idea? looks like the working example is using functions of the sdfat library without the SD wrapper. however I didn’t find a working example using the the sdfat functions for writing. What I have tried didn’t work either.
any ideas? is it not possible to write to the sd card and use the ethernet interface at the same time?
I know about SPI that only one device can be online at the same time but reading works so I thougth the libraries are taking care of that?
thanks!

I have a web server working with the SD card, but it takes a lot of memory. It should work on an Uno, but works best on a Mega. http://playground.arduino.cc/Code/WebServerST

I have that same code running with the SdFat library also, and it work good. Requires only a couple minor mods. https://github.com/greiman/SdFat-beta

Thanks for your reply! Are you writing to the sd card too? Because reading did work for me too…

I have written to it also using my FTP sketch, and it does ok. However, the SD library is limited to 8.3 file names and this isn't 8.3: datalogger2.txt

I count 10.3.

holy crap.
i have read a thousand times about the 8.3 limitation and simply didn’t realize that datalogger2.txt isn’t 8.3.

are you some kind of code-parsing-debugging-machine? :smiley:

thank you, will try that!

nemail:
are you some kind of code-parsing-debugging-machine? :smiley:

No, I’ve just made the same mistake myself.

SurferTim: No, I've just made the same mistake myself.

oh ok, i felt very stupid until you said that :D

logfile = SD.open("dl2.txt", FILE_WRITE); if (! logfile) { Serial.println("couldnt create file"); } logfile.println("blabla123"); logfile.close();

that did not work but the following code worked:

if(file.open(&root, "dl2.txt", FILE_WRITE)) { Serial.println(" >> yay"); file.println("blabla"); delay(10); file.close(); // print to the serial port too: Serial.println(" >> written"); } // if the file isn't open, pop up an error: else { Serial.println("error opening dl2.txt"); }

however, this seems to always overwrite the file, instead of appending. do you know how to append to the file or is there something else going wrong?

thanks!

Reference your first example above, what do you mean by "didn't work"? Didn't open or didn't write?

SurferTim: Reference your first example above, what do you mean by "didn't work"? Didn't open or didn't write?

same as always, logfile was not true so the if/else jumped to Serial.println("error opening dl2.txt");

Do you have at least 512 bytes of SRAM available when you attempt to open the file? Add this function to your sketch and call it just before attempting to open the file. How much SRAM does it say you have available?

int freeRam() {
  extern int __heap_start,*__brkval;
  int v;
  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int) __brkval);  
}

// and call it like this
Serial.print(F("SRAM available: "));
Serial.println(freeRam());

SurferTim: Do you have at least 512 bytes of SRAM available when you attempt to open the file? Add this function to your sketch and call it just before attempting to open the file. How much SRAM does it say you have available?

int freeRam() {
  extern int __heap_start,*__brkval;
  int v;
  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int) __brkval);  
}

// and call it like this Serial.print(F("SRAM available: ")); Serial.println(freeRam());

hmm it says 407... :(

Then it is failing because you don't have enough SRAM to construct the 512 byte array (buffer) to open the file. You gotta work on reducing that. The F() function is a good way to do that if you haven't used it already.

cool function, now it says 579 free SRAM but still the same result...

You may need more than 512. I can't test the fail part for you because I have a Mega 2560 and a Due, and comparatively, both have gobs of SRAM. Use the F() function on all your client.println function calls that you can.

You can post your code and I can try it on my Mega and see if it does ok.

already tried that… thanks for your offer to try it on your mega (ordered some of them for myself but still waiting for them).

here is my code:

/*
 * This sketch uses the microSD card slot on the Arduino Ethernet shield
 * to serve up files over a very minimal browsing interface
 *
 * Some code is from Bill Greiman's SdFatLib examples,
 * some is from the Arduino Ethernet WebServer example, 
 * some is from Limor Fried (Adafruit),
 * some is from "jurs" for German Arduino forum,
 * so its probably under GPL
 */

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

/************ ETHERNET STUFF ************/
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 25, 222 };
EthernetServer server(80);

int testcounter;

/************ SDCARD STUFF ************/
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
File logfile;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

void error_P(const char* str) {
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}

char* strupper( char* s )
// helper function char array to uppercase letters
{
  for (char* p = s; *p; ++p)
    *p = toupper( *p );
  return s;
}

char* strlower( char* s )
// helper function char array to lowercase letters
{
  for (char* p = s; *p; ++p)
    *p = tolower( *p );
  return s;
}

int freeRam() {
  extern int __heap_start,*__brkval;
  int v;
  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int) __brkval);  
}

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

  PgmPrint("Free RAM: ");
  Serial.println(FreeRam());  
  
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  pinMode(10, OUTPUT);                       // set the SS pin as an output (necessary!)
  digitalWrite(10, HIGH);                    // but turn off the W5100 chip!

  if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!");
  
  // initialize a FAT volume
  if (!volume.init(&card)) error("vol.init failed!");

  PgmPrint("Volume is FAT");
  Serial.println(volume.fatType(),DEC);
  Serial.println();
  
  if (!root.openRoot(&volume)) error("openRoot failed");

  // list file in root with date and size
  PgmPrintln("Files found in root:");
  root.ls(LS_DATE | LS_SIZE);
  Serial.println();
  
  // Recursive list of all directories
  PgmPrintln("Files found in all dirs:");
  root.ls(LS_R);
  
  Serial.println();
  PgmPrintln("Done");
  
  // Debugging complete, we start the server!
  Ethernet.begin(mac, ip);
  server.begin();
}

void ListFiles(EthernetClient client, uint8_t flags) {
  // This code is just copied from SdFile.cpp in the SDFat library
  // and tweaked to print to the client output in html!
  dir_t p;
  
  root.rewind();
  client.println("<ul>");
  while (root.readDir(p) > 0) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;

    // print any indent spaces
    client.print("<li><a href=\"");
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print((char)p.name[i]);
    }
    client.print("\">");
    
    // print file name with possible blank fill
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print((char)p.name[i]);
    }
    
    client.print("</a>");
    
    if (DIR_IS_SUBDIR(&p)) {
      client.print('/');
    }

    // print modify date/time if requested
    if (flags & LS_DATE) {
       root.printFatDate(p.lastWriteDate);
       client.print(' ');
       root.printFatTime(p.lastWriteTime);
    }
    // print size if requested
    if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
      client.print(' ');
      client.print(p.fileSize);
    }
    client.println(F("</li>"));
  }
  client.println(F("</ul>"));
}

// How big our line buffer should be. 100 is plenty!
#define BUFSIZ 100

void loop()
{
  char clientline[BUFSIZ];
  int index = 0;
  
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    
    // reset the input buffer
    index = 0;
    
    while (client.connected()) {
      if (client.available()) {
        testcounter +=1;
        char c = client.read();
        
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
          
          // continue to read more data!
          continue;
        }
        
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
        
        // Print it out for debugging
        Serial.println(clientline);
        
        // Look for substring such as a request to get the root file
        if (strstr(clientline, "GET / ") != 0) {
          // send a standard http response header
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println();
          
          // print all the files, use a helper to keep it clean
          client.println(F("<h2>Files:</h2>"));
          ListFiles(client, LS_SIZE);
        } else if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
          
          // print the file we want
          Serial.println(filename);

          if (! file.open(&root, filename, O_READ)) {
            client.println(F("HTTP/1.1 404 Not Found"));
            client.println(F("Content-Type: text/html"));
            client.println();
            client.println(F("<h2>File Not Found!</h2>"));
            break;
          }
          
          Serial.println(F("Opened!"));
          strlower(filename);          
          client.println(F("HTTP/1.1 200 OK"));
          if (strstr(filename,".htm")!=NULL)
            client.println(F("Content-Type: text/html"));
          else if (strstr(filename,".jpg")!=NULL)
            client.println(F("Content-Type: image/jpg"));
          else  
            client.println(F("Content-Type: text/plain"));
          client.println();
          
          int16_t c;
          while ((c = file.read()) >= 0) {
              // uncomment the serial to debug (slow!)
              //Serial.print((char)c);
              client.print((char)c);
          }
          file.close();
          Serial.println(" >> FREE RAM: " + String(freeRam()));
          
          SD.begin();
          logfile = SD.open("dl2.txt", FILE_WRITE);
          if (! logfile) {
            Serial.println(F("couldnt create file"));
          }
          logfile.println(F("blabla123"));
          logfile.close();
          

          /*if(file.open(&root, "dl2.txt", FILE_WRITE))
          {
            Serial.println(" >> yay");
            file.println("blablablablablablablablablabla" + String(testcounter));
            delay(10);
            file.close();
            // print to the serial port too:
            Serial.println(" >> written");
          }
          // if the file isn't open, pop up an error:
          else {
            Serial.println("error opening dl2.txt");
          }*/
          
        } else {
          // everything else is a 404
          client.println(F("HTTP/1.1 404 Not Found"));
          client.println(F("Content-Type: text/html"));
          client.println();
          client.println(F("<h2>File Not Found!</h2>"));
        }
        break;
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}

Put it in code tags, not quote tags. I can't tell what the emoticons are.

done, sorry for the quote tags

I could not get your code to write to the SD card. It has some problems with the SD.begin() call after you run the card_init. It didn't throw an error, but didn't write.

I tried a logfile with my server using both SD and SdFat, and it worked fine.

SurferTim: I could not get your code to write to the SD card. It has some problems with the SD.begin() call after you run the card_init. It didn't throw an error, but didn't write.

I tried a logfile with my server using both SD and SdFat, and it worked fine.

as I'm a beginner it is quite hard for me to get an overview of your code. is there a chance that you'll find some time sometime to narrow your sample code down to a very simple example which does something like my code? it doesn't even have to read from the sd card. basically i just want it to show some random html page with static output like hello world or something and then writing a line to a log file that someone accessed the server.

that would be really great, i wasn't able to acomplish this for days :(

I have not tried your code with this, but it is the example from the reference. It does not use card_init. You would probably have to modify the printDirectory function to print to your web page, but that shouldn’t be hard. I use 115200 for the baud rate here.

#include <SD.h>

File root;
void setup()
{
  Serial.begin(115200);
  digitalWrite(10,HIGH);
  
  SD.begin(4);
  root = SD.open("/");
  printDirectory(root, 0);
  delay(2000);
  Serial.println();
  Serial.println("Rewinding, and repeating below:" );
  Serial.println();
  delay(2000);
  root.rewindDirectory();
  printDirectory(root, 0);
  root.close();
}

void loop()
{
  // nothing happens after setup finishes.
}

void printDirectory(File dir, int numTabs)
{
  while (true)
  {
    File entry = dir.openNextFile();
    if (! entry)
    {
      if (numTabs == 0)
        Serial.println("** Done **");
      return;
    }
    for (uint8_t i = 0; i < numTabs; i++)
      Serial.print('\t');
    Serial.print(entry.name());
    if (entry.isDirectory())
    {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    }
    else
    {
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}