If you are building a data logger with a 5V board like the Arduino Uno R3, the default pick for your arduino sd card reader is the Adafruit MicroSD Breakout Board (Product ID: 254). Generic $2 modules lack the necessary 3.3V logic level shifters, and feeding 5V SPI signals directly into a 3.3V SD card will permanently brick the card's internal controller. This guide gives you the exact wiring, compilable code with robust error handling, and a decision matrix to ensure your SPI communication works on the first upload.

The Quick Decision: Which SD Module to Buy

Not all SD modules are wired identically, and the voltage logic of your microcontroller dictates which module you must buy. Use this decision tree to select the exact part number for your workbench.

Microcontroller Logic Level Board Examples Required Module Feature Concrete Pick (Part Number)
5V Logic Arduino Uno R3, Mega 2560, Nano (ATmega328P) Built-in 3.3V LDO and logic level shifter (e.g., CD4050) Adafruit 254 (or SparkFun DEV-13743)
3.3V Logic ESP32 DevKit, Arduino Zero, Raspberry Pi Pico Direct SPI passthrough (no level shifter needed) Generic Catalex MicroSD Adapter (saves ~$8)
Integrated / All-in-One Adafruit Feather M0 Adalogger Onboard microSD cage + RTC Adafruit 2796 (Feather M0 Adalogger)
Default Recommendation: If you are a beginner or using a standard 5V Arduino Uno, buy the Adafruit 254. It includes the CD4050 level shifter and a proper 3.3V voltage regulator, eliminating the most common cause of SD card failure: overvoltage on the MOSI and SCK lines.

Hardware Spec Sheet & Pin Mapping

The code and wiring below specifically target the Arduino Uno R3 paired with the Adafruit 254 MicroSD Breakout.

Parts List

  • MCU: Arduino Uno R3 (or equivalent ATmega328P clone) - ~$22.00
  • Module: Adafruit MicroSD Breakout Board (PID: 254) - ~$9.50
  • Media: SanDisk 16GB microSDHC (Class 10, formatted FAT32) - ~$8.00
  • Wiring: 22 AWG stranded silicone jumper wires

SPI Pin Mapping Table (Arduino Uno R3)

The Arduino SD.h library relies on the hardware SPI bus. You cannot move the MOSI, MISO, or SCK pins on an Uno without switching to a slower software SPI library. The Chip Select (CS) pin can be any digital pin, but Pin 10 is the hardware default and must be configured as an OUTPUT even if you use a different pin for CS.

SD Breakout Pin Arduino Uno R3 Pin Function
5V (or 3V) 5V Power input (Adafruit board has onboard LDO to drop to 3.3V)
GND GND Common ground reference
CLK (SCK) Pin 13 SPI Clock signal
DO (MISO) Pin 12 Master In, Slave Out (Data from SD to Arduino)
DI (MOSI) Pin 11 Master Out, Slave In (Data from Arduino to SD)
CS (Chip Select) Pin 10 Slave select (Active LOW)

Step-by-Step Wiring & Compilable Data Logging Code

Difficulty Rating: Beginner/Intermediate | Time: 15 Minutes
  1. Format the SD Card: Insert the microSD card into your PC. Use the official SD Memory Card Formatter to format it as FAT32. Do not use your OS's default formatter, as it often defaults to exFAT for cards over 32GB, which the Arduino SD library cannot read.
  2. Wire Power: Connect the breakout board's 5V pin to the Uno's 5V pin, and GND to GND. (If using a 3.3V native board like an ESP32, wire 3V3 to 3V3).
  3. Wire SPI: Connect CLK to 13, DO to 12, DI to 11, and CS to 10.
  4. Upload Code: Copy the code block below into your Arduino IDE. Ensure you have the built-in SD library installed via the Library Manager.
/*
  Target Board: Arduino Uno R3 (5V Logic)
  Module: Adafruit MicroSD Breakout (PID 254)
  Library: SD.h (Built-in Arduino IDE)
  Reference: https://docs.arduino.cc/language-reference/en/functions/communication/SD/
*/

#include <SPI.h>
#include <SD.h>

// Hardware SPI pins on Uno: MOSI=11, MISO=12, SCK=13
// We define CS explicitly. Pin 10 MUST be an output for hardware SPI to work on Uno.
#define CHIP_SELECT_PIN 10

File dataFile;
unsigned long logCount = 0;

void setup() {
  Serial.begin(9600);
  while (!Serial) { 
    delay(10); // Wait for serial port to connect (needed for native USB boards)
  }

  // Ensure hardware SS pin is output even if not used as CS
  pinMode(10, OUTPUT);

  Serial.print("Initializing SD card...");

  // SD.begin() initializes the SPI bus and the card.
  if (!SD.begin(CHIP_SELECT_PIN)) {
    Serial.println("initialization failed!");
    // Halt execution to prevent endless serial spam
    while (1) { 
      delay(1000); 
    } 
  }
  Serial.println("card initialized.");
}

void loop() {
  // Open the file. Note: only one file can be open at a time on standard SD.h
  dataFile = SD.open("datalog.txt", FILE_WRITE);

  if (dataFile) {
    int sensorValue = analogRead(A0); // Read dummy sensor on A0
    logCount++;
    
    dataFile.print("Log #");
    dataFile.print(logCount);
    dataFile.print(", Sensor: ");
    dataFile.println(sensorValue);
    
    dataFile.close(); // ALWAYS close the file to flush the buffer and update the FAT table
    Serial.println("Data logged successfully.");
  } else {
    Serial.println("error opening datalog.txt");
  }
  
  delay(2000); // Log every 2 seconds
}

Debugging: Exact Error Strings and the "First Three" Checks

When working with SPI storage, silent failures are rare; the library will tell you exactly what went wrong if you read the serial monitor. Here is how to debug the two most common exact error strings.

Error 1: "initialization failed!"

This string triggers when SD.begin() returns false. The Arduino cannot establish SPI communication with the card's controller. The first three things to check are:

  1. File System Format (Most Common): The Arduino SD.h library only supports FAT16 and FAT32. If you formatted a 64GB card on Windows, it is likely exFAT. Reformat using the SD Association Formatter and select FAT32 (cards >32GB may require a third-party tool like GUIFormat to force FAT32).
  2. Logic Level Overvoltage: If you are using a generic Catalex module on a 5V Uno, the 5V MOSI signal has likely tripped the SD card's internal overvoltage protection or fried the silicon. Swap to a module with a CD4050 level shifter (like the Adafruit 254) and test with a brand new SD card.
  3. Floating CS Pin: The Chip Select line must be pulled HIGH when the card is not being addressed. If your wiring is loose or the CS pin is floating, the card will ignore SPI clock pulses. Ensure Pin 10 is wired securely and defined as an OUTPUT in setup().

Error 2: "error opening datalog.txt"

This triggers when SD.open() fails. The card initialized fine, but the file system rejected the write request.

  • Root Directory Limit: The FAT32 root directory has a limit on the number of files (usually 512). If you are generating a new file every minute, you will hit this limit. Move your files into subdirectories.
  • File Name Constraints: The standard SD.h library strictly enforces the 8.3 filename format. "datalog.txt" is valid. "arduino_data_log_2026.txt" will fail because the prefix exceeds 8 characters. Use SdFat.h if you need long file name (LFN) support.
  • Unclosed Files: If your code crashed or reset before calling dataFile.close(), the file allocation table may be corrupted. Reformat the card and ensure .close() is called immediately after writing.

Extending or Simplifying the Build

Once you have basic logging working, you will quickly realize that "Log #1, Sensor: 512" is useless without a timestamp. The Arduino Uno has no internal real-time clock (RTC); it only knows milliseconds since boot.

How to Extend: Adding a DS3231 RTC

To add timestamps, wire a DS3231 RTC module to the Uno's I2C bus (SDA to A4, SCL to A5). Because I2C and SPI use different hardware peripherals on the ATmega328P, they will not conflict. Use the RTClib library to fetch the time, and write it to the SD card alongside your sensor data. Ensure the RTC module also has a 3.3V LDO if you are powering it from the 5V rail.

How to Simplify: The All-in-One Alternative

If managing SPI wiring, level shifters, and external RTC modules feels like a mess of jumper wires, simplify the build by changing the board entirely. The Adafruit Feather M0 Adalogger (PID 2796) is a native 3.3V Cortex-M0 board that features a built-in microSD card cage and a built-in PCF8523 RTC on the PCB. You eliminate the SPI wiring entirely, bypass the 5V-to-3.3V logic translation problem, and cut your BOM (Bill of Materials) down to just the board, a battery, and a sensor. For production or permanent field deployments, this is the superior engineering choice over an Uno with a breakout board.

Final Bench Tip: Always keep a known-good, pre-formatted 4GB or 8GB FAT32 microSD card in your toolbox. When a new module or code branch fails to initialize, test it with your "golden" card. If the golden card works, your code and wiring are fine, and the issue is isolated to the formatting or capacity of the card you were originally testing.