The Arduino SD library: read and write files on a microSD card
SD is the library that ships with the Arduino IDE for SD and microSD cards on the SPI bus. It reads the FAT file system on the card, so a file you write from a sketch opens on a computer afterward, which is the whole point of a data logger: readings with timestamps in a CSV file, one line at a time.
Built in on Mokxi: #include <SD.h> and it builds, with nothing to install. Free, and no account needed.
What SD does
The module shares SPI with anything else on the bus: MOSI, MISO and SCK on the board鈥檚 SPI pins (11, 12 and 13 on an Uno) and its own chip select pin, which you pass to SD.begin(). Most microSD breakouts have a level shifter and a 3.3 V regulator, so they run from 5 V; the bare card itself would not survive 5 V signals.
File names are the old 8.3 kind, up to eight characters, a dot and three more, and only one file of the library鈥檚 buffer is kept in RAM, about 600 bytes on an Uno. Open, write, close: a file that is never closed or flushed may lose its last lines when power goes.
| Library Manager name | SD |
|---|---|
| Author | Arduino, SparkFun |
| Include | #include <SD.h> |
| Boards | every board Mokxi compiles for but ATtiny85 |
| In the simulator | Simulated: it runs against the part the way it does on the bench. |
| Parts it drives | microSD card module |
Install it
It builds on every board Mokxi compiles for but ATtiny85.
In Mokxi it is built in: #include <SD.h> and the sketch compiles, with no download, no zip and no Library Manager.
On a real board, install "SD" by Arduino, SparkFun from the Arduino IDE鈥檚 Library Manager (Sketch, Include Library, Manage Libraries), or add SD to lib_deps in PlatformIO. Mokxi follows the calls of release 1.3.0, so a sketch written here builds there too.
ESP32-C3 路 Arduino Uno 路 Arduino Nano 路 Arduino Mega 2560 路 Arduino Leonardo 路 SparkFun Pro Micro 路 Arduino Micro 路 ESP32-C6 路 Raspberry Pi Pico 路 Raspberry Pi Pico W 路 Seeed XIAO SAMD21 路 STM32 路 STM32 Nucleo-F411RE 路 STM32 Blue Pill 路 micro:bit V2 路 Arduino Uno R4 路 ESP32 DevKit 路 ESP32-S3-DevKitC-1 路 ESP8266 NodeMCU 路 Arduino Pro Mini 3.3 V 路 Seeed XIAO ESP32-C3 路 ESP32-C3 SuperMini 路 Seeed XIAO ESP32-C6 路 ESP32-DevKitC V4 路 LOLIN32 Lite 路 Wemos D1 R32 路 Cheap Yellow Display 路 TTGO T-Display 路 Seeed XIAO ESP32-S3 路 Arduino Nano ESP32 路 Arduino Nano RP2040 Connect 路 Seeed XIAO RP2040 路 Waveshare RP2040-Zero 路 Arduino Uno R4 WiFi
The calls you will use
In the order a sketch meets them.
SD.begin(4)
Starts the card with chip select on pin 4 (use 10, or whatever your module is wired to). Returns false when no card answers or it is not formatted FAT.
if (!SD.begin(4)) {
Serial.println("initialization failed!");
while (true);
}File dataFile = SD.open("datalog.txt", FILE_WRITE)
Opens a file for appending, creating it if needed. Without FILE_WRITE it opens for reading. The result is false if the open failed.
dataFile.println(reading)
Writes a line, with everything Print offers: print(), println() and write().
dataFile.close()
Writes what is buffered to the card and frees the file. dataFile.flush() writes without closing.
dataFile.available()
The bytes left to read; loop on it with dataFile.read() to copy a file to Serial.
SD.exists("datalog.txt")
True when the file or directory is on the card.
SD.remove("datalog.txt")
Deletes a file. SD.mkdir() and SD.rmdir() make and remove directories.
File entry = root.openNextFile()
Steps through a directory opened with SD.open("/"), as the listfiles example does; entry.name() and entry.isDirectory() describe each one.
Example: GPS logger
On the Arduino Mega 2560 R3. It runs in the browser exactly as printed.
// GPS logger: where the module is, once a second, written to a microSD card.
//
// The NEO-6M sends NMEA sentences on Serial1 (its TX to the Mega's RX1, pin
// 19). TinyGPS++ reads them a character at a time; each time a new position
// comes in, one line "time,latitude,longitude,km/h" is added to TRACK.CSV.
//
// The module is moving: its speed and course properties walk the position
// along a heading once a second. Change them and watch the numbers change.
// Click the GPS to take its fix away: the sentences keep coming, but empty,
// the position stops updating and its age() climbs, and nothing is logged
// until the fix is back.
//
// The track grows in the list beside the card. Click TRACK.CSV to download it;
// it opens in a spreadsheet or a mapping tool.
//
// The card is slow and the GPS does not wait. While the sketch is busy writing
// a sector, the bytes arriving on Serial1 pile up in a 64-byte buffer and the
// rest of the sentence is lost. So the lines are kept in memory and written
// to the card five at a time: four seconds in five nothing is missed.
const int LINES_PER_WRITE = 5;
const int LINE_LENGTH = 48;
char pending[LINES_PER_WRITE][LINE_LENGTH];
int pendingCount = 0;
#include <SPI.h>
#include <SD.h>
#include <TinyGPS++.h>
TinyGPSPlus gps;
const int CARD_CS = 53;
bool cardReady = false;
unsigned long lastWarning = 0;
uint32_t lastStamp = 0xFFFFFFFF;
void setup()
{
Serial.begin(9600);
Serial1.begin(9600);
cardReady = SD.begin(CARD_CS);
Serial.println(cardReady ? "Card ready." : "No card: positions go to the serial monitor only.");
if (cardReady) {
File file = SD.open("track.csv", FILE_WRITE);
if (file) {
if (file.size() == 0) {
file.println("time,lat,lng,kmh");
}
file.close();
}
}
}
// A Print that writes into one of the pending lines.
class LineBuffer : public Print {
public:
explicit LineBuffer(char *line) : line_(line) { line_[0] = 0; }
size_t write(uint8_t c) override
{
if (length_ + 1 >= LINE_LENGTH) {
return 0;
}
line_[length_++] = (char)c;
line_[length_] = 0;
return 1;
}
private:
char *line_;
int length_ = 0;
};
void printTwo(Print &out, int value)
{
if (value < 10) {
out.print('0');
}
out.print(value);
}
void writeLine(Print &out)
{
printTwo(out, gps.time.hour());
out.print(':');
printTwo(out, gps.time.minute());
out.print(':');
printTwo(out, gps.time.second());
out.print(',');
out.print(gps.location.lat(), 6);
out.print(',');
out.print(gps.location.lng(), 6);
out.print(',');
out.println(gps.speed.kmph(), 1);
}
void writePending()
{
File file = SD.open("track.csv", FILE_WRITE);
if (file) {
for (int i = 0; i < pendingCount; i++) {
file.print(pending[i]);
}
file.close();
}
pendingCount = 0;
}
void loop()
{
while (Serial1.available() > 0) {
gps.encode(Serial1.read());
}
if (!gps.location.isUpdated()) {
// No new position. After three seconds of that, say so, once every
// three seconds.
if (gps.location.age() > 3000 && millis() - lastWarning > 3000) {
Serial.println("No fix yet.");
lastWarning = millis();
}
return;
}
// RMC and GGA both carry the position, so it is updated twice a second;
// one line a second is enough, so the second one is skipped by its time.
uint32_t stamp = gps.time.value();
if (stamp == lastStamp) {
gps.location.lat(); // read, so isUpdated() waits for the next one
return;
}
lastStamp = stamp;
LineBuffer line(pending[pendingCount]);
writeLine(line);
Serial.print(pending[pendingCount]);
pendingCount++;
if (pendingCount == LINES_PER_WRITE) {
if (cardReady) {
writePending();
}
pendingCount = 0;
}
}Open it in the editor, wired and ready to run 路 GPS logger: the project page
Common errors and how to fix them
initialization failed!
Check the chip select pin passed to SD.begin() matches the wiring, that MOSI, MISO and SCK go to the board鈥檚 SPI pins, and that the card is formatted FAT16 or FAT32 (not exFAT, which large cards come with). Try another card: some cheap ones do not answer in SPI mode.
SD.open() returns false for a file name that looks right
The SD library only does 8.3 names, so "temperature.csv" fails while "temps.csv" works. A name with a folder needs the folder to exist first, created with SD.mkdir().
The last readings are missing from the file
Data stays in the 512-byte buffer until close() or flush(). Close the file after each write, or flush every few lines, so a reset or a pulled card does not lose them.
Projects that use SD
Built-in projects that include it. Each page has the full sketch and opens in the editor, already wired.
Try SD without installing anything
Open the example, change the sketch, and keep your version in a free account.