SD Card Tutorial for Arduino, ESP8266 and ESP32

SD Card Tutorial for Arduino, ESP8266 and ESP32

In this tutorial you learn how to use the SD card module for your Arduino, ESP8266 and ESP32 microcontroller board.

We create a temperature logger where we store the time from a real-time clock module, the temperature and humidity to a micro SD card.

You can download all Arduino files at the end of the tutorial.

SD Card Tutorial Thumbnail

Table of Contents

In my projects I like to transfer data wireless via MQTT to a central database, that is normally on a Raspberry Pi. But what if there is no WiFi available like in the garden? In this case we have to save the data to the SD card, because the internal memory of the microcontroller is only some megabyte. Moreover the internal memory of the microcontroller is harder to access and can be lost during a power reset.

Different SD Card Modules and Shields

There are different SD card modules and also an SD card shield developed by AZDelivery, called Data Logger Module. The SD card shield fits with all pins on an Arduino Uno. The following picture shows a SD card module for micro SD cards and the Arduino Uno shield.

SD Card Module and Arduino Uno Shield

If we take a look at the SD card module, we see that there are two electrical components on the board.

  • Voltage regulator: the SD card module has a build in voltage regulator, because SD cards work with a 3.3V logic. If you want to connect an Arduino microcontroller with an operation voltage of 5V, you need this voltage regulator to reduce the voltage from 5V to 3.3V.
  • Logic-level shifter: The voltage regulator reduces only the supply voltage but for the SPI communication, we need the level shifter to translate the signals between 5V and 3.3V. The SD card module uses the 74LVC125A as logic-level shifter.

More components are not needed because the SD card itself has a build in SPI interface. Therefore the SD card module does not have to supply this interface.

It is important that the SD card that you use is formatted as FAT16 or FAT32. Depending on the SD card module there is a limit of 32GB for the SD card. You find any limitations on the datasheet of your SD card module.

Wiring between SD Card Module and Microcontroller

The SD card modules are connected via SPI to the Arduino, ESP8266 or ESP32 microcontroller. The SPI communication ensures that the data is transferred as fast as possible. The following pictures shows how to wire the module to the different Arduino, ESP8266 and ESP32 microcontroller boards.

In my case, the SD card Module has the following pins from the left to the right:

GND |VCC | MISO | MOSI | SCK | CS

SD Card Module Arduino Nano

SD Card Module Arduino Nano

SD Card Module Arduino Pro Mini

SD Card Module Arduino Pro Mini

SD Card Module Arduino Uno

SD Card Module Arduino Uno

SD Card Module Arduino Mega

SD Card Module Arduino Mega

SD Card Module ESP32 NodeMCU

SD Card Module ESP32 NodeMCU

SD Card Module ESP8266 NodeMCU

SD Card Module ESP8266 NodeMCU

SD Card Module ESP8266 WeMos D1 Mini

SD Card Module ESP8266 WeMos D1 Mini

Instead of the default Slave Select (SS) pin for each microcontroller, you can define another digital pin, but if the default pin is not used, the GPIO must be left empty as output for the SD library.

The following table gives you an overview of all components and parts that I used for this tutorial. I get commissions for purchases made through links in this table.

Arduino UnoAmazonBanggoodAliExpress
ORESP8266 NodeMCUAmazonBanggoodAliExpress
ORESP32 NodeMCUAmazonBanggoodAliExpress
ANDSD Card ModuleAmazonBanggoodAliExpress
ANDReal-Time ClockAmazonBanggoodAliExpress
ANDDHT22 Temperature and Humidity SensorAmazonBanggoodAliExpress

Arduino and ESP8266 Temperature Logger with RTC and SD Card

In the following example we want to build a temperature logger that reads the temperature and humidity from an DHT22 sensor and gets the current time from a real-time clock (RTC) module. The whole dataset is then written to a micro SD card.

The following picture shows the wiring for Arduino and ESP8266 microcontroller. You can klick on each image to enlarge it.

Temperaturelogger RTC Arduino Nano

Temperaturelogger RTC Arduino Nano

Temperaturelogger RTC Arduino Pro Mini

Temperaturelogger RTC Arduino Pro Mini

Temperaturelogger RTC Arduino Uno

Temperaturelogger RTC Arduino Uno

Temperaturelogger RTC Arduino Mega

Temperaturelogger RTC Arduino Mega

Temperaturelogger RTC ESP8266 NodeMCU

Temperaturelogger RTC ESP8266 NodeMCU

Temperaturelogger RTC ESP8266 WeMos D1 Mini

Temperaturelogger RTC ESP8266 WeMos D1 Mini

For this project we need quite some libraries. The only library, that is not installed by default, is the RTClib library that we need to get the time from the RTC. The library can be installed via the Arduino IDE and if you do not know how to install a library, you can visit my library installation guide.

The following lines show the program code for Arduino and ESP8266 based microcontroller.

#include "SPI.h"
#include "SD.h"
#include "DHT.h"
#include "RTClib.h"

#define DHTPIN 7    // used for Arduino
//#define DHTPIN D4   // used for ESP8266

#define DHTYPE DHT22

const int chipSelect = 10;  // used for Arduino
//const int chipSelect = D8;  // used for ESP8266

DHT dht(DHTPIN, DHTYPE);
RTC_DS1307 rtc;

void setup() {
  Serial.begin(9600);
  
  while (!Serial) {
  }

   if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (!rtc.isrunning()) {
    Serial.println("RTC lost power, lets set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization failed!");
    while (1);
  }

  dht.begin();
}

void loop() {
  
  String dataString = "";

  float h = dht.readHumidity();
  float t = dht.readTemperature();

  DateTime now = rtc.now();

  dataString += String(now.unixtime());
  dataString += ",";
  dataString += String(h);
  dataString += ",";
  dataString += String(t);
  
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    Serial.println(dataString);
  }
  
  else {
    Serial.println("error opening datalog.txt");
  }

  delay(2000);
}

At the beginning of the script we include the following libraries:

  • SPI: used for the communication to the SD card
  • SD: read and write to the SD card
  • DHT: used for the DHT temperature and humidity sensor
  • RTClib: interface for the RTC

For Arduino microcontroller we read the DHT22 sensor values from digital pin 7 and for ESP8266 microcontroller from pin D4 like you see in the fritzing sketches. You only have to comment out the line for the microcontroller that you do not use.

Because I use the DHT22 temperature and humidity sensor, I have to define the DHT type as DHT22. If you use the DHT11, you only have to change the type in this line to DHT11.

In the next two lines we define the chip select pin to identify the SD card module on the SPI communication line. In my case I use the default chip select pins. If you use another digital pin for the CS line, remember that you do not connected the pin to other components, because the SD card library will use the default pin. The Arduino uses pin 10 and the ESP8266 pin D8 for the default SPI chip select connection.

Before we enter the setup function we create an DHT object with the previous defined DHT pin and type. Also we create an object for the RTC.

In the setup function we set the baud rate to 9600 that have to match the baud rate of the serial monitor. Now we wait until the serial communication channel is established. With the following three if statements we initialize the real-time clock and the SD card module. The real-time clock is set to the date and time when the sketch is compiled. If something failed during the initialization, we get an error message in the serial monitor.

In the last line of the setup function we initialize the DHT object.

After the setup function the RTC, the DHT sensor and the SD card module are ready to operate. The loop function start with the creation of an empty string.

Now the humidity and temperature is read from the DHT sensor and also the current time in unix datetime format from the RTC. After we have all values that we want to save to the SD card we create one string that will be one line in the text file that we create on the SD card. Therefore we connect all measurements and separate them with a comma.

The next step is to write the complete string to the SD card. Therefore we open the text file with the open function of the SD library and set the path to the text file and use the argument FILE_WRITE to open the file with writing permission.

If the file is available we write the dataString to the opened text file and close the file. To see the string in the serial monitor of the Arduino IDE, we write the same string to the serial USB connection.

If we can not open the file, we write an error message and at the end of the script we wait for 2 seconds.

The following two pictures show the serial monitor and also the opened text file on the PC.

ESP32 Temperature Logger with RTC and SD Card

For the ESP32 we want to create the same temperature logger that reads the temperature and humidity from an DHT22 sensor and gets the current time from a real-time-clock (RTC) module, like for the Arduino and ESP8266. But because the program script is different, I create a separate section in this tutorial.

The following picture shows the wiring of the ESP32 NodeMCU, SD card module, RTC module and DHT22.

Temperaturelogger RTC ESP32 NodeMCU

For the program script we can use most of the parts in the previous example but we will use two functions to write on the SD card. The following lines show the Arduino script for the ESP32 temperature logger.

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

#define DHTPIN 2
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);
RTC_DS1307 rtc;

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

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
    }

  if (!rtc.isrunning()) {
    Serial.println("RTC lost power, lets set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  
  writeFile(SD, "/datalog_ESP32.txt", "Time, Humidity, Temperature \r\n");
}

void loop(){

  String dataString = "";

  float h = dht.readHumidity();
  float t = dht.readTemperature();

  DateTime now = rtc.now();

  dataString += String(now.unixtime());
  dataString += ",";
  dataString += String(h);
  dataString += ",";
  dataString += String(t);
  dataString += "\r\n";

  Serial.println(dataString);
  appendFile(SD, "/datalog_ESP32.txt", dataString.c_str());
    
  delay(2000);
}

Because most of the script is already described in the Arduino and ESP8266 example, I want to focus on the differences.

The first difference is that we not only need the SD card library but also need the FS (file system) library. Also the DHT22 sensor is connected to digital pin 2.

In the setup function we use the writeFile function that is only executed once and that has three arguments:

  1. The memory address of the file system from the SD card (global namespace)
  2. The path to the text file
  3. The string that should be the headline of the text file

Like in the previous example we first open the text file or if it does not exist, we create the file. Then we write the string that was defined by the function argument to the file but at the end we add “\r\n” for a carriage return and start in a new line for the next message payload. When the string was written successfully to the file, we close the text file.

In the loop function we use the appendFile function that is the same as the writeFile function but now the string in the argument is the timestamp from the RTC, the humidity and the temperature.

The following picture shows the output of the serial monitor and the text file on the PC.

Read data from SD Card

In our next example we want to read the data that we wrote on the SD card in the previous example. You can take the wiring from the third chapter of this tutorial.

Arduino Program Code for Arduino and ESP8266

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

const int chipSelect = 10;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
  }

  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization failed!");
    while (1);
  }

  File dataFile = SD.open("datalog.txt");
  if (dataFile) {
    Serial.println("datalog.txt:");

    // read from the file until there's nothing else in it:
    while (dataFile.available()) {
      Serial.write(dataFile.read());
    }
    dataFile.close();
  } else {
    Serial.println("error opening datalog.txt");
  }
}

void loop() {
}

Arduino Program Code for ESP32

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

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 setup(){
  Serial.begin(9600);
  if(!SD.begin()){
      Serial.println("Card Mount Failed");
      return;
  }
  
  readFile(SD, "/datalog_ESP32.txt");
}

void loop(){
}

You know most of the program code from the example with the temperature logger. The only difference is that we open the file in the setup function without the FILE_WRITE argument. Therefore the Arduino, ESP8266 or ESP32 has only the permission to read the file.

In the following while loop, the program loops over every row of the text file and writes the line on the serial output so that we see the complete text file in the serial monitor of the Arduino IDE. If the last line is printed to the serial, the file is closed.

For the ESP32 we use again a function from the examples that opens the text file and uses the same while function to loop over every line of the text file.

I hope you learned a lot in this tutorial. If you have any questions regarding the SD card module, please use the comment section below to ask your questions. If will answer them as soon as possible.

5 Responses

Leave A Comment