ESP32: Guide for MicroSD Card Module using Arduino IDE

This guide shows how to use a microSD card with the ESP32: you’ll learn how to read and write files to the microSD card. To interface the microSD card with the ESP32 board, we’ll use a microSD card module (SPI communication protocol). Using a microSD card with the ESP32 is especially useful for data logging or storing files that don’t fit in the filesystem (SPIFFS). The ESP32 will be programmed using the Arduino core.

ESP32 Guide for MicroSD Card Module using Arduino IDE

In this tutorial, we’ll cover the following topics:

MicroSD Card Module

There are different microSD card modules compatible with the ESP32. We’re using the microSD card module sown in the following figure – it communicates using SPI communication protocol. You can use any other microSD card module with an SPI interface.

MicroSD card module for ESP32 ESP8266 Arduino SPI

This microSD card module is also compatible with other microcontrollers like the Arduino and the ESP8266 NodeMCU boards. To learn how to use the microSD card module with the Arduino, you can follow the next tutorial:

Where to Buy?

You can click the link below to check different stores where you can get the microSD card module:

MicroSD card module for ESP32 ESP8266 Arduino SPI

MicroSD Card Module Pinout – SPI

The microSD card module communicates using SPI communication protocol. You can connect it to the ESP32 using the default SPI pins.

MicroSD card moduleESP32
3V33.3V
CSGPIO 5
MOSIGPIO 23
CLKGPIO 18
MISOGPIO 19
GNDGND

Parts Required

ESP32 microSD card module wiring breadboard diagram

For this tutorial, you need the following parts:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

ESP32 with microSD Card Module – Schematic Diagram

To wire the microSD card module to the ESP32 board, you can follow the next schematic diagram (for the default ESP32 SPI pins):

ESP32 microSD Card Module Wiring Diagram

Recommended reading: ESP32 Pinout Reference: Which GPIO pins should you use?

Preparing the microSD Card

Before proceeding with the tutorial, make sure you format your microSD card as FAT32. Follow the next instructions to format your microSD card or use a software tool like SD Card Formater (compatible with Windows and Mac OS).

1. Insert the microSD card into your computer. Go to My Computer and right-click on the SD card. Select Format as shown in the figure below.

MicroSD Card Module format sd card

2. A new window pops up. Select FAT32, press Start to initialize the formatting process and follow the onscreen instructions.

MicroSD Card Module format sd card

Preparing Arduino IDE

We’ll program the ESP32 board using Arduino IDE. So, make sure you have the ESP32 add-on installed. Follow the next tutorial:

If you prefer using VSCode + PlatformIO, follow the next tutorial instead:

ESP32 Handling Files with a MicroSD Card Module

There are two different libraries for the ESP32 (included in the Arduino core for the ESP32): the SD library and the SDD_MMC.h library.

If you use the SD library, you’re using the SPI controller. If you use the SDD_MMC library you’re using the ESP32 SD/SDIO/MMC controller. You can learn more about the ESP32 SD/SDIO/MMC driver.

ESP32 Handle Files in microSD card Example Read and Write

There are several examples in Arduino IDE that show how to handle files on the microSD card using the ESP32. In the Arduino IDE, go to File > Examples > SD(esp32) > SD_Test, or copy the following code.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
  
  This sketch can be found at: Examples > SD(esp32) > SD_Test
*/

#include "FS.h"
#include "SD.h"
#include "SPI.h"

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if(!root){
    Serial.println("Failed to open directory");
    return;
  }
  if(!root.isDirectory()){
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while(file){
    if(file.isDirectory()){
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if(levels){
        listDir(fs, file.name(), levels -1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
}

void createDir(fs::FS &fs, const char * path){
  Serial.printf("Creating Dir: %s\n", path);
  if(fs.mkdir(path)){
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

void removeDir(fs::FS &fs, const char * path){
  Serial.printf("Removing Dir: %s\n", path);
  if(fs.rmdir(path)){
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

void readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while(file.available()){
    Serial.write(file.read());
  }
  file.close();
}

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)){
      Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void renameFile(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\n", path);
  if(fs.remove(path)){
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

void testFileIO(fs::FS &fs, const char * path){
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if(file){
    len = file.size();
    size_t flen = len;
    start = millis();
    while(len){
      size_t toRead = len;
      if(toRead > 512){
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %u ms\n", flen, end);
    file.close();
  } else {
    Serial.println("Failed to open file for reading");
  }


  file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for(i=0; i<2048; i++){
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  file.close();
}

void setup(){
  Serial.begin(115200);
  if(!SD.begin(5)){
    Serial.println("Card Mount Failed");
    return;
  }
  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");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);

  listDir(SD, "/", 0);
  createDir(SD, "/mydir");
  listDir(SD, "/", 0);
  removeDir(SD, "/mydir");
  listDir(SD, "/", 2);
  writeFile(SD, "/hello.txt", "Hello ");
  appendFile(SD, "/hello.txt", "World!\n");
  readFile(SD, "/hello.txt");
  deleteFile(SD, "/foo.txt");
  renameFile(SD, "/hello.txt", "/foo.txt");
  readFile(SD, "/foo.txt");
  testFileIO(SD, "/test.txt");
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}

void loop(){

}

View raw code

This example shows how to do almost any task you may need with the microSD card:

Alternatively, you can use the SD_MMC examples – these are similar to the SD examples, but use the SDMMC driver. For the SDMMC driver, you need a compatible microSD card module. The module we’re using in this tutorial doesn’t support SDMMC.

How the Code Works

First, you need to include the following libraries: FS.h to handle files, SD.h to interface with the microSD card and SPI.h to use SPI communication protocol.

#include "FS.h"
#include "SD.h"
#include "SPI.h"

The example provides several functions to handle files on the microSD card.

List a directory

The listDir() function lists the directories on the SD card. This function accepts as arguments the filesystem (SD), the main directory’s name, and the levels to go into the directory.

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if(!root){
    Serial.println("Failed to open directory");
    return;
  }
  if(!root.isDirectory()){
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while(file){
    if(file.isDirectory()){
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if(levels){
        listDir(fs, file.name(), levels -1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
     file = root.openNextFile();
  }
}

Here’s an example of how to call this function. The / corresponds to the microSD card root directory.

listDir(SD, "/", 0);

Create a Directory

The createDir() function creates a new directory. Pass as an argument the SD filesystem and the directory name path.

void createDir(fs::FS &fs, const char * path){
  Serial.printf("Creating Dir: %s\n", path);
  if(fs.mkdir(path)){
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

For example, the following command creates a new directory on the root called mydir.

createDir(SD, "/mydir");

Remove a Directory

To remove a directory from the microSD card, use the removeDir() function and pass as an argument the SD filesystem and the directory name path.

void removeDir(fs::FS &fs, const char * path){
  Serial.printf("Removing Dir: %s\n", path);
  if(fs.rmdir(path)){
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

Here is an example:

removeDir(SD, "/mydir");

Read File Content

The readFile() function reads the content of a file and prints the content in the Serial Monitor. As with previous functions, pass as an argument the SD filesystem and the file path.

void readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while(file.available()){
    Serial.write(file.read());
  }
  file.close();
}

For example, the following line reads the content of the hello.txt file.

readFile(SD, "/hello.txt")

Write Content to a File

To write content to a file, you can use the writeFile() function. Pass as an argument, the SD filesystem, the file path and the message

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

The following line writes Hello in the hello.txt file.

writeFile(SD, "/hello.txt", "Hello ");

Append Content to a File

Similarly, you can append content to a file (without overwriting previous content) using the appendFile() function.

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)){
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

The following line appends the message World!\n in the hello.txt file. The \n means that the next time you write something to the file, it will be written in a new line.

appendFile(SD, "/hello.txt", "World!\n");

Rename a File

You can rename a file using the renameFile() function. Pass as arguments the SD filesystem, the original filename, and the new filename.

void renameFile(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

The following line renames the hello.txt file to foo.txt.

renameFile(SD, "/hello.txt", "/foo.txt");

Delete a File

Use the deleteFile() function to delete a file. Pass as an argument the SD filesystem and the file path of the file you want to delete.

void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\n", path);
  if(fs.remove(path)){
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

The following line deletes the foo.txt file from the microSD card.

deleteFile(SD, "/foo.txt");

Test a File

The testFileIO() functions shows how long it takes to read the content of a file.

void testFileIO(fs::FS &fs, const char * path){
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if(file){
    len = file.size();
    size_t flen = len;
    start = millis();
    while(len){
      size_t toRead = len;
      if(toRead > 512){
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %u ms\n", flen, end);
    file.close();
  } 
  else {
    Serial.println("Failed to open file for reading");
  }

  file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for(i=0; i<2048; i++){
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  file.close();
}

The following function tests the test.txt file.

testFileIO(SD, "/test.txt");

Initialize the microSD Card

In the setup(), the following lines initialize the microSD card with SD.begin().

Serial.begin(115200);
if(!SD.begin()){
  Serial.println("Card Mount Failed");
  return;
}
uint8_t cardType = SD.cardType();

if(cardType == CARD_NONE){
  Serial.println("No SD card attached");
  return;
}

If you don’t pass any argument to the begin() function, it will try to initialize SPI communication with the microSD card on the default chip select (CS) pin. If you want to use another CS pin, you can pass it as an argument to the begin() function. For example, if you wanted to use GPIO 17 as a CS pin, you should use the following lines of code:

Serial.begin(115200);
if(!SD.begin(17)){
  Serial.println("Card Mount Failed");
  return;
}
uint8_t cardType = SD.cardType();

If you want to use custom SPI pins with the microSD card, go to this section.

Get microSD Card Type

The following lines print the microSD card type on the Serial Monitor.

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");
}

Get microSD Card Size

You can get the microSD card size by calling the cardSize() method:

uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);

Testing MicroSD Card Functions

The following lines call the functions we’ve seen previously.

listDir(SD, "/", 0);
createDir(SD, "/mydir");
listDir(SD, "/", 0);
removeDir(SD, "/mydir");
listDir(SD, "/", 2);
writeFile(SD, "/hello.txt", "Hello ");
appendFile(SD, "/hello.txt", "World!\n");
readFile(SD, "/hello.txt");
deleteFile(SD, "/foo.txt");
renameFile(SD, "/hello.txt", "/foo.txt");
readFile(SD, "/foo.txt");
testFileIO(SD, "/test.txt");
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));

Demonstration

Upload the previous sketch to your ESP32 board. After that, open the Serial Monitor and press the ESP32 on-board RST button. If the initialization succeeds, you’ll get similar messages on the Serial Monitor.

ESP32 board MicroSD Card Module testing features: read write delete
ESP32 board MicroSD Card Module testing features: read write delete

Use Custom SPI Pins with the MicroSD Card

The SD.h and SD_MMC.h libraries use the VSPI SPI pins (23, 19, 18, 5) by default. You can set other pins as SPI pins. The ESP32 features two SPI interfaces: HSPI and VSPI on the following pins:

SPIMOSIMISOCLKCS
VSPIGPIO 23GPIO 19GPIO 18GPIO 5
HSPIGPIO 13GPIO 12GPIO 14GPIO 15

Recommended reading: ESP32 Pinout Reference: Which GPIO pins should you use?

To use other SPI pins, you can proceed as follows:

At the beginning of your code, declare the pins you want to use, for example:

#define SCK  17
#define MISO  19
#define MOSI  23
#define CS  5

In the setup(), create a new SPI class on HSPI or VSPI. We’re using VSPI. Both will work fine.

SPIClass spi = SPIClass(VSPI);

Initialize SPI communication protocol on the pins defined previously:

spi.begin(SCK, MISO, MOSI, CS);

Finally, initialize the microSD card with the begin() method. Pass as argument the CS pin, the SPI instance you want to use, and the bus frequency.

if (!SD.begin(CS,spi,80000000)) {
  Serial.println("Card Mount Failed");
  return;
}

Here is the sample code modified to use custom SPI pins:

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
  
  This sketch was mofidied from: Examples > SD(esp32) > SD_Test
*/

#include "FS.h"
#include "SD.h"
#include "SPI.h"

#define SCK  17
#define MISO  19
#define MOSI  23
#define CS  5

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if(!root){
    Serial.println("Failed to open directory");
    return;
  }
  if(!root.isDirectory()){
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while(file){
    if(file.isDirectory()){
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if(levels){
        listDir(fs, file.name(), levels -1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
}

void createDir(fs::FS &fs, const char * path){
  Serial.printf("Creating Dir: %s\n", path);
  if(fs.mkdir(path)){
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

void removeDir(fs::FS &fs, const char * path){
  Serial.printf("Removing Dir: %s\n", path);
  if(fs.rmdir(path)){
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

void readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while(file.available()){
    Serial.write(file.read());
  }
  file.close();
}

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)){
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void renameFile(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\n", path);
  if(fs.remove(path)){
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

void testFileIO(fs::FS &fs, const char * path){
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if(file){
    len = file.size();
    size_t flen = len;
    start = millis();
    while(len){
      size_t toRead = len;
      if(toRead > 512){
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %u ms\n", flen, end);
    file.close();
  } else {
    Serial.println("Failed to open file for reading");
  }


  file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for(i=0; i<2048; i++){
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  file.close();
}

void setup(){
  Serial.begin(115200);
  SPIClass spi = SPIClass(VSPI);
  spi.begin(SCK, MISO, MOSI, CS);

  if (!SD.begin(CS,spi,80000000)) {
    Serial.println("Card Mount Failed");
    return;
  }
  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");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);

  listDir(SD, "/", 0);
  createDir(SD, "/mydir");
  listDir(SD, "/", 0);
  removeDir(SD, "/mydir");
  listDir(SD, "/", 2);
  writeFile(SD, "/hello.txt", "Hello ");
  appendFile(SD, "/hello.txt", "World!\n");
  readFile(SD, "/hello.txt");
  deleteFile(SD, "/foo.txt");
  renameFile(SD, "/hello.txt", "/foo.txt");
  readFile(SD, "/foo.txt");
  testFileIO(SD, "/test.txt");
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}

void loop(){

}

View raw code


Example: ESP32 Data Logging to microSD Card

Using a microSD card is especially useful for data logging projects. As an example, we’ll show you how to save sensor readings from a BME280 sensor with timestamps (epoch time).

ESP32 datalogging BME280-microSD card project overview

Prerequisites

For this example, make sure you have the following libraries installed:

You can install these libraries using the Arduino library manager. In your Arduino IDE, go to Sketch > Include Library > Manage Libraries… Then, search for the library names and install them.

If you’re using VS Code with PlatformIO, copy the following lines to the platformio.ini file to include all the necessary libraries.

lib_deps = adafruit/Adafruit BME280 Library @ ^2.1.0
  adafruit/Adafruit Unified Sensor @ ^1.1.4

Schematic Diagram

For this example, you need to wire the microSD card module and the BME280 sensor to the ESP32. Here’s a list of the parts required:

Wire the circuit by following the next schematic diagram.

ESP32 microSD card BME280 circuit diagram schematic

You can also take a look at the following tables:

BME280ESP32
VIN3V3
GNDGND
SCLGPIO 22
SDAGPIO 21
microSD card moduleESP32
3V33.3V
CSGPIO 5
MOSIGPIO 23
CLKGPIO 18
MISOGPIO 19
GNDGND

Code

Copy the following code to your Arduino IDE. This sketch gets BME280 sensor readings (temperature, humidity, and pressure) and logs them in a file on the microSD card every 30 seconds. It also logs the timestamp (epoch time requested to an NTP server).

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

// Libraries for SD card
#include "FS.h"
#include "SD.h"
#include <SPI.h>

//Libraries for BME280 sensor
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// Libraries to get time from NTP Server
#include <WiFi.h>
#include "time.h"

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

// BME280 I2C
Adafruit_BME280 bme;

// Variables to hold sensor readings
float temp;
float hum;
float pres;
String dataMessage;

// NTP server to request epoch time
const char* ntpServer = "pool.ntp.org";

// Variable to save current epoch time
unsigned long epochTime; 

// Function that gets current epoch time
unsigned long getTime() {
  time_t now;
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    //Serial.println("Failed to obtain time");
    return(0);
  }
  time(&now);
  return now;
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

// Init BME280
void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

// Initialize SD card
void initSDCard(){
   if (!SD.begin()) {
    Serial.println("Card Mount Failed");
    return;
  }
  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");
  }
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
}

// Write to the SD card
void writeFile(fs::FS &fs, const char * path, const char * message) {
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)) {
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

// Append data to the SD card
void appendFile(fs::FS &fs, const char * path, const char * message) {
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file) {
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)) {
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void setup() {
  Serial.begin(115200);
  
  initWiFi();
  initBME();
  initSDCard();
  configTime(0, 0, ntpServer);
  
  // If the data.txt file doesn't exist
  // Create a file on the SD card and write the data labels
  File file = SD.open("/data.txt");
  if(!file) {
    Serial.println("File doesn't exist");
    Serial.println("Creating file...");
    writeFile(SD, "/data.txt", "Epoch Time, Temperature, Humidity, Pressure \r\n");
  }
  else {
    Serial.println("File already exists");  
  }
  file.close();
}

void loop() {
  if ((millis() - lastTime) > timerDelay) {
    //Get epoch time
    epochTime = getTime();
    
    //Get sensor readings
    temp = bme.readTemperature();
    //temp = 1.8*bme.readTemperature() + 32;
    hum = bme.readHumidity();
    pres = bme.readPressure()/100.0F;

    //Concatenate all info separated by commas
    dataMessage = String(epochTime) + "," + String(temp) + "," + String(hum) + "," + String(pres)+ "\r\n";
    Serial.print("Saving data: ");
    Serial.println(dataMessage);

    //Append the data to file
    appendFile(SD, "/data.txt", dataMessage.c_str());

    lastTime = millis();
  }
}

View raw code

Insert your network credentials in the following variables and the code will work straight away:

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

This example uses the functions we’ve seen previously to write and append data to the microSD card (writeFile() and appendFile() functions).

To better understand how this example works, we recommend taking a look at the following tutorials:

Demonstration

Upload the code to your board. You can check on the Serial Monitor if everything is working as expected.

ESP32 BME280 Datalogging to microSD card Serial Monitor

Let the project run for a while to gather some readings. Then, insert the microSD card on your computer, and you should have a file called data.txt with the sensor readings.

ESP32 BME280 Datalogging to file on microSD card

Example: ESP32 Web Server with Files from microSD Card

You can save the files to build a web server with the ESP32 on a microSD card (HTML, CSS, JavaScript, and image files). This can be useful if the files are too big to fit on the ESP32 filesystem, or it can also be more convenient depending on your project.

ESP32 Web Server with Files from microSD Card How it Works

To show you how to do this, we’ll create a simple web server that serves a HTML, a CSS and a PNG file to build a web page and display a favicon.

Move the following files to your microSD card (click on the links to download the files):

Note: move only the files to the microSD card, not the folders.

Then, upload the following code to your Arduino IDE.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-web-server-microsd-card/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

void initSDCard(){
  if(!SD.begin()){
    Serial.println("Card Mount Failed");
    return;
  }
  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");
  }
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
}

void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  initSDCard();

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SD, "/index.html", "text/html");
  });

  server.serveStatic("/", SD, "/");

  server.begin();
}

void loop() {
  
}

View raw code

Insert your network credentials in the following variables, and the code should work straight away:

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Demonstration

After uploading the files to the microSD card and the sketch to your ESP32 board, open the Serial Monitor at a baud rate of 115200. Press the ESP32 on-board RST button. The ESP32 IP address will be displayed on the Serial Monitor.

ESP32 Web Server with Files from microSD card Arduino IDE Serial Monitor Demonstration

On your local network, open a web browser and type the ESP32 IP address. You should get access to the following web page built with the files stored on the microSD card.

ESP32 Web Server with Files from microSD card

For a detailed explaination of this project, refer to the following tutorial:

Wrapping Up

In this tutorial, you’ve learned how to interface a microSD card with the ESP32 and read and write files. You’ve also learned how to use it for data logging projects or store files to serve in your web server projects.

For more in-depth projects with the microSD card, we recommend taking a look at the following:

We hope you’ve found this tutorial useful. Learn more about the ESP32 with our resources:



Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our newsletter!

46 thoughts on “ESP32: Guide for MicroSD Card Module using Arduino IDE”

  1. Excellent article on MicroSD card usage. Having all that information in one place is so useful. I have seen most of it before but in bits and pieces from all over the place and got myself very confused in the process. Well done you guys.
    Ian.

    Reply
  2. The SPI information is very useful, thank you.

    On an ESP32, accessing a MicroSD card using MMC ( either in 1 bit or 2 nit mode) is also possible. Usually I/O is many times faster.

    Reply
    • Yes. I think it will.
      You just need to declare the right pins.
      Additionally, with the ESP32-CAM you can also use SDMMC interface.
      Regards,
      Sara

      Reply
    • This code will run on the ESP32-CAM board with a few changes as follows to convert the code from SD to SD-MMC. It may also work with SD as Sara suggested. Here are the steps to change the code.
      1. Replace #include “SD.h” with #include “SD_MMC.h”
      2. Remove #include “SPI.h” completely, or just comment out
      3. Leave #include “FS.h” as is
      4. Change all uses of SD to SD_MMC – examples are:
      ‘listDir(SD, “/”, 0);’ becomes ‘listDir(SD_MMC, “/”, 0);’
      ‘if(!SD.begin()){‘ becomes ‘if(!SD_MMC.begin()){‘
      ‘uint8_t cardType = SD.cardType();’ becomes ‘uint8_t cardType = SD_MMC.cardType();’
      Make sure you set the board type to ‘A1 Thinker ESP32-CAM’. That’s it.

      Reply
    • First, a fact (as far as I can tell, and as far as I have actually tested):
      SD cards formatted as FAT32 work just fine with the SD and FS libraries. Cards formatted as exFAT do not work.

      Second, the not-so-good news…
      SD Cards larger than 32 MBytes are (usually) supplied formatted as exFAT. You can’t format these large devices with the ‘usual’ Windows 10 format program. The funny thing is that if you format the large cards as FAT32 (using some kind of third-party application) Windows 10 can read and write them just fine.

      Finally, The Good News…
      I have just now used fat32format.exe from Ridgecrop Consultants to format my Kingston 64 GByte SD card as FAT32 and tested it with the Arduino SD and FS libraries shown in this example. (My present ESP application is using a 16 GB SD card, but your question piqued my interest, and led me to discover something that I very well may find useful for future projects.)

      Reply
  3. Great explanation of useful functions in the FS and SD libraries!

    However…

    The SD Modules you gave links for in Aliexpress are out of stock as of this writing. All of the ones in the various Amazon links are designed for “classic” Arduino usage and require 5 Volts, not 3.3 Volts on their VCC Connection. These modules have on-board 3.3 Volt regulators and level shifters and will work with ESP32 (and other 3.3 Volt processor modules) if you feed the SD module from the Vin pin of the ESP.

    If you feed these particular SD modules from the ESP 3.3 V pin, the SD will be working at something around 2.2 Volts and the very well may not function. (My SanDisk 16 GB SD cards do not.)

    Bottom line: Be VERY careful about feeding 5V to anything connected to an ESP32 module. MAKE DANG SURE that your SD Module has the on-board regulator and level shifter.

    You can see the blocky regulator and the small 14-pin level shifter. If people have ANY question about whether their SD module actually is designed for 5 Volt operation, don’t connect 5 Volts to Vcc. Ask someone who can give a definitive answer.

    Best Regards,

    Dave

    Reply
  4. Using the HiLetgo micro SD TF card adapter, I was getting an error of “card not mounted”. After some research, I discovered that instead of using the 3.3V pin, I needed to power the card reader from the 5V pin of the ESP32. After making that change, it worked fine.

    Reply
  5. Just a small tip regarding those sd cardreaders.
    As you can see in the picture of the card-reader, they are equipped with a little switch which detects if a card is inserted or not.
    When a card is inserted, this pin is pulled to ground
    Sadly, this switch is not connected to the output pins of the module.
    So I measured with a multimeter which from those 10 pins towards the card-reader is pulled to ground and soldered a wire on this pin (for me it was the pin far left).
    This wire I connected to a GPIO-pin of my esp32.
    Now I can detect if my card is inserted or removed and remount the SD-card it in software accordingly.
    Just be sure not to remove a card when your esp is writing to it.

    Reply
  6. Will the SD card be readable after it has been removed and reinserted without removing power or reset of the system?

    Reply
    • I am going to say ‘No’ because that would be rather an unwise thing to do, especially on a regular basis. I was having loads of trouble with SD cards and the code for using them until I found out how to repair them after removing them with the power on. Others may disagree.

      Reply
  7. Hi, can someone please help me. I tried to do the tutorial but somehow my esp32 board will have error that has something to do with flashing bootloader. Right now, if I want to upload my sketch, I need to hold the EN button then hold the BOOT button and after that release the EN button first followed by BOOT button. Only then my board will be recognised by my PC to upload the sketch

    Can someone please help me revert back my esp32 board back to normal

    Reply
  8. I’ve been following some of your tutorials and they’ve really helped me out! I’ve been dealing with Arduino for a while now, and finally took the leap to convert to ESP!! The initial tutorials were all fine, but I’m having a really hard time trying to make the SD card work. The shield is fine and works on Arduino’s 5V, card is formated as FAT32, apparently the wires are all connected as they should be, and when I try to run the code it simply won’t initialize the SD card.
    What I found really weird is that it’s as if it skips a bunch of functions on “setup()”, even tried a simples Serial.print and is simply won’t show… I checked the voltages with a multimeter and it’s fine (4.9-5V), and even feeding the system through a standalone PCB power source and nothing… Any clues what it could be? My guess is it could be because my SD shield is a bit different than yours… But it should still work (provided the pins are corecctly connected to it)

    Reply
  9. Hi random nerd,

    I followed this tutorial, but there is no output shown in the serial monitor.

    Please help me on this.

    Thanks
    Jegan

    Reply
  10. I checked it at least 50 times! Tried with a bunch of different shields and nothing… But I just found out the problem! The microSD I was using was compatible with my Arduino (so it worked every time I went back to it to test) but for some reason wasn’t compatible with my ESP. I just tried a different one and bingo!
    Thanks a lot for your help, and great tutorials!!
    PS: I tried checking the web to see if there was any way of testing/checking wether a card was compatible and couldn’t find it… Any clues?

    Reply
  11. Hi,How can we do this with TTGO T-CALL(ESP32+SIM800L) ? I tried so many codes and I always get error Card Mount failed.Please help.

    Reply
      • Hi.
        You’re probably initializing the microSD card on the wrong pins. Please double-check the connections.
        Regards,
        Sara

        Reply
          • besides, I noticed pin23 sim800l power pin and pin 5 reset it means I can t use spi pins .How can I remap spi pins?

          • Hi again.
            Those pins seem right.
            How are you powering the microSD card module?
            Some modules need 5V, others only need 3.3V. If it doesn’t have enough power, it might not initialize properly.
            Regards,
            Sara

          • I use external power suply .I tried both 5v and 3.3v.As I said even if it works,Sim800l uses mosi pin?

  12. Hola, he intentado este código pero el modulo lector SD se calienta mucho, este esta conectado a 5V y los demás pines como lo indican aquí, sin embargo, al conectar y correr el código se empieza a calentar muchísimo el lector SD, no guardar archivos ni hace lecturas del sensor, la SD no crea el archivo .txt, agradecería mucho de su ayuda

    Reply
    • Hi.
      There must be something wrong with your circuit.
      In our case, our microSD card module is powered with 3.3V, not 5V.
      Connect it to 3.3V.
      Regards,
      Sara

      Reply
  13. Wow – This site has helped so much over the last year – i came here looking for how to remap the SD pins to work on a data logging project and found it – thank you so much for keeping the site current – the level of info is perfect with a great breakdown of how it all works

    Reply
  14. I tried to use the Custom Pins code in the way you posted it SCK =17, CS = 5 , MISO 19, MOSI 23. But i get the “SD Card Mount Failed” error, i double checked everything is well Connected but still i can’t get it to work… Any possible reasons? I’m using Arduino IDE and ESP32 board. It will be very useful since i’m using a 3.5″ RPi Touch Display along with the SD module but the display uses the default SPI pins(23,19,18,5) and i’m trying to use custom pins for the SD, i also used the second SPI pins(12,13,14,15) but still i can’t get it to work. I would appreciate any help.

    Reply
    • Hi.
      Did you try the code with only the SD card with the custom pins? Without the display?
      Also, note that if you’re using two SPI devices, depending on your code, you might need to instantiate two SPI instances with different names.
      I hope this helps.
      Regards,
      Sara

      Reply
  15. It’s really great to come onto a web site like this and find that there is someone knowledgeable and helpful ACTUALLY answering questions quite promptly (I mean you Sara) and that other contributors and users are supplying helpful insights from their experience.
    I have been to other sites and been fed up with the lack of answers or the snotty tone and snide unhelpful comments of their users and admins.
    This, here, is a very refreshing, pleasant and useful place that has helped me, and I notice, many others, on many occasions.
    I’m going to try the voltage idea, and the formatting ideas above because my 32gb Kingston microSD is getting the “failed to mount” error.

    Well done to the Santos team! Great site.
    Up until just recently I have been beginning to despair of the ESP32 as I have been unable to write to it because of some error messages in the Arduino IDE. I tried using different versions of the board andvthe IDE but to no avail. It wasn’t until I read a comment on a similar post that I had any success. The comment said that this chap had the same issue on his Windows10 machine, but on his laptop he had no problem. So, lightbulb moment, I got out my old Sony Vaio running Windows 7 and, behold, the problem no longer happened.

    Reply
    • Hi Richard.
      Thanks for your nice words. I make an effort to help as many people as possible.
      As for your issue: make sure that your microSD card module is wired properly and that the microSD card is connected properly.
      Additionally, double-check the voltage used by your microSD card module. Some might require 5V to operate instead of 3.3V.
      Regards,
      Sara

      Reply
      • Thanks so much to you all again, it was indeed a voltage issue, and once I connected the Vcc pin of the microSD card reader to V5 on the ESP32 everything worked as it should and the sketch above performed as expected.

        Keep it up, no doubt I’ll be back for more education later 🙂
        Regards and thanks
        Richard

        Reply
  16. Would it be possible for ESP32 to have WiFi, Ethernet and SD Card at the same time? Local connections should accept either WiFi or Wired network, and deliver files from the SD card over the web-interface.

    Reply
  17. Hey Sara!

    I’m on a social project, that is planting algae in Africa. Our sensor is now supposed to store data in an SD-card. We followed your tutorial while using an esp32 firebeetle bord and a similar reader to yours. We were pretty sure to get a quick success with your detailed description! (Thank you for that!)
    Sadly, no matter what we try, we get the error “File system is not mounted” by the program.
    What indicates/means this error and do you have any ideas what might cause this?
    Thanks in advance for helping out some amateurs! :))

    Kind regards,
    Felicia

    Reply
    • Hi.
      What is the microSD card that you are using?
      Format your microSD card as FAT32. Other formats might not work and prevent mounting the filesystem.
      Additionally, double-check all connections with the microSD card module and check the power supply.
      Regards,
      Sara

      Reply

Leave a Reply to İLKER Cancel reply

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.