Help Reading/Writing to SD Card

Hi all,

I'm having some trouble using the newer Ethernet/Micro SD card shield. My program creates a log file and writes to it once/second. Meanwhile it monitors for and responds to HTTP requests. The HTTP page contains a list of files on the SD card, and when you click on one of the file names, it should open the file.

The web server part works correctly when the logging is disabled, but once I turn on the logging, it fails when I try to download one of the files. Do I need to disable the logging while reading the file?

A very abbreviated portion of the code is below... I basically cut out everything not related to the web server or SD card.

#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <Ethernet.h>
#include <Time.h> 
#include <Udp.h>
#include <Flash.h>

//define USE_LOG

// ETHERNET SETUP
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 101 };
Server server(80);

// SD CARD SETUP
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

// setup variables to track interval for logging/temp adjustment
long previousMillis = 0;        // will store last time logging/temp adjustment was called
long SECOND = 1000;
long MINUTE = SECOND*60;
long HOUR = MINUTE*60;
long interval = SECOND;           // interval at which to log data/adjust temp

int tempProgram[] = {110, 110, 120, 130, 140, 150, 160};

// heating element
int elementStatus = HIGH;
unsigned int elementPin =  A5;

//... messages

//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);
  }
}

#define BUFSIZ 120

void setup() {
  Serial.begin(9600); 
  char buffer[BUFSIZ];

  Serial.print(welcome_to);
  Serial.println(botul9m);

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

  pinMode(elementPin, OUTPUT);

  // start ethernet
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);
  server.begin();

  // initialize time
  setupTime();
  initializeLog();
}

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis > interval) {
  
    // save the last time you log/adjust temp
    previousMillis = currentMillis;
 
    // read temp data
    pitTemp = thermister_temp(analogRead(4));
    meatTemp = thermister_temp(analogRead(3));
   
#ifdef USE_LOG
    logData();
#endif // USE_LOG
    adjustTargetTemp();
    adjustHeat();
  }
  
  listenForClients();
}

void listenForClients() {
  Client client = server.available();
  if (client) {
    char buffer[BUFSIZ];
    Serial.println(web_req);
    int index = 0;

    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c != '\n' && c != '\r') {
          buffer[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;

          continue;            // continue to read more data!
        }
        buffer[index] = 0;

        if (strstr(buffer, "?") != 0) { // update pit/meat targets if needed
          String s = String(buffer);
          String p = s.substring(s.indexOf("pit=")+4, s.indexOf("meat=")-1);
          char p_char[p.length()+1];
          p.toCharArray(p_char, sizeof(p_char));
          pitTarget = atoi(p_char);
          String m = s.substring(s.indexOf("meat=")+5, s.indexOf("HTTP")-1);
          char m_char[m.length()+1];
          m.toCharArray(m_char, sizeof(m_char));
          meatTarget = atoi(m_char);
        }

        if (strstr(buffer, "GET /del=") !=0 ) { // handle delete
          char filename[13];
          for(int i=0; i<=11; i++){
            filename[i] = buffer[i+9];
          }
          filename[12] = 0;
          if (!file.open(&root, filename, O_WRITE)) {
            Serial.print("Can't open "); 
            Serial.println(filename);
            //error("file.open failed");
          }
          if (!file.remove()) {
            Serial.print("file.remove failed");
          }
        }else if (strstr(buffer, ".CSV") != 0) { // serve up file
          char filename[13];
          for(int i=0; i<=11; i++){
            filename[i] = buffer[i+5];
          }
          filename[12] = 0;
          Serial.println(filename);
          if (! file.open(&root, filename, O_READ)) { // can't read file
            client.println(http_404);
            client.println(content_html);
            client.println();
            client.println(file_not_found);
          } else {  // can read file
            client.println(http_ok);
            client.println(content_csv);
            attachment.copy(buffer);
            char buf[55];
            sprintf(buf, buffer, filename);
            client.println(buf);
            client.println();
            
            int16_t c;
            while ((c = file.read()) > 0) {
                //Serial.print((char)c); // uncomment the serial to debug (slow!)
                client.print((char)c);
            }
            file.close();
          }
          break;

        }
        
        client.println(http_ok);
        client.println(content_html);
        client.println("");
        client.println(html_open);
        client.println(h2_files);
        ListFiles(client, 0);
        client.println(html_close);
        break;

      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}

int thermister_temp(int aval) {
//
}

void initializeLog() {
  if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!");
  
  // initialize a FAT volume
  if (!volume.init(&card)) error("volume.init failed");
  
  // open root directory
  if (!root.openRoot(&volume)) error("openRoot failed");

#ifdef USE_LOG
  // create a new file
  char name[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    name[6] = i/10 + '0';
    name[7] = i%10 + '0';
    if (file.open(&root, name, O_CREAT | O_EXCL | O_WRITE)) break;
  }
  file.open(&root, name, O_CREAT | O_EXCL | O_WRITE);
  if (!file.isOpen()) error ("file.create");
  Serial.print(F("Logging to: "));
  Serial.println(name);

  // write header
  file.writeError = 0;
  file.print(F("time,pitTemp,pitTarget,meatTemp,meatTarget,elementStatus"));
  file.println();
  if (file.writeError || !file.sync()) {
    error("write header failed");
  }
#endif // USE_LOG
}

// log data to the SD card
void logData() {
  char line[60];
  file.writeError = 0;
  sprintf(line, "%2.2d:%2.2d:%2.2d,%3d,%3d,%3d,%3d,%3d", hour(), minute(), second(),pitTemp, pitTarget, meatTemp, meatTarget, elementStatus);
  file.print(line);
  file.println();  
  if (!file.sync()) error("sync failed");
}


void ListFiles(Client 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;
  char buffer[15];
  
  root.rewind();
  client.println(open_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.println(open_li);
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print(p.name[i]);
    }
    client.println(end_a);
    
    // 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(p.name[i]);
    }
    
    client.println(close_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);
    }
    
    // print file name with possible blank fill
    client.print(open_del);
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print(p.name[i]);
    }
    client.print(close_del);
    
    client.println(close_li);
  }
  client.println(close_ul);
}

You can only use the ehternet shield OR the SD card at one time. You have to disable one to use the other.

It could be tricky trying to serve up a file off the SD card. You'd need to read some data into memory, disable the SD card, enable the ethernet connection, send the data, disable the ethernet connection, read some more data into memory, and repeat the process, until all data was sent.

Whether data is maintained, like ethernet connections and read positions, when the devices are disabled, I don't know.

The adafruit tutorial found here works fine, and I am able to serve files from the SD card using similar code. The problem comes up when I'm writing to the log as well. Maybe when serving up files I need to close the log file, then re-open after?

Maybe when serving up files I need to close the log file, then re-open after?

I think this is a "Well, duh!" moment.

Q1: If you write your log statements to the serial port (@ 115200) instead of the SDcard does the problem still occur?

Q2: how much free RAM do you have when calling logdata?
there is a local var line[60] and some temp var's due to sprintf().
??