SD Card Read Only

At the moment I store some configuration data (5 variable values) in EEPROM which is read only once during setup, then used in the sketch.

I would like to put the configuration data on an SD Card instead. The only functional requirement will be a one time read during setup. The data will be pre-written on the card on a different system, so there is no SD Card write requirement in the sketch.

The issue I am having is the amount of memory the standard SD.h library uses. I have been doing some research and found some alternative libraries including Sdfat.h, although there seems to be a number of forks. Fat16 is not practical - as it is not easy to find cards.

If my understanding is correct, since there is no write requirement, there may be no need for a 512 buffer.

Does anyone have a recommendation for a suitable library?

Another "out there" question... Once the sketch starts and the data has been read from the SD Card, is it possible to "exclude" the SD Card library and free up some memory?

TIA for any help.

If my understanding is correct, since there is no write requirement, there may be no need for a 512 buffer.

It is not. Data is read from and written to the physical device in 512 byte chunks.

Once the sketch starts and the data has been read from the SD Card, is it possible to "exclude" the SD Card library and free up some memory?

No. The buffer that holds the data doesn't care/know that you don't ever intend to read from/write to the card again.

Thanks for the input.

So far I have switched from the core SD library to the SdFat library and that has helped. At least I can now compile the sketch and not run out of memory.

I found the FatFs library and an Arduino wrapper to go with it. Unfortunately the wrapper examples don't compile - yet, so I would need to look into that.

However I also found the smaller version viz. Petit FatFs library which seems more suited for what I need.

I also found an outdated Arduino wrapper for an ATTiny84 of Petit FatFs.

Would anyone be able to provide guidance on how to use Petit FatFs with an Arduino?

Please use this version.

Here is a zip file with a version of Petit FatFs I was playing with. I did some quick hacks to get it to run on an Uno.

This example reads the file “TEST.TXT” and prints it to serial.

// Petit FS test.   

#include "PetitFS.h"

const uint8_t CS_PIN = 4;

FATFS fs;     /* File system object */
//------------------------------------------------------------------------------
void errorHalt(char* msg) {
  Serial.print("Error: ");
  Serial.println(msg);
  while(1);
}
//------------------------------------------------------------------------------
void test() {
  uint8_t buf[32];
  
  // Set CS pin.  Was PORT/PIN defs in diskio.h
  set_cs_pin(CS_PIN);
  
  // Initialize SD and file system.
  if (pf_mount(&fs)) errorHalt("pf_mount ");
  
  // Set SPI clock to 8 MHz.
  set_fast_spi();
  
  // Open test file.
  if (pf_open("TEST.TXT")) errorHalt("pf_open ");
  
  // Dump test file to Serial.
  while (1) {
    uint16_t nr;
    if(pf_read(buf, sizeof(buf), &nr)) errorHalt("pf_read");
    if (nr == 0) break;
    for (uint8_t i = 0; i < nr; i++) {
      Serial.write(buf[i]);
    }
  }
}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  test();
  Serial.println("\nDone!");
}
void loop() {}

The example has the following size:

Sketch uses 5,910 bytes (18%) of program storage space. Maximum is 32,256 bytes.
Global variables use 296 bytes (14%) of dynamic memory, leaving 1,752 bytes for local variables. Maximum is 2,048 bytes.

The original files are included and a diff shows what I did. See the readme.txt file in the PetitFS folder.

You are on your own to do more mods.

PetitFS_v1.zip (217 KB)

@fat16lib : Thanks for going preparing that - much appreciated.

Please use this version.

I was able to save a bit more flash in the example on 328 by defining a constant SD chip select pin.

Sketch uses 5,446 bytes (16%) of program storage space. Maximum is 32,256 bytes.
Global variables use 295 bytes (14%) of dynamic memory, leaving 1,753 bytes for local variables. Maximum is 2,048 bytes.

A second version is attached.

In 2008 I wrote a read only SD library as part of the WaveHC library for the Adafruit Wave Shield. That library ran on 168 boards with 16K flash and 1K of RAM.

I am updating WaveHC and plan to break out the SD read library. It is slightly larger than PetitFS but has several useful features. It is much faster when reading a file in small chunks.

ChaN’s design of PetitFS is extremely clever so I may post a cleaned up version of my mods for Arduino on gitHub.

PetitFS_v2.zip (218 KB)

@fat16lib : I just tried the sample PetitFS.ino sketch and get : Error: pf_open

FWIW I used a different filename as I have an existing file on the card which works with SdFat.h

I am using the library files attached in your post #5 (i.e. not the GitHub ones) with an Uno with Ethernet shield + SD Card and IDE 1.5.7.

I commented out the define for CS_PIN in avr_mmcp.cpp and defined the SD_CS_PIN in PetitFS.ino by uncommenting the Pin 4 define setting.

I also set : pinMode (10, OUTPUT);

Any ideas.

@fat16lib : I just tried the sample PetitFS.ino sketch and get : Error: pf_open

ChaN, the author of Petit FatFs, insists on the proper case for file names unless you enable lower case in pffconfig.h

#define _USE_LCC 0 /* Allow lower case characters for path name */

It should work if you use upper case in the open call. The short file name on the SD is upper case even if you used lower case to create it.

I tried the GitHub example on an Ethernet Shield and it works.

I edited pffArduino.h for the Ethernet shield like this:

// SD chip select pin
#define SD_CS_PIN 4

I also set : pinMode (10, OUTPUT);

Pin 10 is SS on Uno so it is set high and in output mode by the SPI initialization routine.

I downloaded the Github version. Yes, the problem was the file name case.

@fat16lib : In your example you used the following three "if" statement to test for error conditions to call the errorHalt() function.

    if (pf_mount(&fs)) errorHalt("pf_mount ");
    if (pf_open("TEST.TXT")) errorHalt("pf_open ");
    if (pf_read(buffer, sizeof(buffer), &nr)) errorHalt("pf_read");

Is there another ways to test for non-error conditions other than using "!" in connection with the above.

    if (!pf_mount(&fs)) ...do something...;
    if (!pf_open("TEST.TXT")) ...do something...;
    if (!pf_read(buffer, sizeof(buffer), &nr)) ...do something...;

FWIW I know it works, but the example code seems almost "illogical" or reversed - to me anyway. For me logical code would run something like this:

if(file.open("name.text") ...everything is ok...;
if(!file.open("name.text") ...everything is not ok...;

PetitFS return an error code, not a bool true/false. This style has many advantages and is often used in embedded systems.

Here are possible returns:

/* File function return code (FRESULT) */

typedef enum {
  FR_OK = 0, 
  FR_DISK_ERR,
  FR_NOT_READY,
  FR_NO_FILE,
  FR_NOT_OPENED,
  FR_NOT_ENABLED,
  FR_NO_FILESYSTEM
} FRESULT;

You might find this style more logical.

    if (FR_OK != pf_mount(&fs)) errorHalt("pf_mount ");
    if (FR_OK != pf_open("TEST.TXT")) errorHalt("pf_open ");
    if (FR_OK != pf_read(buffer, sizeof(buffer), &nr)) errorHalt("pf_read");

fat16lib: PetitFS return an error code, not a bool true/false. This style has many advantages and is often used in embedded systems.

Here are possible returns:

/* File function return code (FRESULT) */

typedef enum {   FR_OK = 0,   FR_DISK_ERR,   FR_NOT_READY,   FR_NO_FILE,   FR_NOT_OPENED,   FR_NOT_ENABLED,   FR_NO_FILESYSTEM } FRESULT;

Thanks for the explanation.

You might find this style more logical.

    if (FR_OK != pf_mount(&fs)) errorHalt("pf_mount ");
    if (FR_OK != pf_open("TEST.TXT")) errorHalt("pf_open ");
    if (FR_OK != pf_read(buffer, sizeof(buffer), &nr)) errorHalt("pf_read");

Yes, definitely more logical - Thanks! Although I would probably reverse the 2 comparators like so:

    if (pf_mount(&fs) != FR_OK) errorHalt("pf_mount ");
    if (pf_open("TEST.TXT") != FR_OK) errorHalt("pf_open ");
    if (pf_read(buffer, sizeof(buffer), &nr) != FR_OK) errorHalt("pf_read");