URL
How do I create a file?
In order to write information to a microSD card we need to have access to a file. If a file doesn't exist yet it must be created. Before the file can be created, though, we must initialize the microSD card to use a FAT file system, initialize a volume on the file system, and then finally open the root directory in the volume. Once all of this is done we can create a file. Luckily for us, the SdFat library (written by William Greiman) does most of the work for us! First create the objects that are needed for the FAT library to work. This code must go at the start of the sketch before both the loop and the setup sections:
//Create the variables to be used by SdFat Library char name[] = "Test.txt";//Create an array that contains the name of our file. Sd2Card card; SdVolume volume; SdFile root; SdFile file;
To initialize the FAT filesystem, the volume and open the root directory all we need to do is place this code in the setup section of the sketch:
pinMode(10, OUTPUT); //Pin 10 must be set as an output for the SD communication to work. card.init(); //Initialize the SD card and configure the I/O pins. volume.init(card); //Initialize a volume on the SD card. root.openRoot(volume); //Open the root directory in the volume.
Now we can create the file. We'll need access to the root object and the file name, which were both created at the beginning of the sketch. We'll also use the constants O_CREATE, O_APPEND and O_WRITE to tell the sketch we want to create a file in write mode, and that we want to append data to the end of the file. The constants are defined by the SdFat library; you can learn more about them by reading the documentation located in the library folder. This line of code will create a file in the root directory with the file name designated by the variable 'name':
file.open(root, name, O_CREAT | O_APPEND | O_WRITE); //Open or create the file 'name' in 'root' for writing to the end of the file
How do I open a file?
To read or write from a file, the file in question must first be opened. In order to open a file the file's name must be known. There are several different ways to open a file: a file can be created and opened (like in the example above), it can be opened in read mode, or it can be opened in write mode. We also specify the index of the file where we want to start reading or writing. For example, if we want to read from the beginning of a file we would open the file like this:
file.open(root, name, O_READ); //Open the file in read mode.
However, if we want to write to the end of a file we would change the command to look like this:
file.open(root, name, O_WRITE | O_APPEND); //Open the file in write mode and append the data to the end of the file.
It's also important to remember that after we're finished reading or writing data to the file it should be closed. If the file isn't closed after writing data to it the data might not end up being saved. It's also just good practice to close this file as it helps to maintain the integrity of the file system. To close the file just use this function:
file.close(); //Close the file
What if I don't know the file name of a file I want to open?
If you're building something like an MP3 Player or a Picture Viewer than the sketch probably won't know all of the file names on the microSD card (you may add or delete songs/pictures). In this case you'll need to be able to open files without knowing their name. Luckily for us the SdFat library has a method of opening files by their location in the FAT file system; all we have to do is find the location of the files. Let's look at some code that will open and print information from all of the files on an SD card (assuming that all of the files on the card are text files):
while(root.readDir(directory)>0){ file.open(root, root.curPosition()/32-1, O_READ); in_char=file.read(); //Get the first byte in the file. //Keep reading characters from the file until we get an error or reach the end of the file. (This will output the entire contents of the file). while(in_char >=0){ //If the value of the character is less than 0 we've reached the end of the file. Serial.print(in_char); //Print the current character in_char=file.read(); //Get the next character } file.close(); //Close the file Serial.println(); }
Before this loop is started, 'root' is an empty file structure. The function:
while(root.readDir(directory)>0)
loads information about the first file in the directory into root. If the function returns a 0 then we've reached the end of the directory; therefor we execute this function repeatedly until we get a 0 returned. Each time we execute the function we retrieve some information about the file. The next line of the sketch is:
file.open(root, root.curPosition()/32-1, O_READ);
You should recognize parts of this function from when we learned how to open a file. However this time instead of telling the file.open() function the name of the file to open, we're indicating the index of the file that needs to be opened. Since we've loaded information about a file into the 'root' variable we can find the file's position using the curPosition() function. However, the documentation of the file.open() command tells us that if we're giving the function an index of a file we need to divide the position by 32. Then, since indices are always 0 based in computer language, we subtract 1 to find the actual index of the file.
Once we open the file and read all of the information from it we close the file and go back to the beginning of the 'while' loop. When the readDir() command is executed again information for the next file in the directory is loaded into 'root.' This is how we move on to the next file.
How do I write to a file?
The most useful aspect of a microSD card is using it write information to a file that can later be used on a computer to view information. Maybe you're building a data logger, or maybe it's an audio recorder; regardless of what it is you're recording you'll need to know how to write information you've retrieved from the real world and save it to a file. Once you've created and opened a file, saving data is very easy! Let's look at a few lines of code that will simply copy the string "Millis: " followed by an integer into a string, and then save the string to a file.
file.open(root, name, O_CREAT | O_APPEND | O_WRITE); //Open or create the file 'name' in 'root' for writing to the end of the file. sprintf(contents, "Millis: %d\n", millis()); //Copy the letters 'Millis: ' followed by the integer value of the millis() function into the 'contents' array. file.print(contents); //Write the 'contents' array to the end of the file. file.close(); //Close the file.
The first line in the sample we should be pretty familiar with; we've opened a file in write mode, and we're going to write to the end of the file. The 'sprintf()' function is a nifty function that allows us to copy a formatted string into a buffer. In this case the function copies the text 'Millis: ', then we substitute the '%d' for the number returned by the function millis(). Finally we add a new-line character at the end of the string. All of this is copied to the 'contents' buffer. The 'millis()' function is an Arduino function that simply returns the number of milliseconds that have elapsed since the sketch started running. So for example, if the sketch has been running for 200 milliseconds and this code was executed the text 'Millis: 200' would be copied to the 'contents' buffer. Now we've captured the data that needs to be saved to the file. All we have to do is print the buffer to the file (just like if you wanted to print information to the Serial terminal in Arduino). Don't forget to close the file after you finish writing information to it!
Challenge: Can you alter this code to save information read from an Analog input pin? Try using a thermistor to read temperature every second and save the information to a file every time you read the value.
How do I read from a file?
There are many reasons we might want to read information from a file on a microSD card; accessing songs or pictures, retrieving system setting or maybe analyzing a set of data that was saved earlier. Whatever the reason is the method for accessing the data from a file is the same, and it's easy too! In fact, if you know how to get information from a Serial connection than you should already be pretty familiar with the process. In order to read data we'll need a buffer to store the information; then we'll just use the 'read()' function in the SdFat library to grab information from an opened file. In this example code a buffer has been created named 'file_contents.'
char file_contents[256]; //This is a data buffer that holds data read from a file
Now we just need to open a file and start reading data from it. There are two ways to read data from a file using the SdFat library; we can read one character at a time or we can read a specific amount of data from a file. We'll look at both methods. If we are reading one character at a time we need to keep the size of the buffer in mind; our buffer size is 256 characters. Here's an example of how to read data from a file one character at a time:
int index=0; //Create a variable to keep track of our position in the data buffer. file.open(root, name, O_READ); //Open the file in read mode. file_contents[index]=file.read(); //Get the first byte in the file. //Keep reading characters from the file until we get an error or reach the end of the file. (This will output the entire contents of the file). while(file_contents[index] >=0 && index < i="0;">
In this example we read one character at a time from the file and save it to our data buffer. The loop keeps track of two conditions. If the character read from the file is 0 than we've reached the end of the file, so we exit the loop and close the file. On the other hand, if the 'index' value reaches 256 than we've completely filled the data buffer so we can't keep adding data to it. If this happens we also exit the loop and close the file. After closing the file the sketch prints the contents of the buffer to the screen.
This method of reading data from a file (one character at a time) can be a bit slow and cumbersome to code. If you know how much data you need to read from a file and you are confident that your data buffer has enough room to store the data then you may prefer this method of reading data from a file:
int index=0; int data_size=10; //This variable sets the number of bytes to read from the file. index=file.read(file_contents, data_size); //file_contents is the data buffer for storing data. data_size is a variable set to the amount of data to read. file.close(); //Close the file for(int i=0; i
Much simpler! This example will read 'data_size' bytes (10) from the file and store the data into the 'file_contents' data buffer. The 'index' variable will contain the number of bytes read from the file in case it's different than the number we specified; this could happen if we reach the end of the file before reading the 'data_size' bytes.
Can I really use any size microSD card without modifying the code?
Well yes, any size that's currently available, and as long as you use a library that supports a FAT file system. The SdFat library (the one we've been using) supports both FAT16 and FAT32. FAT32 can support volume sizes up to 2 TiB in size; I'm pretty sure you can't even get microSD cards with that much storage...yet. So, as long as the microSD card being used is formatted in either FAT16 or FAT32 then the code will be the same regardless of the cards size.
I downloaded a FAT library for Arduino on my own from the Web but it's not working! Why not?
If you decided to go and download a FAT library from the Internet on your own, good for you! Arduino is all about learning and exploring on your own. You may have found, though, that the library is not working for you. It happens to the best of us! In physical computing we have the added complexity of hardware to keep in mind when we are trying to debug a project. If you've downloaded a library that you're confident in then the problem is likely arising from a hardware issue rather than a code issue. In the case of the microSD card, the problem probably lies within the SPI communication library.
SPI is a protocol designed for communicating between two devices. You can read more about it on wikiepedia; but for our purposes it's important to know that there are four signals used in SPI: MOSI, MISO, SCK and CS (or SS). These signals are defined in a FAT library and associated with a specific pin. On Arduino, the default pins are D10 (CS), D11, (MOSI), D12 (MISO) and D13 (SCK). The FAT library you are using has probably defined the signals according to these defaults. The microSD shield, however, uses pin D8 for the CS signal. If this is left unchanged, the library will not work. You'll need to read your libraries documentation to find out where the signals are defined and change the definition accordingly. For example, in the SdFat library the definition is located in the file named 'ArduinoPins.h' and to change the signal definition on line number 518 from
#define SS_PIN 10
to
#define SS_PIN 8
Did you try this and the library still isn't working? The FAT library is probably using an internal SPI library. The SPI library expects D10 to be configured as an output (even if it's not being used!). In the setup section of your sketch configure D10 to be an output and you should be ready to go.
pinMode(10, OUTPUT);
Schematic and PCB Layout
For technical information including the schematic and design files, please see the product page. You may also want to add/review the comments on this page or do a google search for example projects that use the joystick shield
/*
ResponderEliminarcodigo:
-----------------------------------------------
Basic example reading and writing to the microSD shield
Based on the SdFat Library by Bill Greiman
SparkFun Electronics
This example creates a file (or opens it if the file already exists) named 'Test.txt.'
A string with the value 'Millis: (number of millis)' is appended to the end of the file.
After writing to the file, the contents of the entire file are printed to the serial port.
*/
//Add the SdFat Libraries
#include
#include
#include
//Create the variables to be used by SdFat Library
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
char name[] = "Test.txt"; //Create an array that contains the name of our file.
char contents[256]; //This will be a data buffer for writing contents to the file.
char in_char=0;
int index=0; //Index will keep track of our position within the contents buffer.
void setup(void)
{
Serial.begin(9600); //Start a serial connection.
pinMode(10, OUTPUT); //Pin 10 must be set as an output for the SD communication to work.
card.init(); //Initialize the SD card and configure the I/O pins.
volume.init(card); //Initialize a volume on the SD card.
root.openRoot(volume); //Open the root directory in the volume.
}
void loop(void){
file.open(root, name, O_CREAT | O_APPEND | O_WRITE); //Open or create the file 'name' in 'root' for writing to the end of the file.
sprintf(contents, "Millis: %d ", millis()); //Copy the letters 'Millis: ' followed by the integer value of the millis() function into the 'contents' array.
file.print(contents); //Write the 'contents' array to the end of the file.
file.close(); //Close the file.
file.open(root, name, O_READ); //Open the file in read mode.
in_char=file.read(); //Get the first byte in the file.
//Keep reading characters from the file until we get an error or reach the end of the file. (This will output the entire contents of the file).
while(in_char >=0){ //If the value of the character is less than 0 we've reached the end of the file.
Serial.print(in_char); //Print the current character
in_char=file.read(); //Get the next character
}
file.close(); //Close the file
delay(1000); //Wait 1 second before repeating the process.
}