Components · 14 min read

Arduino SD Card Module: CSV Data Logger with RTC Timestamps

Arduino SD card module guide: SPI wiring, FAT32 formatting, CSV sensor logging with RTC timestamps, ESP32 SD_MMC, Pico and fixing initialization failed.

An Arduino SD card module is a small, cheap board that lets you save the temperature, humidity, distance or location readings from your sensors as files on a micro SD card. It's the easiest way to build a data logger that measures for days or even months without a computer attached: the Arduino takes the readings, the module writes each one as a new line in a CSV file on the card, and you plug the card into your computer and chart the data in Excel. The module connects over SPI and works with just a few lines of code using the SD library that ships with the Arduino IDE.

In this guide you'll learn step by step how the module works, the difference between 5V and 3.3V modules, the SPI wiring for Arduino Uno, Mega, ESP32 and Raspberry Pi Pico, how to format the card as FAT32, how to write and read files, and how to keep a date- and time-stamped CSV log with an RTC module. We also cover the ESP32's fast SD_MMC interface, using an SD card on the Pico with MicroPython, and how to fix the all-too-common "initialization failed" error.

Arduino micro SD card module
Micro SD card module: card slot, 3.3 V regulator, level shifter chip and 6 pins (GND, VCC, MISO, MOSI, SCK, CS).

What is an SD card module and how does it work?

SD cards run on 3.3 V and can communicate in two ways: the fast native SD mode and the simpler SPI mode. Microcontrollers like the Arduino talk to the card over SPI. The module's job is to make the two electrically compatible: its regulator drops 5 V to the 3.3 V the card needs, and a level shifter chip (usually a 74LVC125A) converts the Arduino's 5 V signals to 3.3 V the card can handle. Inside, the card is made of 512-byte blocks, while ideas like files, folders and file names are handled by the FAT file system. The SD library does all this heavy lifting for you; you just open a file, write to it and close it.

FeatureValue
InterfaceSPI (MISO, MOSI, SCK, CS)
Supply (micro SD module with regulator)4.5–5.5 V, 3.3 V regulator on board
Signal level3.3 V and 5 V compatible (thanks to the level shifter)
Supported cardsmicro SD (up to 2 GB) and micro SDHC (4–32 GB)
File systemFAT16 or FAT32
CurrentA few mA when idle, up to 100 mA while writing
File name (Arduino SD library)8.3 format: up to 8 characters for the name + 3 for the extension

5V vs 3.3V SD card modules: which one should you buy?

Module typeSupplySignal levelBest for
Micro SD module with regulator (74LVC125A)5 V3.3 V and 5 VUno, Nano, Mega; ESP32 and Pico when powered from 5 V
Full-size SD card module (+5 and +3.3 pins)5 V or 3.3 VMost have no real level shifter3.3 V boards; unreliable with 5 V signals
Micro SD module without regulator (bare slot)3.3 V only3.3 V onlyESP32, Raspberry Pi Pico
Boards with a built-in slot (like the ESP32-CAM)From the board3.3 VUsed directly with SD_MMC

If you're using an Arduino Uno, Nano or Mega, choose a micro SD module with a regulator and level shifter like the one in the photo, and connect VCC to the 5V pin. The ESP32 and Pico run on 3.3 V; you can use the same module with them too, but you should still power it from 5 V (VIN on the ESP32, VBUS on the Pico when it's powered over USB). Because the level shifter is powered at 3.3 V, the MISO signal going back to the microcontroller is already 3.3 V. Modules without a regulator connect straight to the 3V3 pin; if you have to use one with a 5 V Arduino, put a 1 kΩ + 2 kΩ voltage divider on each of the MOSI, SCK and CS lines, or use a logic level converter. You can check the resistor values with the voltage divider calculator.

The most common mistake is powering a micro SD module that has a regulator from 3.3 V. The AMS1117 regulator on the module drops about 1 V, so only around 2.3 V reaches the card, and it will usually report initialization failed. The opposite is just as bad: applying 5 V to the pins of a module without a regulator can permanently damage the card. Read the labels on the module and check whether it has a regulator before you connect it.

SD card module wiring: SPI pins

Module pinArduino Uno / NanoArduino MegaESP32Raspberry Pi Pico
GNDGNDGNDGNDGND
VCC5V5VVIN (5 V)VBUS (5 V)
MISOD12D50GPIO 19GP16
MOSID11D51GPIO 23GP19
SCKD13D52GPIO 18GP18
CSD10D53GPIO 5GP17

MISO, MOSI and SCK must go to the hardware SPI pins on every board; CS (chip select) can be any digital pin, as long as you pass the same number to SD.begin() in your code. On ready-made shields CS is usually D4 or D10. You can connect several devices to the same SPI bus: they all share MISO, MOSI and SCK, and each one gets its own CS pin. We explain how SPI works in detail in SPI communication. To see where the pins are on your board, use the pinout tool.

Preparing the SD card: formatting as FAT32

  • Card size: pick a micro SDHC card between 4 and 32 GB. The Arduino SD library supports SDSC (up to 2 GB) and SDHC cards; SDXC cards of 64 GB and above come formatted as exFAT, which this library can't read.
  • File system: format the card as FAT32 (FAT16 also works on cards of 2 GB or less). NTFS and exFAT won't work.
  • Windows: right-click the card in File Explorer, choose Format, then select FAT32 as the file system and the default allocation unit size. Windows doesn't offer FAT32 for cards larger than 32 GB.
  • macOS: select the card in Disk Utility, click Erase, and choose MS-DOS (FAT) as the format and Master Boot Record as the scheme.
  • The safest option: the free SD Card Formatter tool from the SD Association formats the card to the official standard and clears up most strange errors.
  • Single partition: the card should have just one partition. Cards previously used in a Raspberry Pi or a camera may still have several partitions; delete them before formatting.
The SD library on the Arduino Uno only handles short file names in the 8.3 format: a name of up to 8 characters and an extension of up to 3, with no spaces or special characters. DATA.CSV or LOG01.TXT will work, but a long name like temperature-log.csv makes SD.open() fail silently. On the ESP32 and Pico you can use long file names without any problem.

Step-by-step first test: writing to and reading from the SD card

  1. Insert the formatted card into the module and wire the module to the Arduino using the table above.
  2. The SD library comes with the Arduino IDE, so there's nothing to install; you only add SPI.h and SD.h at the top of the sketch.
  3. Upload the code below and open the Serial Monitor at 9600 baud.
  4. If you see "tamam" (OK) and the file's contents, the card and wiring are fine. A new line is added to TEST.TXT every time the board starts.
  5. When you take the card out and plug it into your computer, you can open TEST.TXT in any text editor.
Arduino (C++)
1#include <SPI.h>
2#include <SD.h>
3
4const int CS_PIN = 10;                           // modülün CS pini / module CS pin
5
6void setup() {
7  Serial.begin(9600);
8  Serial.print(F("SD kart baslatiliyor... "));
9  if (!SD.begin(CS_PIN)) {                       // kart başlatılamadı / card init failed
10    Serial.println(F("initialization failed"));
11    while (true);                                // burada dur / stop here
12  }
13  Serial.println(F("tamam"));
14
15  File dosya = SD.open("TEST.TXT", FILE_WRITE);  // yoksa oluşturur, sona ekler / creates it, appends
16  if (dosya) {
17    dosya.println(F("Merhaba BlueGrays!"));
18    dosya.close();                               // tamponu karta yazar / flushes the buffer to the card
19    Serial.println(F("TEST.TXT yazildi"));
20  } else {
21    Serial.println(F("TEST.TXT acilamadi"));     // dosya açılamadı / could not open file
22  }
23
24  dosya = SD.open("TEST.TXT");                   // okumak için aç / open for reading
25  if (dosya) {
26    Serial.print(F("Boyut (bayt): "));
27    Serial.println(dosya.size());
28    while (dosya.available()) {
29      Serial.write(dosya.read());                // içeriği seri porta yaz / print contents to serial
30    }
31    dosya.close();
32  }
33}
34
35void loop() {
36}
FunctionWhat it does
SD.begin(cs)Initialises the card; returns true on success
SD.open("NAME.TXT", FILE_WRITE)Opens the file for writing; creates it if missing, appends to the end if it exists
SD.open("NAME.TXT")Opens the file for reading (FILE_READ)
file.print() / println()Writes text or a number to the file
file.flush()Writes the buffered data to the card without closing the file
file.close()Writes any remaining data to the card and closes the file
file.available() / read()Checks whether there are bytes left to read / reads one byte
SD.exists() / SD.remove() / SD.mkdir()Checks whether a file exists / deletes a file / creates a folder

Logging sensor data to CSV with RTC timestamps

The most practical file format for a data logger is CSV (comma-separated values): each line is one measurement, each column is one value, and the first line holds the column headings. To know when each reading was taken, you should add the date and time to every line. millis() only tells you how long the Arduino has been running and resets every time it restarts, so we use an RTC (real-time clock) module with a coin cell. The RTC keeps counting even while the Arduino is switched off. The DS1307 is cheap but can drift a few seconds a day, while the DS3231 drifts only about a minute a year thanks to its temperature-compensated crystal. We compare them in RTC clock modules.

DS1307 RTC clock module
The DS1307 RTC module connects over I2C (SDA, SCL) and keeps time on its coin cell even when the Arduino is off.

The circuit: the SD module goes to the SPI pins as in the table above, the RTC module to the I2C bus (on the Uno SDA = A4, SCL = A5, VCC = 5V), and the data pin of the DHT22 sensor to D2; if you're using a bare sensor, add a 10 kΩ resistor between the data pin and VCC. Install the RTClib (Adafruit) and DHT sensor library libraries from the Library Manager. For details on wiring the sensor, see DHT11 and DHT22 temperature and humidity sensor.

Arduino (C++)
1#include <SPI.h>
2#include <SD.h>
3#include <Wire.h>
4#include <RTClib.h>
5#include <DHT.h>
6
7const int CS_PIN = 10;                           // SD modülü CS / SD module CS
8const unsigned long ARALIK = 10000;              // 10 saniyede bir kayıt / log every 10 seconds
9
10RTC_DS1307 rtc;                                  // DS3231 için RTC_DS3231 / RTC_DS3231 for a DS3231
11DHT dht(2, DHT22);                               // veri pini D2, DHT11 de olur / data pin D2, DHT11 also works
12unsigned long sonKayit = 0;
13
14void setup() {
15  Serial.begin(9600);
16  dht.begin();
17  if (!rtc.begin()) {
18    Serial.println(F("RTC bulunamadi"));         // RTC yok / RTC not found
19    while (true);
20  }
21  if (!rtc.isrunning()) {                        // saat hiç kurulmamış / clock never set
22    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));  // derleme anına kur / set to compile time
23  }
24  if (!SD.begin(CS_PIN)) {
25    Serial.println(F("SD kart hatasi"));         // SD başlatılamadı / SD init failed
26    while (true);
27  }
28  if (!SD.exists("VERI.CSV")) {                  // yeni dosyaya başlık satırı / header row for a new file
29    File f = SD.open("VERI.CSV", FILE_WRITE);
30    if (f) {
31      f.println(F("tarih,saat,sicaklik_C,nem_yuzde"));
32      f.close();
33    }
34  }
35}
36
37void loop() {
38  if (millis() - sonKayit < ARALIK) return;      // zamanı gelmedi / not time yet
39  sonKayit = millis();
40
41  float t = dht.readTemperature();
42  float h = dht.readHumidity();
43  if (isnan(t) || isnan(h)) {                    // sensör okunamadı / sensor read failed
44    Serial.println(F("DHT okuma hatasi"));
45    return;
46  }
47
48  DateTime simdi = rtc.now();
49  char ts[8], hs[8], satir[48];
50  dtostrf(t, 1, 1, ts);                          // AVR'de sprintf %f desteklemez / AVR sprintf has no %f
51  dtostrf(h, 1, 1, hs);
52  snprintf(satir, sizeof(satir), "%04d-%02d-%02d,%02d:%02d:%02d,%s,%s",
53           simdi.year(), simdi.month(), simdi.day(),
54           simdi.hour(), simdi.minute(), simdi.second(), ts, hs);
55
56  File f = SD.open("VERI.CSV", FILE_WRITE);      // sona ekle / append
57  if (f) {
58    f.println(satir);
59    f.close();                                   // her kayıtta kapat: güç kesilse de veri kalır / close every time so data survives a power cut
60    Serial.println(satir);
61  } else {
62    Serial.println(F("VERI.CSV acilamadi"));
63  }
64}

Every 10 seconds the code reads temperature and humidity, gets the date and time from the RTC and appends a line like 2026-11-04,14:05:30,23.5,45.2 to the end of VERI.CSV. Because the file is closed after every entry, the data recorded so far survives even a power cut. On AVR boards sprintf() doesn't support floating-point numbers (%f), so the values are first converted to text with dtostrf(). Since the timing uses millis() instead of delay(), you can do other things in loop() as well. If the RTC has never been set, the clock is set to the moment the code was compiled. If you're using a DS3231, just write RTC_DS3231 instead of RTC_DS1307 and check lostPower() instead of isrunning().

How much space does it take? One line is about 31 bytes. Logging every 10 seconds gives 8,640 lines a day, or about 260 KB; over a year that's roughly 95 MB. So at this rate an 8 GB card lasts for decades. The real limit isn't the card but the power supply: if you'll run the project on batteries, work out how long they'll last beforehand with the battery life calculator.

Opening the SD card data in Excel

  1. Disconnect the Arduino's power, take the card out of the module and plug it into your computer.
  2. Copy VERI.CSV to your computer; working on a copy is safer than working directly on the card.
  3. Open a blank workbook in Excel and choose the file via Data > From Text/CSV.
  4. In the preview window select Comma as the delimiter and click Load; the columns are split into date, time, temperature and humidity.
  5. Select the temperature and humidity columns and add a line chart from the Insert tab to see how they change over time.
If your Excel uses a comma as the decimal separator (as with Turkish, German, French, Spanish or Italian settings), a value like 23.5 may not be recognised as a number and can show up as text, a date or a wrong number. There are two fixes: click Transform Data during the import and convert the temperature and humidity columns to decimal numbers using the English (United States) locale, or change the code as below so it uses a semicolon as the separator and a comma as the decimal mark. With the second option you can simply double-click the file and Excel splits the columns by itself.
Arduino (C++)
1// Excel'de ondalık ayırıcı virgülse / if Excel uses a decimal comma
2// Bu iki fonksiyonu kayıt kodunda loop()'un üstüne ekle / add these two functions above loop() in the logging sketch
3void noktayiVirgulYap(char *s) {
4  for (; *s; s++) {
5    if (*s == '.') *s = ',';                     // 23.5 -> 23,5
6  }
7}
8
9void excelSatiri(char *satir, size_t boyut, const DateTime &simdi, char *ts, char *hs) {
10  noktayiVirgulYap(ts);
11  noktayiVirgulYap(hs);
12  snprintf(satir, boyut, "%04d-%02d-%02d;%02d:%02d:%02d;%s;%s",   // ayırıcı ; / separator ;
13           simdi.year(), simdi.month(), simdi.day(),
14           simdi.hour(), simdi.minute(), simdi.second(), ts, hs);
15}
16
17// loop() içinde snprintf satırının yerine / in loop(), replace the snprintf line with:
18//   excelSatiri(satir, sizeof(satir), simdi, ts, hs);
19// Başlık satırı da / header row too: tarih;saat;sicaklik_C;nem_yuzde

If you'd rather stream readings live into Excel while the Arduino is connected to your computer, without removing the card, take a look at the Data Streamer and PLX-DAQ methods in sending Arduino data to Excel. An SD card, on the other hand, is essential for projects that run out in the field, far from any computer.

Using an SD card with the ESP32 (SPI)

The ESP32 Arduino core comes with its own SD library and it works almost the same way, but there are three important differences: file paths start with / ("/olcumler.csv"), long file names are allowed, and FILE_WRITE overwrites the file from scratch. To append you must use FILE_APPEND; if you port the Uno code unchanged, your old data is wiped every time the board starts. The default SPI pins are SCK 18, MISO 19 and MOSI 23, and GPIO 5 is usually used for CS. You can also read the card type and size with a single line:

Arduino (C++)
1#include <SPI.h>
2#include <SD.h>
3
4const int CS_PIN = 5;                            // VSPI: SCK 18, MISO 19, MOSI 23 / default VSPI pins
5const char *DOSYA = "/olcumler.csv";             // yol / ile başlar / path starts with /
6
7void setup() {
8  Serial.begin(115200);
9  if (!SD.begin(CS_PIN)) {                       // varsayılan SPI hızı 4 MHz / default SPI clock 4 MHz
10    Serial.println("Card Mount Failed");
11    return;
12  }
13  uint8_t tur = SD.cardType();
14  if (tur == CARD_NONE) {
15    Serial.println("Kart takili degil");         // kart yok / no card inserted
16    return;
17  }
18  Serial.printf("Kart tipi: %s\n", tur == CARD_SDHC ? "SDHC" : (tur == CARD_SD ? "SDSC" : "MMC"));
19  Serial.printf("Boyut: %llu MB\n", SD.cardSize() / (1024 * 1024));
20
21  if (!SD.exists(DOSYA)) {                       // uzun dosya adı serbest / long file names are fine
22    File f = SD.open(DOSYA, FILE_WRITE);         // FILE_WRITE dosyayı sıfırlar / FILE_WRITE truncates
23    if (f) {
24      f.println("sure_s,isik_ham");
25      f.close();
26    }
27  }
28}
29
30void loop() {
31  File f = SD.open(DOSYA, FILE_APPEND);          // ESP32'de sona ekleme / append on ESP32
32  if (f) {
33    f.printf("%lu,%d\n", millis() / 1000, analogRead(34));   // GPIO34'teki LDR / LDR on GPIO34
34    f.close();
35  }
36  delay(5000);
37}

The code logs the raw value (0–4095) of an LDR connected to GPIO 34 every 5 seconds. On the ESP32 you don't even need an RTC module for real time: you can connect to Wi-Fi, get the time from an NTP server and add a timestamp to every line with getLocalTime(). If your wires are long or the card is unreliable, lower the SPI speed: SD.begin(5, SPI, 1000000) starts the card at 1 MHz. In a battery-powered logger, using deep sleep between readings multiplies battery life.

ESP32 SD_MMC: the fast SD interface and the ESP32-CAM

Besides SPI, the ESP32 also has an SDMMC controller that speaks the card's own fast protocol. In 4-bit mode the SD_MMC library is several times faster than SPI, which makes it ideal for large files such as photos or audio. On the classic ESP32 the pins are fixed: CLK GPIO 14, CMD GPIO 15, D0 GPIO 2, D1 GPIO 4, D2 GPIO 12, D3 GPIO 13, and these lines need 10 kΩ pull-up resistors. In 1-bit mode only CLK, CMD and D0 are used; it's slower, but GPIO 4, 12 and 13 are freed up. The micro SD slot on the ESP32-CAM board is wired to these pins, and using 1-bit mode stops the flash LED on GPIO 4 from blinking every time the card is accessed.

Arduino (C++)
1#include "FS.h"
2#include "SD_MMC.h"
3
4void setup() {
5  Serial.begin(115200);
6  // 1-bit mod: yalnız CLK 14, CMD 15, D0 2 / 1-bit mode: only CLK 14, CMD 15, D0 2
7  if (!SD_MMC.begin("/sdcard", true)) {
8    Serial.println("SD_MMC baglanamadi");        // kart bağlanamadı / mount failed
9    return;
10  }
11  Serial.printf("Bos alan: %llu MB\n", (SD_MMC.totalBytes() - SD_MMC.usedBytes()) / (1024 * 1024));
12
13  File f = SD_MMC.open("/gunluk.txt", FILE_APPEND);
14  if (f) {
15    f.println("ESP32-CAM acildi");               // açılış kaydı / boot log line
16    f.close();
17  }
18}
19
20void loop() {
21}
GPIO 12 is a strapping pin that sets the flash memory voltage while the ESP32 boots. In 4-bit mode the pull-up resistor on the D2 line can stop some modules from booting at all; if that happens, use 1-bit mode. On newer chips such as the ESP32-S3 you can choose the pins freely with SD_MMC.setPins().

SD card with the Raspberry Pi Pico (MicroPython)

In MicroPython, SD cards are handled by the sdcard.py driver from micropython-lib. If you have mpremote on your computer, install it with mpremote mip install sdcard, or copy the file to the Pico with Thonny. The card is mounted as the /sd folder with os.mount(), and after that you use it with ordinary Python file commands (open, write). The code below reads the Pico's built-in temperature sensor and writes a CSV line every 10 seconds. If you're new to the Pico, read What is the Raspberry Pi Pico? first.

MicroPython
1from machine import Pin, SPI, ADC
2import os, time
3import sdcard                                    # micropython-lib sürücüsü / micropython-lib driver
4
5spi = SPI(0, baudrate=1000000, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
6sd = sdcard.SDCard(spi, Pin(17, Pin.OUT))        # CS = GP17; kart yoksa OSError / OSError if no card
7os.mount(os.VfsFat(sd), "/sd")                   # kart artık /sd klasörü / the card is now the /sd folder
8
9sensor = ADC(4)                                  # dahili sıcaklık sensörü / internal temperature sensor
10DOSYA = "/sd/pico_log.csv"
11
12if "pico_log.csv" not in os.listdir("/sd"):      # yeni dosyaya başlık / header for a new file
13    with open(DOSYA, "w") as f:
14        f.write("tarih,saat,sicaklik_C\n")
15
16while True:
17    v = sensor.read_u16() * 3.3 / 65535
18    t = 27 - (v - 0.706) / 0.001721              # RP2040 veri sayfası formülü / RP2040 datasheet formula
19    y, ay, g, sa, dk, sn = time.localtime()[:6]
20    with open(DOSYA, "a") as f:                  # "a" = sona ekle / append
21        f.write("{:04d}-{:02d}-{:02d},{:02d}:{:02d}:{:02d},{:.1f}\n".format(y, ay, g, sa, dk, sn, t))
22    print(sa, dk, sn, t)
23    time.sleep(10)                               # 10 saniye bekle / wait 10 seconds

The Pico has no battery-backed clock: when it's connected to Thonny its clock is set automatically from your computer, but running on its own it starts from 1 January 2021. For accurate timestamps you can add a DS3231 module, or on a Pico W get the time from the internet via NTP. If no card is inserted or the wiring is wrong, the driver raises OSError: no SD card. If you program the Pico with the Arduino IDE (the arduino-pico core), you can use the same SD library approach; the default SPI0 pins match the table, and you just write SD.begin(17) for CS.

Preventing data loss: power, write frequency and card lifespan

  • Nothing reaches the card without close() or flush(): the library first collects data in a 512-byte buffer. If you keep the file open all the time, call flush() every few lines.
  • Don't remove the card while it's writing: pulling the card or cutting the power mid-write can corrupt the file system. Adding a button that stops logging and an LED that shows logging is active is a good habit.
  • Keep the power supply solid: the card draws up to 100 mA while writing. A 10–100 µF capacitor between the module's VCC and GND reduces voltage dips and random write errors.
  • Don't open and close too often: if you take dozens of readings per second, opening and closing the file for every line is slow; buffer the readings in RAM and write them in batches of 10–20 lines.
  • Card lifespan: SD cards spread writes across different cells internally (wear leveling), so a logger writing a few lines a minute won't wear out the card for years. Still, choose a card from a well-known brand.
  • Split the file: opening a separate file for each day (e.g. 20261104.CSV) keeps files small, and if something goes wrong you only lose that day's data.

SD card module project ideas

  • Weather station logger: record temperature, humidity and pressure every hour with a BME280 and chart a whole season.
  • Plant watering log: record soil moisture sensor readings and the moments the pump runs to study your watering pattern.
  • GPS route logger: save your cycling or hiking route as CSV with a NEO-6M GPS, then show it on a map.
  • Card-based entry log: write every card scanned with an RFID RC522 to a file along with the date and time.
  • Energy use tracker: measure and log the current a device draws throughout the day with an ACS712 current sensor.
  • MP3 player: the DFPlayer Mini plays audio files from its own micro SD slot; the Arduino just sends commands.

Common mistakes and how to fix them

SymptomLikely causeFix
"initialization failed"No card inserted, wrong wiring, or the CS pin doesn't match the codeCheck that MISO, MOSI and SCK are on the hardware SPI pins and that the CS number in SD.begin() is correct
"initialization failed" (wiring is correct)A module with a regulator is powered from 3.3 V, or the supply is weakPower the module from 5 V, shorten the wires, add a capacitor between VCC and GND
"Could not find FAT16/FAT32 partition"The card is formatted as exFAT or NTFS; an SDXC card of 64 GB or moreFormat a card of up to 32 GB as FAT32
The file won't open ("error opening")The file name breaks the 8.3 rule, or the previous file wasn't closedUse a short name like VERI.CSV and close every file with close()
The file is empty or the last lines are missingPower was cut before close() or flush() was calledClose the file after every entry or call flush() regularly
The SD card stops working when another SPI device is addedThe other device's CS is floating, or the module doesn't release the MISO lineSet every CS pin to OUTPUT and HIGH at the start of setup(); if needed, use a module that releases MISO
The Uno freezes or behaves strangelyOut of RAM: the SD library uses about 650 bytes and an OLED display buffer 1 KBWrap text in F(); if needed, move to a Mega or an ESP32
On the ESP32, old data is erased at every bootThe file is opened with FILE_WRITEUse FILE_APPEND to add to the end
"Card Mount Failed" on the ESP32Power, wiring, a high SPI speed, or missing pull-ups with SD_MMCLower the SPI speed to 1 MHz; try 1-bit mode with SD_MMC
The quickest way to track down the problem is the File > Examples > SD > CardInfo example in the Arduino IDE. If it shows the card type (SD1, SD2 or SDHC) but can't find a partition, the problem is the formatting; if it can't even show the card type, check the wiring and the power supply.

An SD card module turns a project that only measures into a device that keeps records, and a few days of real data tell you far more than a single reading ever could. In the BlueGrays app you'll find the ESP32 temperature logger with DHT22, DS1307 RTC, micro SD and OLED, RTC-based digital clocks and temperature and humidity projects, all with step-by-step circuit diagrams and code.

Frequently asked questions

What size SD card can an Arduino use?

The standard Arduino SD library supports SDSC and SDHC cards formatted as FAT16 or FAT32, which means cards up to 32 GB. SDXC cards of 64 GB and above come with exFAT and can't be read by this library. For a data logger, a 4–16 GB card is more than enough.

Why does my SD card say "initialization failed"?

The most common causes are a wrong CS pin, MISO, MOSI or SCK not connected to the hardware SPI pins, a card formatted as something other than FAT32, and a regulated module powered from 3.3 V. Run the CardInfo example first, then try shorter wires and powering the module from 5 V.

Does an SD card module work with 3.3V?

Modules without a regulator run directly on 3.3 V and suit the ESP32 or Pico. Micro SD modules with a regulator and level shifter need 4.5–5.5 V; powered from 3.3 V, the card doesn't get enough voltage. You can power these modules from VIN on the ESP32 or VBUS on the Pico.

How do I open an Arduino CSV file from the SD card in Excel?

Copy the file to your computer and import it in Excel with Data > From Text/CSV, choosing comma as the delimiter. If your Excel uses a decimal comma, the numbers may look wrong; in that case convert the columns using the English (United States) locale or use a semicolon as the separator in your code.

How often can an Arduino write to an SD card?

Writing a few lines per second is no problem, but opening and closing the file for every line takes time. For fast measurements, buffer the data in RAM and write it in batches, or keep the file open and call flush() regularly. For slow rates like one entry a minute, open-write-close every time is the safest method.

Can I use an SD card module with RFID or a TFT display at the same time?

Yes. SPI devices share the MISO, MOSI and SCK lines and each uses its own CS pin. If you run into trouble, set every CS pin to OUTPUT and HIGH at the start of setup(). The level shifter on some cheap micro SD modules never releases the MISO line; in that case use a module that does, or a second SPI bus on the ESP32.

See all projects
Temperature and Humidity with DHT11
#55Intermediate

Temperature and Humidity with DHT11

How warm is your room, and is the air humid or dry? The DHT11 sensor measures temperature and humidity, and the Arduino writes the values on a two-line LCD screen, refreshing every couple of seconds.

4.7(47)
64
ESP32 Temperature Data Logger (DHT22 + DS1307 RTC + microSD + OLED)
#144Advanced
Premium

ESP32 Temperature Data Logger (DHT22 + DS1307 RTC + microSD + OLED)

Build a timestamped temperature logger with ESP32! Read DHT22 temperature/humidity every minute, get real-world time from DS1307 RTC, and log to microSD card as CSV. The OLED displays live temperature (large font), humidity percentage, timestamp, and log counter. The `/temp_log.csv` file on the SD card opens in Excel, Google Sheets, or Python pandas for analysis — perfect for greenhouse monitoring, home/room temperature tracking, laboratory data collection, food storage conditions, or beehiv

4.7(7)
12
Digital Clock with ESP32 and DS1307 RTC (I2C + LCD)
#120Intermediate
Premium

Digital Clock with ESP32 and DS1307 RTC (I2C + LCD)

In this project, we display the time and date on a 16x2 I2C LCD screen using the DS1307 real-time clock (RTC) module. The DS1307 is a battery-backed RTC that maintains the time even if the ESP32 is powered off. Both modules share the same I2C bus (SDA/SCL), allowing us to learn how to control multiple devices using just two signal wires. Through this project, you will gain practical knowledge of the I2C communication protocol, RTC module operation, and dynamic data display on an LCD screen.

4.6(26)
40
ESP32 DHT22 Temperature and Humidity (with LCD Display)
#117Intermediate

ESP32 DHT22 Temperature and Humidity (with LCD Display)

This project measures ambient temperature and humidity using a DHT22 sensor and displays the data in real time on a 16x2 I2C LCD. Data is read from the sensor using the one-wire protocol via the Adafruit DHT library. This project serves as the foundation for IoT weather stations, smart home automation, or environmental monitoring systems in greenhouses. Communication with the DHT22 is established via the ESP32's GPIO 4 pin.

4.8(38)
54
ESP32 Deep Sleep — Battery Saving (OLED + RTC RAM Counter)
#135Advanced
Premium

ESP32 Deep Sleep — Battery Saving (OLED + RTC RAM Counter)

The ESP32's Deep Sleep mode reduces current consumption to ~10 µA by shutting down the CPU and most peripherals, making it critical for battery-powered IoT devices. In this project, we use three different wake-up sources: a timer (15 seconds), an external pin (EXT0 — button GPIO 33), and touch (T0/GPIO 4). Variables marked with `RTC_DATA_ATTR` are preserved during deep sleep—the boot counter, timer wake counter, and button wake counter are continuously tracked. On each wake-up, the SSD1306 OLED

4.3(10)
16
Real-Time Clock from the Internet via ESP32 NTP
#128Advanced
Premium

Real-Time Clock from the Internet via ESP32 NTP

The ESP32 connects to Wi-Fi and retrieves real time with millisecond precision from NTP (Network Time Protocol) servers. Thanks to the `configTzTime()` function, the Turkey time zone (TRT, UTC+3) is automatically applied, and DST transitions in countries that observe daylight saving time are automatically managed using a POSIX TZ string. The time is displayed both on the Serial Monitor and on an optional I2C LCD. `configTzTime()` starts the SNTP service; periodic resynchronization takes place wh

4.5(13)
21
Dot Matrix Clock, Calendar and Weather Station (MAX7219, DS1307)
#85Intermediate
Premium

Dot Matrix Clock, Calendar and Weather Station (MAX7219, DS1307)

A scrolling-text board for your desk or wall that shows the time, the date and the room's weather in turn. On the 8x32 LED display you first see the temperature in both Celsius and Fahrenheit, then th

5.0(50)
66
DS1302 RTC Clock with LCD Display
#46Intermediate
Premium

DS1302 RTC Clock with LCD Display

A little desk clock that keeps the time even when the power goes out. The DS1302 clock module keeps time thanks to its own tiny battery; every second the Arduino reads it and writes the time and date

4.7(41)
51