How to use SD Cards on the tinyCore
!!! warning “Are you skipping ahead?”
Make sure you have already installed the custom tinyCore board library and setup your Arduino IDE using the Get Started tutorial!
Okay, but first:
Why would I need an SD card?
Section titled “Why would I need an SD card?”Great question! The tinyCore has limited onboard memory, which is fine for running programs, but what if you want to save data for later? Maybe you’re logging sensor readings, saving user settings, or just want to store a bunch of cat pictures. (No judgment.)
An SD card gives your tinyCore external storage that persists even after power is removed. You can pop the card into your computer later to analyze, graph, or back up your data.
SD Card Slot Location photo goes here
Section titled “SD Card Slot Location photo goes here”The tinyCore has a built-in microSD card slot that supports cards up to 32GB (FAT32 formatted). That’s a lot of sensor readings.
Example 1: Initializing the SD Card
Section titled “Example 1: Initializing the SD Card”Before we can read or write anything, we need to make sure the SD card is actually there and working. This example will check for the card and print some info about it.
The Code
Section titled “The Code”Download or copy the example program below:
#include "FS.h"#include "SD.h"#include "SPI.h"
void setup() { Serial.begin(115200); while (!Serial) { delay(10); }
Serial.println("Initializing SD card...");
// Initialize the SD card if (!SD.begin()) { Serial.println("Card Mount Failed!"); Serial.println("Make sure a FAT32 formatted SD card is inserted."); return; }
Serial.println("SD Card mounted successfully!");
// Check what type of card is inserted uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) { Serial.println("No SD card attached"); return; }
Serial.print("SD Card Type: "); if (cardType == CARD_MMC) { Serial.println("MMC"); } else if (cardType == CARD_SD) { Serial.println("SDSC"); } else if (cardType == CARD_SDHC) { Serial.println("SDHC"); } else { Serial.println("UNKNOWN"); }
// Print the card size uint64_t cardSize = SD.cardSize() / (1024 * 1024); Serial.print("SD Card Size: "); Serial.print(cardSize); Serial.println(" MB");
Serial.println("You're all set!");}
void loop() { // Nothing to do here}What’s Happening?
Section titled “What’s Happening?”Let’s break down the key parts:
The Includes:
#include "FS.h" // File System library#include "SD.h" // SD card library#include "SPI.h" // SPI communication (how we talk to the SD card)Mounting the Card:
if (!SD.begin()) { Serial.println("Card Mount Failed!"); return;}The SD.begin() function initializes communication with the SD card. If it returns false, something went wrong. Usually the card isn’t inserted or isn’t formatted correctly.
Checking Card Type:
The SD.cardType() function tells you what kind of card you have. Most modern cards are SDHC (Secure Digital High Capacity).
Try It Out!
Section titled “Try It Out!”- Insert a FAT32 formatted microSD card into the tinyCore
- Flash the program to your board
- Open the Serial Monitor at
115200 baud - You should see your card info printed!
!!! tip “Formatting your SD Card”
Make sure your SD card is formatted as FAT32. On Windows, right-click the drive and select “Format”. On Mac, use Disk Utility. Cards larger than 32GB may need a third-party tool to format as FAT32.
Example 2: Writing to a File
Section titled “Example 2: Writing to a File”Now let’s actually save something! This example writes a simple message to a text file.
The Code
Section titled “The Code”#include "FS.h"#include "SD.h"#include "SPI.h"
void setup() { Serial.begin(115200); while (!Serial) { delay(10); }
// Initialize SD card if (!SD.begin()) { Serial.println("Card Mount Failed!"); return; } Serial.println("SD Card mounted!");
// Create and open a file for writing File myFile = SD.open("/hello.txt", FILE_WRITE);
if (myFile) { Serial.println("Writing to hello.txt...");
myFile.println("Hello from tinyCore!"); myFile.println("This is my first SD card file."); myFile.println("Pretty cool, right?");
myFile.close(); // Always close the file when done! Serial.println("Done! File saved."); } else { Serial.println("Error opening file for writing"); }}
void loop() { // Nothing here}What’s Happening?
Section titled “What’s Happening?”Opening a File:
File myFile = SD.open("/hello.txt", FILE_WRITE);This creates (or overwrites) a file called hello.txt in the root directory of the SD card. The / at the beginning means “root directory.”
Writing Data:
myFile.println("Hello from tinyCore!");Just like Serial.println(), but it writes to the file instead of the Serial Monitor. You can also use myFile.print() for text without a newline.
Closing the File:
myFile.close();This is crucial! If you don’t close the file, your data might not actually be saved. Always close your files.
Try It Out!
Section titled “Try It Out!”- Flash the program
- Open Serial Monitor to confirm it worked
- Remove the SD card and plug it into your computer
- Open
hello.txtand see your message!
Example 3: Reading from a File
Section titled “Example 3: Reading from a File”What good is saving data if you can’t view it? Let’s read that file we just created.
The Code
Section titled “The Code”#include "FS.h"#include "SD.h"#include "SPI.h"
void setup() { Serial.begin(115200); while (!Serial) { delay(10); }
// Initialize SD card if (!SD.begin()) { Serial.println("Card Mount Failed!"); return; } Serial.println("SD Card mounted!");
// Open the file for reading File myFile = SD.open("/hello.txt");
if (myFile) { Serial.println("Reading from hello.txt:"); Serial.println("------------------------");
// Read and print each character until end of file while (myFile.available()) { Serial.write(myFile.read()); }
Serial.println("------------------------"); myFile.close(); } else { Serial.println("Error opening file for reading"); Serial.println("Did you run the Write example first?"); }}
void loop() { // Nothing here}What’s Happening?
Section titled “What’s Happening?”Opening for Reading:
File myFile = SD.open("/hello.txt");Notice we didn’t specify FILE_WRITE this time. By default, SD.open() opens files in read mode.
Reading the Contents:
while (myFile.available()) { Serial.write(myFile.read());}myFile.available() returns how many bytes are left to read. myFile.read() grabs one byte at a time. We use Serial.write() instead of Serial.print() because we’re outputting raw bytes.
Try It Out!
Section titled “Try It Out!”Make sure you’ve run Example 2 first so there’s a file to read! Then flash this program and watch your message appear in the Serial Monitor.
Example 4: Appending to a File
Section titled “Example 4: Appending to a File”Sometimes you don’t want to overwrite a file—you want to add to it. This is especially useful for data logging.
The Code
Section titled “The Code”#include "FS.h"#include "SD.h"#include "SPI.h"
int bootCount = 0;
void setup() { Serial.begin(115200); while (!Serial) { delay(10); }
// Initialize SD card if (!SD.begin()) { Serial.println("Card Mount Failed!"); return; } Serial.println("SD Card mounted!");
// Open file in APPEND mode File logFile = SD.open("/bootlog.txt", FILE_APPEND);
if (logFile) { // Get a simple "timestamp" (milliseconds since boot) unsigned long timestamp = millis();
logFile.print("Device booted at "); logFile.print(timestamp); logFile.println(" ms");
logFile.close();
Serial.println("Boot logged successfully!"); } else { Serial.println("Error opening file"); }
// Now let's read back all the boot entries Serial.println("\nAll boot entries:"); Serial.println("------------------");
File readFile = SD.open("/bootlog.txt"); if (readFile) { while (readFile.available()) { Serial.write(readFile.read()); } readFile.close(); }}
void loop() { // Nothing here}What’s Happening?
Section titled “What’s Happening?”Append Mode:
File logFile = SD.open("/bootlog.txt", FILE_APPEND);FILE_APPEND is the magic word here. Instead of overwriting the file, new data gets added to the end.
Try It Out!
Section titled “Try It Out!”Flash this program, then press the reset button a few times. Each time the tinyCore boots, it adds a new entry to the log. Watch the list grow in the Serial Monitor!
Example 5: CSV Data Logging
Section titled “Example 5: CSV Data Logging”Our final example is probably the most useful imo; Logging sensor data in CSV format so you can analyze it in Excel, Google Sheets, or Python. We’ll log random “sensor” values to keep it simple, but you can swap in real sensor readings, or use the IMU!
The Code
Section titled “The Code”#include "FS.h"#include "SD.h"#include "SPI.h"
File dataFile;const char* FILENAME = "/sensor_data.csv";unsigned long lastLogTime = 0;const unsigned long LOG_INTERVAL = 1000; // Log every 1 secondint readingCount = 0;
void setup() { Serial.begin(115200); while (!Serial) { delay(10); }
// Initialize SD card if (!SD.begin()) { Serial.println("Card Mount Failed!"); return; } Serial.println("SD Card mounted!");
// Check if file exists to decide whether to write header bool fileExists = SD.exists(FILENAME);
// Open file for appending dataFile = SD.open(FILENAME, FILE_APPEND);
if (!dataFile) { Serial.println("Error opening data file!"); return; }
// Write CSV header if this is a new file if (!fileExists) { dataFile.println("timestamp_ms,reading_number,temperature,humidity,pressure"); Serial.println("Created new CSV file with header"); } else { Serial.println("Appending to existing CSV file"); }
Serial.println("\nLogging started! Data format:"); Serial.println("timestamp_ms,reading_number,temperature,humidity,pressure"); Serial.println("Press reset to stop logging.\n");}
void loop() { unsigned long currentTime = millis();
// Log data at the specified interval if (currentTime - lastLogTime >= LOG_INTERVAL) { lastLogTime = currentTime; readingCount++;
// Generate some fake sensor data (replace with real sensors!) float temperature = 20.0 + random(-50, 50) / 10.0; // 15.0 to 25.0 float humidity = 50.0 + random(-100, 100) / 10.0; // 40.0 to 60.0 float pressure = 1013.0 + random(-50, 50) / 10.0; // 1008.0 to 1018.0
// Create CSV line String dataLine = String(currentTime) + "," + String(readingCount) + "," + String(temperature, 2) + "," + String(humidity, 2) + "," + String(pressure, 2);
// Write to SD card dataFile.println(dataLine); dataFile.flush(); // Make sure data is written immediately
// Also print to Serial for monitoring Serial.println(dataLine);
// Stop after 60 readings (1 minute) to save your SD card if (readingCount >= 60) { dataFile.close(); Serial.println("\n60 readings logged. File closed."); Serial.println("Remove SD card and check your data!"); while (1) { delay(1000); } // Stop the loop } }}What’s Happening?
Section titled “What’s Happening?”Checking if File Exists:
bool fileExists = SD.exists(FILENAME);We only want to write the CSV header once. By checking if the file already exists, we can skip the header on subsequent runs.
CSV Header:
dataFile.println("timestamp_ms,reading_number,temperature,humidity,pressure");This creates the column names for your spreadsheet. Each column is separated by a comma.
Building the Data Line:
String dataLine = String(currentTime) + "," + String(readingCount) + "," + String(temperature, 2) + "," + ...We build a comma-separated string with all our values. The String(value, 2) syntax limits floats to 2 decimal places.
Flushing the Buffer:
dataFile.flush();This forces the data to actually write to the SD card immediately, rather than waiting in a buffer. Important for data logging—you don’t want to lose data if power is cut!
Try It Out!
Section titled “Try It Out!”- Flash the program
- Let it run for about a minute (60 readings)
- Remove the SD card and open
sensor_data.csvin Excel or Google Sheets - Try graphing your data!
CSV Data in Spreadsheet example goes here
Example 6: Delete and List Files
Section titled “Example 6: Delete and List Files”Sometimes you need to clean up. This example shows how to list all files in a directory and delete files you don’t need.
The Code
Section titled “The Code”#include "FS.h"#include "SD.h"#include "SPI.h"
void setup() { Serial.begin(115200); while (!Serial) { delay(10); }
if (!SD.begin()) { Serial.println("Card Mount Failed!"); return; } Serial.println("SD Card mounted!\n");
// List all files in root directory Serial.println("Files on SD card:"); Serial.println("------------------"); listDir("/"); Serial.println("------------------\n");
// Delete a specific file (uncomment to use) // deleteFile("/hello.txt");
// Delete all .txt files (uncomment to use) // deleteAllTxtFiles("/");}
void listDir(const char* dirname) { File root = SD.open(dirname);
if (!root || !root.isDirectory()) { Serial.println("Failed to open directory"); return; }
File file = root.openNextFile(); while (file) { if (file.isDirectory()) { Serial.print(" [DIR] "); Serial.println(file.name()); } else { Serial.print(" "); Serial.print(file.name()); Serial.print(" ("); Serial.print(file.size()); Serial.println(" bytes)"); } file = root.openNextFile(); }}
void deleteFile(const char* path) { Serial.print("Deleting file: "); Serial.println(path);
if (SD.remove(path)) { Serial.println("File deleted successfully"); } else { Serial.println("Delete failed - file may not exist"); }}
void deleteAllTxtFiles(const char* dirname) { File root = SD.open(dirname);
if (!root || !root.isDirectory()) { return; }
File file = root.openNextFile(); while (file) { if (!file.isDirectory()) { String filename = String(file.name()); if (filename.endsWith(".txt")) { String fullPath = String(dirname) + filename; file.close(); // Close before deleting SD.remove(fullPath.c_str()); Serial.print("Deleted: "); Serial.println(fullPath); } } file = root.openNextFile(); }}
void loop() { // Nothing here}Key Functions
Section titled “Key Functions”| Function | Description |
|---|---|
SD.exists(path) | Check if a file exists |
SD.remove(path) | Delete a file |
SD.mkdir(path) | Create a directory |
SD.rmdir(path) | Remove an empty directory |
file.isDirectory() | Check if entry is a folder |
file.name() | Get the filename |
file.size() | Get file size in bytes |
Quick Reference
Section titled “Quick Reference”Here’s a cheat sheet of the most common SD card operations:
// === SETUP ===#include "FS.h"#include "SD.h"#include "SPI.h"
SD.begin(); // Initialize SD card
// === FILE OPERATIONS ===SD.open("/file.txt", FILE_WRITE); // Create/overwrite fileSD.open("/file.txt", FILE_APPEND); // Append to fileSD.open("/file.txt"); // Open for reading (default)
// === WRITING ===file.print("text"); // Write without newlinefile.println("text"); // Write with newlinefile.flush(); // Force write to cardfile.close(); // Close file (important!)
// === READING ===file.available(); // Bytes remaining to readfile.read(); // Read one bytefile.readString(); // Read entire file as String
// === FILE MANAGEMENT ===SD.exists("/file.txt"); // Check if file existsSD.remove("/file.txt"); // Delete fileSD.mkdir("/folder"); // Create directorySD.rmdir("/folder"); // Remove empty directoryTroubleshooting
Section titled “Troubleshooting”??? question “Card Mount Failed!”
- Make sure the SD card is properly inserted
- Ensure the card is formatted as FAT32
- Try a different SD card (some cards are incompatible)
- Check that your card is 32GB or smaller
??? question “File won’t open for writing”
- Check that the filename starts with
/ - Ensure you’re not trying to write to a read-only card
- Make sure the card isn’t full
??? question “Data seems corrupted or incomplete”
- Always call
file.flush()after writing important data - Always call
file.close()when done - Don’t remove the SD card while the program is running
??? question “I can write but not read”
- Make sure you closed the file after writing before trying to read
- Check that you’re using the correct filename (case-sensitive!)
Final Thoughts
Section titled “Final Thoughts”You now have all the tools you need to save and load data on your tinyCore! Some project ideas:
- Temperature logger – Log readings every minute for a week
- GPS tracker – Save coordinates to review your route later
- Game save system – Save high scores or game state
- Configuration file – Store settings that persist across reboots
The possibilities are endless. Now go fill up that SD card!
!!! warning “Having trouble?”
Send us an email at support@mr.industries or join our Discord for help!