From 059bc5f0224efc2b15489fae99a9f49dc15f1b53 Mon Sep 17 00:00:00 2001 From: "David A. Mellis" Date: Mon, 18 Apr 2011 17:58:54 -0400 Subject: [PATCH] Incorporating ladyada's SD changes (multifile & directories). Grabbed a patch from this repository: https://github.com/adafruit/SD and updated it for Arduino 1.0 (using Arduino.h instead of WProgram.h). --- libraries/SD/File.cpp | 102 ++++++++-- libraries/SD/README.txt | 2 + libraries/SD/SD.cpp | 183 +++++++++++++++++- libraries/SD/SD.h | 16 +- libraries/SD/examples/CardInfo/CardInfo.pde | 108 +++++++++++ libraries/SD/examples/listfiles/listfiles.pde | 77 ++++++++ 6 files changed, 472 insertions(+), 16 deletions(-) create mode 100644 libraries/SD/examples/CardInfo/CardInfo.pde create mode 100644 libraries/SD/examples/listfiles/listfiles.pde diff --git a/libraries/SD/File.cpp b/libraries/SD/File.cpp index ded622c85..9ef819667 100644 --- a/libraries/SD/File.cpp +++ b/libraries/SD/File.cpp @@ -14,52 +14,130 @@ #include +/* for debugging file open/close leaks + uint8_t nfilecount=0; +*/ + +File::File(SdFile f, char *n) { + // oh man you are kidding me, new() doesnt exist? Ok we do it by hand! + _file = (SdFile *)malloc(sizeof(SdFile)); + if (_file) { + memcpy(_file, &f, sizeof(SdFile)); + + strncpy(_name, n, 12); + _name[12] = 0; + + /* for debugging file open/close leaks + nfilecount++; + Serial.print("Created \""); + Serial.print(n); + Serial.print("\": "); + Serial.println(nfilecount, DEC); + */ + } +} + +File::File(void) { + _file = 0; + _name[0] = 0; + //Serial.print("Created empty file object"); +} + +File::~File(void) { + // Serial.print("Deleted file object"); +} + +// returns a pointer to the file name +char *File::name(void) { + return _name; +} + +// a directory is a special type of file +boolean File::isDirectory(void) { + return (_file && _file->isDir()); +} + + void File::write(uint8_t val) { - SD.file.write(val); + if (_file) + _file->write(val); } void File::write(const char *str) { - SD.file.write(str); + if (_file) + _file->write(str); } void File::write(const uint8_t *buf, size_t size) { - SD.file.write(buf, size); + if (_file) + _file->write(buf, size); } int File::peek() { - char c = SD.file.read(); - if (c != -1) SD.file.seekCur(-1); + if (! _file) + return 0; + + int c = _file->read(); + if (c != -1) _file->seekCur(-1); return c; } int File::read() { - return SD.file.read(); + if (_file) + return _file->read(); + return -1; +} + +// buffered read for more efficient, high speed reading +int File::read(void *buf, uint16_t nbyte) { + if (_file) + return _file->read(buf, nbyte); + return 0; } int File::available() { + if (! _file) return 0; return size() - position(); } void File::flush() { - SD.file.sync(); + if (_file) + _file->sync(); } boolean File::seek(uint32_t pos) { - return SD.file.seekSet(pos); + if (! _file) return false; + + return _file->seekSet(pos); } uint32_t File::position() { - return SD.file.curPosition(); + if (! _file) return -1; + return _file->curPosition(); } uint32_t File::size() { - return SD.file.fileSize(); + if (! _file) return 0; + return _file->fileSize(); } void File::close() { - SD.file.close(); + if (_file) { + _file->close(); + free(_file); + _file = 0; + + /* for debugging file open/close leaks + nfilecount--; + Serial.print("Deleted "); + Serial.println(nfilecount, DEC); + */ + } } File::operator bool() { - return SD.file.isOpen(); + if (_file) + return _file->isOpen(); + return false; } + diff --git a/libraries/SD/README.txt b/libraries/SD/README.txt index 863e878c0..495ea4c79 100644 --- a/libraries/SD/README.txt +++ b/libraries/SD/README.txt @@ -9,3 +9,5 @@ License: GNU General Public License V3 (C) Copyright 2010 SparkFun Electronics +Now better than ever with optimization, multiple file support, directory handling, etc - ladyada! + diff --git a/libraries/SD/SD.cpp b/libraries/SD/SD.cpp index aca7468dd..64dc40b98 100644 --- a/libraries/SD/SD.cpp +++ b/libraries/SD/SD.cpp @@ -275,10 +275,10 @@ boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent, } + /* boolean callback_openPath(SdFile& parentDir, char *filePathComponent, boolean isLastComponent, void *object) { - /* Callback used to open a file specified by a filepath that may specify one or more directories above it. @@ -293,7 +293,6 @@ boolean callback_openPath(SdFile& parentDir, char *filePathComponent, Returns false once the file has been opened--to prevent the traversal from descending further. (This may be unnecessary.) - */ if (isLastComponent) { SDClass *p_SD = static_cast(object); p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode); @@ -305,6 +304,8 @@ boolean callback_openPath(SdFile& parentDir, char *filePathComponent, } return true; } + */ + boolean callback_remove(SdFile& parentDir, char *filePathComponent, @@ -345,6 +346,64 @@ boolean SDClass::begin(uint8_t csPin) { } + +// this little helper is used to traverse paths +SdFile SDClass::getParentDir(char *filepath, int *index) { + // get parent directory + SdFile d1 = root; // start with the mostparent, root! + SdFile d2; + + // we'll use the pointers to swap between the two objects + SdFile *parent = &d1; + SdFile *subdir = &d2; + + char *origpath = filepath; + + while (strchr(filepath, '/')) { + + // get rid of leading /'s + if (filepath[0] == '/') { + filepath++; + continue; + } + + if (! strchr(filepath, '/')) { + // it was in the root directory, so leave now + break; + } + + // extract just the name of the next subdirectory + uint8_t idx = strchr(filepath, '/') - filepath; + if (idx > 12) + idx = 12; // dont let them specify long names + char subdirname[13]; + strncpy(subdirname, filepath, idx); + subdirname[idx] = 0; + + // close the subdir (we reuse them) if open + subdir->close(); + if (! subdir->open(parent, subdirname, O_READ)) { + // failed to open one of the subdirectories + return SdFile(); + } + // move forward to the next subdirectory + filepath += idx; + + // we reuse the objects, close it. + parent->close(); + + // swap the pointers + SdFile *t = parent; + parent = subdir; + subdir = t; + } + + *index = (int)(filepath - origpath); + // parent is now the parent diretory of the file! + return *parent; +} + + File SDClass::open(char *filepath, uint8_t mode) { /* @@ -369,6 +428,72 @@ File SDClass::open(char *filepath, uint8_t mode) { */ + int pathidx; + + // do the interative search + SdFile parentdir = getParentDir(filepath, &pathidx); + // no more subdirs! + + filepath += pathidx; + + if (! filepath[0]) { + // it was the directory itself! + return File(parentdir, "/"); + } + + // Open the file itself + SdFile file; + + // failed to open a subdir! + if (!parentdir.isOpen()) + return File(); + + // there is a special case for the Root directory since its a static dir + if (parentdir.isRoot()) { + if ( ! file.open(SD.root, filepath, mode)) { + // failed to open the file :( + return File(); + } + // dont close the root! + } else { + if ( ! file.open(parentdir, filepath, mode)) { + return File(); + } + // close the parent + parentdir.close(); + } + + if (mode & (O_APPEND | O_WRITE)) + file.seekSet(file.fileSize()); + return File(file, filepath); +} + + +/* +File SDClass::open(char *filepath, uint8_t mode) { + // + + Open the supplied file path for reading or writing. + + The file content can be accessed via the `file` property of + the `SDClass` object--this property is currently + a standard `SdFile` object from `sdfatlib`. + + Defaults to read only. + + If `write` is true, default action (when `append` is true) is to + append data to the end of the file. + + If `append` is false then the file will be truncated first. + + If the file does not exist and it is opened for writing the file + will be created. + + An attempt to open a file for reading that does not exist is an + error. + + // + // TODO: Allow for read&write? (Possibly not, as it requires seek.) fileOpenMode = mode; @@ -377,6 +502,7 @@ File SDClass::open(char *filepath, uint8_t mode) { return File(); } +*/ //boolean SDClass::close() { @@ -436,4 +562,55 @@ boolean SDClass::remove(char *filepath) { return walkPath(filepath, root, callback_remove); } -SDClass SD; \ No newline at end of file + +// allows you to recurse into a directory +File File::openNextFile(uint8_t mode) { + dir_t p; + + //Serial.print("\t\treading dir..."); + while (_file->readDir(&p) > 0) { + + // done if past last used entry + if (p.name[0] == DIR_NAME_FREE) { + //Serial.println("end"); + return File(); + } + + // skip deleted entry and entries for . and .. + if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') { + //Serial.println("dots"); + continue; + } + + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(&p)) { + //Serial.println("notafile"); + continue; + } + + // print file name with possible blank fill + SdFile f; + char name[13]; + _file->dirName(p, name); + //Serial.print("try to open file "); + //Serial.println(name); + + if (f.open(_file, name, mode)) { + //Serial.println("OK!"); + return File(f, name); + } else { + //Serial.println("ugh"); + return File(); + } + } + + //Serial.println("nothing"); + return File(); +} + +void File::rewindDirectory(void) { + if (isDirectory()) + _file->rewind(); +} + +SDClass SD; diff --git a/libraries/SD/SD.h b/libraries/SD/SD.h index 609104b10..3e0f0f62b 100644 --- a/libraries/SD/SD.h +++ b/libraries/SD/SD.h @@ -24,7 +24,14 @@ #define FILE_WRITE (O_READ | O_WRITE | O_CREAT | O_SYNC) class File : public Stream { + private: + char _name[13]; // our name + SdFile *_file; // underlying file pointer + public: + File(SdFile f, char *name); // wraps an underlying SdFile + File(void); // 'empty' constructor + ~File(void); // destructor virtual void write(uint8_t); virtual void write(const char *str); virtual void write(const uint8_t *buf, size_t size); @@ -32,11 +39,17 @@ public: virtual int peek(); virtual int available(); virtual void flush(); + int read(void *buf, uint16_t nbyte); boolean seek(uint32_t pos); uint32_t position(); uint32_t size(); void close(); operator bool(); + char * name(); + + boolean isDirectory(void); + File openNextFile(uint8_t mode = O_RDONLY); + void rewindDirectory(void); }; class SDClass { @@ -47,6 +60,8 @@ private: SdVolume volume; SdFile root; + // my quick&dirty iterator, should be replaced + SdFile getParentDir(char *filepath, int *indx); public: // This needs to be called to set up the connection to the SD card // before other methods are used. @@ -70,7 +85,6 @@ public: boolean rmdir(char *filepath); private: - SdFile file; // This is used to determine the mode used to open a file // it's here because it's the easiest place to pass the diff --git a/libraries/SD/examples/CardInfo/CardInfo.pde b/libraries/SD/examples/CardInfo/CardInfo.pde new file mode 100644 index 000000000..f81e21d17 --- /dev/null +++ b/libraries/SD/examples/CardInfo/CardInfo.pde @@ -0,0 +1,108 @@ +/* + SD card test + + This example shows how use the utility libraries on which the' + SD library is based in order to get info about your SD card. + Very useful for testing a card when you're not sure whether its working or not. + + The circuit: + * SD card attached to SPI bus as follows: + ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila + ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila + ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila + ** CS - depends on your SD card shield or module + + + created 28 Mar 2011 + by Limor Fried + */ + // include the SD library: +#include + +// set up variables using the SD utility library functions: +Sd2Card card; +SdVolume volume; +SdFile root; + +// change this to match your SD shield or module; +// Arduino Ethernet shield: pin 4 +// Adafruit SD shields and modules: pin 10 +// Sparkfun SD shield: pin 8 +const int chipSelect = 8; + +void setup() +{ + Serial.begin(9600); + Serial.print("\nInitializing SD card..."); + // On the Ethernet Shield, CS is pin 4. It's set as an output by default. + // Note that even if it's not used as the CS pin, the hardware SS pin + // (10 on most Arduino boards, 53 on the Mega) must be left as an output + // or the SD library functions will not work. + pinMode(10, OUTPUT); // change this to 53 on a mega + + + // we'll use the initialization code from the utility libraries + // since we're just testing if the card is working! + if (!card.init(SPI_HALF_SPEED, chipSelect)) { + Serial.println("initialization failed. Things to check:"); + Serial.println("* is a card is inserted?"); + Serial.println("* Is your wiring correct?"); + Serial.println("* did you change the chipSelect pin to match your shield or module?"); + return; + } else { + Serial.println("Wiring is correct and a card is present."); + } + + // print the type of card + Serial.print("\nCard type: "); + switch(card.type()) { + case SD_CARD_TYPE_SD1: + Serial.println("SD1"); + break; + case SD_CARD_TYPE_SD2: + Serial.println("SD2"); + break; + case SD_CARD_TYPE_SDHC: + Serial.println("SDHC"); + break; + default: + Serial.println("Unknown"); + } + + // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32 + if (!volume.init(card)) { + Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card"); + return; + } + + + // print the type and size of the first FAT-type volume + long volumesize; + Serial.print("\nVolume type is FAT"); + Serial.println(volume.fatType(), DEC); + Serial.println(); + + volumesize = volume.blocksPerCluster(); // clusters are collections of blocks + volumesize *= volume.clusterCount(); // we'll have a lot of clusters + volumesize *= 512; // SD card blocks are always 512 bytes + Serial.print("Volume size (bytes): "); + Serial.println(volumesize); + Serial.print("Volume size (Kbytes): "); + volumesize /= 1024; + Serial.println(volumesize); + Serial.print("Volume size (Mbytes): "); + volumesize /= 1024; + Serial.println(volumesize); + + + Serial.println("\nFiles found on the card (name, date and size in bytes): "); + root.openRoot(volume); + + // list all files in the card with date and size + root.ls(LS_R | LS_DATE | LS_SIZE); +} + + +void loop(void) { + +} diff --git a/libraries/SD/examples/listfiles/listfiles.pde b/libraries/SD/examples/listfiles/listfiles.pde new file mode 100644 index 000000000..b2435a2c6 --- /dev/null +++ b/libraries/SD/examples/listfiles/listfiles.pde @@ -0,0 +1,77 @@ +/* + SD card basic file example + + This example shows how to create and destroy an SD card file + The circuit: + * SD card attached to SPI bus as follows: + ** MOSI - pin 11 + ** MISO - pin 12 + ** CLK - pin 13 + ** CS - pin 4 + + created Nov 2010 + by David A. Mellis + updated 2 Dec 2010 + by Tom Igoe + + This example code is in the public domain. + + */ +#include + +File root; + +void setup() +{ + Serial.begin(9600); + Serial.print("Initializing SD card..."); + // On the Ethernet Shield, CS is pin 4. It's set as an output by default. + // Note that even if it's not used as the CS pin, the hardware SS pin + // (10 on most Arduino boards, 53 on the Mega) must be left as an output + // or the SD library functions will not work. + pinMode(10, OUTPUT); + + if (!SD.begin(10)) { + Serial.println("initialization failed!"); + return; + } + Serial.println("initialization done."); + + root = SD.open("/"); + + printDirectory(root, 0); + + Serial.println("done!"); +} + +void loop() +{ + // nothing happens after setup finishes. +} + +void printDirectory(File dir, int numTabs) { + while(true) { + + File entry = dir.openNextFile(); + if (! entry) { + // no more files + //Serial.println("**nomorefiles**"); + break; + } + for (uint8_t i=0; i