SD card read speed

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
Hokama
 
Posts: 3
Joined: Sun Mar 18, 2012 5:45 am

SD card read speed

Post by Hokama »

Hello!
I have problem with SD card read speed, when using SdFat lib.
root dir contains: index.htm with link on style.css, 3 javascript files and 2 png images
I'm using MEGA 2560 rc3 with Ethernet Sield rc3 and microSD card 4GB FAT32
sketch for web server:

Code: Select all

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

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

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_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();
}

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

void loop()
{

  char clientline[BUFSIZ];
  char *filename;
  int index = 0;
  int image = 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;
        filename = 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) {
          filename = rootFileName;
        }
        if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file
          
          if (!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!");
          
          client.println("HTTP/1.1 200 OK");
          if (strstr(filename, ".htm") != 0)
             client.println("Content-Type: text/html");
         else if (strstr(filename, ".css") != 0)
             client.println("Content-Type: text/css");
         else if (strstr(filename, ".png") != 0)
             client.println("Content-Type: image/png");
          else if (strstr(filename, ".jpg") != 0)
             client.println("Content-Type: image/jpeg");
         else if (strstr(filename, ".gif") != 0)
             client.println("Content-Type: image/gif");
         else if (strstr(filename, ".3gp") != 0)
             client.println("Content-Type: video/mpeg");
         else if (strstr(filename, ".pdf") != 0)
             client.println("Content-Type: application/pdf");
         else if (strstr(filename, ".js") != 0)
             client.println("Content-Type: application/x-javascript");
         else if (strstr(filename, ".xml") != 0)
             client.println("Content-Type: application/xml");
         else
             client.println("Content-Type: text");

          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();
  }
}
766KB loading about 5min :evil:

I combined all the .htm .js .css files in one index.htm, without .png and try run this sketch:

Code: Select all

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

#define BUFFER_SIZE 512
#define ETH_SS 10
#define SD_SS 4

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 177 };
EthernetServer server(80);

void setup()
{
    pinMode(ETH_SS, OUTPUT);
    digitalWrite(ETH_SS, HIGH);

    SD.begin(SD_SS);

    digitalWrite(SD_SS, HIGH);
    digitalWrite(ETH_SS, LOW);
    Ethernet.begin(mac, ip);
    server.begin();
}

void loop()
{
    EthernetClient client = server.available();
    byte buff[BUFFER_SIZE];

    if (client) {
        bool eol = false;
        while (client.connected()) {
            if (client.available()) {
                char c = client.read();
                if (c == '\r')
                    continue;

                if (c == '\n' && eol) {
                    File f = SD.open("index.htm");
                    if (f) {
                        client.println("HTTP/1.1 200 OK");
                        client.println("Content-Type: text/html");
                        client.println();

                        while (true) {
                            int n = f.read((char*)buff, BUFFER_SIZE);
                            if (!n)
                                break;
                            client.write(buff, n);
                        }

                        f.close();
                    } else {
                        client.println("HTTP/1.1 404 Not Found");
                        client.println("Content-Type: text/plain");
                        client.println();
                        client.println("Not found");
                        client.println();
                    }

                    break;
                }

                eol = (c == '\n');
            }
        }

        client.stop();
    }
}
now 231KB loading about 4.7 sec
and is work more than 20 times faster, but i can't use separated .css and .js files...
have any idea where there may be a mistake?

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: SD card read speed

Post by adafruit_support_bill »

What makes you think it is SD card read speed? The two sketches are completely different. The first is sending the file over one byte at a time. The second one is sending it in chunks of 512 bytes.

Hokama
 
Posts: 3
Joined: Sun Mar 18, 2012 5:45 am

Re: SD card read speed

Post by Hokama »

I know. but how to make the first sketch to send files in chunks of 512 bytes?
#define BUFSIZ 512 in first sketch this is not the same as #define BUFFER_SIZE 512 in second sketch?
sorry if this is stupid question, but I just started to learn SdFat lib.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: SD card read speed

Post by adafruit_support_bill »

#define BUFSIZ 512 in first sketch this is not the same as #define BUFFER_SIZE 512 in second sketch?
The first sketch is using the buffer for reading the client request. When it comes to sending the file, it does it one character at a time like this:
while ((c = file.read()) >= 0) {
// uncomment the serial to debug (slow!)
//Serial.print((char)c);
client.print((char)c);
}
The second sketch is doing only minimal parsing of the client request. It only looks for the EOL, then sends index.htm as the response.
It uses the buffer to send it in 512 byte chunks like this:

Code: Select all

                        while (true) {
                            int n = f.read((char*)buff, BUFFER_SIZE);
                            if (!n)
                                break;
                            client.write(buff, n);
                        }
Since memory space is limited, you can probably just re-use the same buffer to transmit the file after you parse the client request .

Hokama
 
Posts: 3
Joined: Sun Mar 18, 2012 5:45 am

Re: SD card read speed

Post by Hokama »

Thx! I found my mistake :D
there SdFat.pdf is really helpful thing:
int16_t SdFile::read (void * buf, uint16_t nbyte)
Read data from a file starting at the current position.
Parameters:
[out] buf Pointer to the location that will receive the data.
[in] nbyte Maximum number of bytes to read.
Returns:
For success read() returns the number of bytes read. A value less than nbyte , including zero, will be
returned if end of file is reached. If an error occurs, read() returns -1. Possible errors include read() called
before a file has been opened, corrupt file system or an I/O error occurred.
Now, this part of sketch looks like that:

Code: Select all

//size of read buffer 
#define CHUNKS 512

void loop()
{
   byte buff[CHUNKS];

//there is changes of read file part
          int16_t c;
          while (true) {
             int c = file.read((char*)buff, CHUNKS);
             if (!c)
             break;
             client.write(buff, c);
          }
          file.close();
}
:mrgreen: :mrgreen:

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”