Ethernet-Shield als Fileserver

Hello everyone

due to our blog post yesterday, many customers contacted us who were already experimenting with the Shield and had problems using the SD card reader together with the W5100 at the same time. We did some research and found a solution that we would like to present to you today. Gerd recently showed us how to use an ESP as a file server with AnD card. Today we are adapting the solution for the Uno with Shield and introduce you to the working code with the standard libraries.

Hardware:

  • UnoR3
  • EthernetShield W5100
  • micro SD card with Fat32 file system

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, 168, 245 };
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* Street) {   PgmPrint("error: ");   SerialPrintln_P(Street);   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 you:");   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  = 0;  < 11; ++) {       If (P.Name[] == ' ') continue;       If ( == 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[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("</li>");   }   client.println("</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()) {         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("<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("HTTP/1.1 404 Not Found");             client.println("Content-Type: text/html");             client.println();             client.println("<h2>File Not Found!</h2>");             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("<h2>File Not Found!</h2>");         }         break;       }     }     // give the web browser time to receive the data     delay(1);     client.stop();   }
}

Viel Spaß beim Nachbasteln!

Ich freue mich über Ihre Feedback, und verabschiede mich bis zum nächsten Mal.
Moritz Spranger


For arduinoProjects for beginners

3 comments

H3

H3

Oh wartet! im vorherigen war ein kleiner fehler! Dieser code funktioniert.

/*
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
#include
#include

/************ ETHERNET STUFF ************/
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 178, 177 };
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)

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(115200);

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!) pinMode(4, OUTPUT); digitalWrite(10, HIGH); // but turn off the W5100 chip! digitalWrite(4, HIGH); if (!card.init(SPI_FULL_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(); delay(1000); digitalWrite(8, LOW);

}

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.name0 == DIR_NAME_FREE) break; // skip deleted entry and entries for . and .. if (p.name0 == DIR_NAME_DELETED || p.name0 == ‘.’) continue; // only list subdirectories and files if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue; // print any indent spaces client.print( 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(“”); 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) { digitalWrite(8, HIGH); // 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, “.html”) != NULL) client.println(“Content-Type: text/html”); else if (strstr(filename, “.jpg”) != NULL) client.println(“Content-Type: image/jpg”); else if (strstr(filename, “.png”) != NULL) client.println(“Content-Type: image/png”); else if (strstr(filename, “.mp3”) != NULL) client.println(“Content-Type: audio/mpeg”); else if (strstr(filename, “.ogg”) != NULL) client.println(“Content-Type: audio/ogg”); else if (strstr(filename, “.wav”) != NULL) client.println(“Content-Type: audio/wav”); else if (strstr(filename, “.m4a”) != NULL) client.println(“Content-Type: audio/mp4”); else if (strstr(filename, “.mp4”) != NULL) client.println(“Content-Type: video/mp4”); 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); digitalWrite(8, !digitalRead(8)); if (!client.connected()) { break; } if (!client.available()) { break; } } file.close(); Serial.println(F(“Closed.”)); } 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(); digitalWrite(8, LOW); }

}

H3

H3

Hab ein paar verbesserungen getätigt.
u.a. stoppt die übertragung jetzt wenn der client disconnected. außerdem gibt es jetzt eine Info LED auf pin 8 die aktivität anzeigt.

/*
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
#include
#include

/************ ETHERNET STUFF ************/
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 178, 177 };
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)

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(115200);

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!) pinMode(4, OUTPUT); digitalWrite(10, HIGH); // but turn off the W5100 chip! digitalWrite(4, HIGH); if (!card.init(SPI_FULL_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(); delay(1000); digitalWrite(8, LOW);

}

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.name0 == DIR_NAME_FREE) break; // skip deleted entry and entries for . and .. if (p.name0 == DIR_NAME_DELETED || p.name0 == ‘.’) continue; // only list subdirectories and files if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue; // print any indent spaces client.print( 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(“”); 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) { digitalWrite(8, HIGH); // 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, “.html”) != NULL) client.println(“Content-Type: text/html”); else if (strstr(filename, “.jpg”) != NULL) client.println(“Content-Type: image/jpg”); else if (strstr(filename, “.png”) != NULL) client.println(“Content-Type: image/png”); else if (strstr(filename, “.mp3”) != NULL) client.println(“Content-Type: audio/mpeg”); else if (strstr(filename, “.ogg”) != NULL) client.println(“Content-Type: audio/ogg”); else if (strstr(filename, “.wav”) != NULL) client.println(“Content-Type: audio/wav”); else if (strstr(filename, “.m4a”) != NULL) client.println(“Content-Type: audio/mp4”); else if (strstr(filename, “.mp4”) != NULL) client.println(“Content-Type: video/mp4”); 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); digitalWrite(8, !digitalRead(8)); if (!client.connected()) { return; } if (!client.available()) { return; } } file.close(); Serial.println(F(“Closed.”)); } 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(); digitalWrite(8, LOW); }

}

Magnus

Magnus

I just bought from you a EthernetShield W5100 and wanted to test it with your code but I always get SD error: 1, FF with the code in the blog.
I have tried the code in the eBook. The WebServer example code works well, but the “Serving the web page from SD card, with AJAX” gives always a SD error.
Any clue to sort this out ?

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN