ESP32 and SPI SD Card

I'm using a micro SD board (e.g., eBay 292104349441) with an ESP32 (DOIT ESP32 DEVKIT VI) and cannot get even a simple example program to work:

/*
 created   Nov 2010 by David A. Mellis
 modified 9 Apr 2012 by Tom Igoe
 modified 13 June 2012 by Limor Fried
 
 This example code is in the public domain.
 	 
 */
#include <SPI.h>
#include <mySD.h>

#define CHIPSELECT       5
#define CLOCK           18
#define MISO            19
#define MOSI            23

File root;

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Serial.print("Initializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin 
  // (10 on Arduino Uno boards, 53 on the Mega) must be left as an output 
  // or the SD library functions will not work. 
  pinMode(SS, OUTPUT);

 if (!SD.begin(CHIPSELECT, MOSI, MISO, CLOCK)) {
     Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  root = SD.open("/");
  
  printDirectory(root, 0);
  
  Serial.println("done!");
}

void loop()
{
}

void printDirectory(File dir, int numTabs) {
  // Begin at the start of the directory
  dir.rewindDirectory();
  
  while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       // no more files
       //Serial.println("**nomorefiles**");
       break;
     }
     for (uint8_t i=0; i<numTabs; i++) {
       Serial.print('\t');   // we'll have a nice indentation
     }
     // Print the 8.3 name
     Serial.print(entry.name());
     // Recurse for directories, otherwise print the file size
     if (entry.isDirectory()) {
       Serial.println("/");
       printDirectory(entry, numTabs+1);
     } else {
       // files have sizes, directories do not
       Serial.print("\t\t");
       Serial.println(entry.size(), DEC);
     }
     entry.close();
   }
}

I have tried several sample ESP32 SD card programs and all fail. If I use the Teensy 4.1 onboard SD holder and use the Teensy SD library, it works fine, so that tells me that the SD card is OK. Any help would be greatly appreciated.

This works for me using a Wemos D1 MINI 32 board

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

#define SD_CS_PIN D8
File myFile;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  Serial.println();
  Serial.print("Initializing SD card...");
  if (!SD.begin(SD_CS_PIN))
  {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  myFile = SD.open("/test.txt", "a"); //append to file
  if (myFile)
  {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
    myFile.close();
    Serial.println("done.");
  }
  else
  {
    Serial.println("error opening test.txt to write");
  }
  myFile = SD.open("/test.txt", "r"); //read from file
  if (myFile)
  {
    Serial.println("test.txt:");
    String inString;  //need to use Strings because of the ESP32 webserver
    while (myFile.available())
    {
      inString += myFile.readString();
    }
    myFile.close();
    Serial.print(inString);
  }
  else
  {
    Serial.println("error opening test.txt to read");
  }
}

void loop()
{
}

Note the use of Strings, which seems common in ESP32land and the D8 pin name. I believe that D8 on the board is pin 5 on the ESP32 which matches you program. Note also the need to preface the filename with "/"

Where did you get the mySD library from ?

Hi Bob: The mySD came from:

I'll give yours a try after dinner...thanks!

i got this one to work on my esp32 DEV using pin map

/*
 * Connect the SD card to the following pins:
 *
 * SD Card | ESP32
 *    GND     GND
 *    3.3v     3.3v
 *    5v        -
 *    CS       VSPI CS0 (GPIO5)
 *    MOSI   VSPI MOSI (GPIO23)
 *    SCK     VSPI CLK (GPIO18)
 *    MISO   VSPI MISO (GPIO 19)
 *    GND    -
 */
#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()){
        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(){

}

i had it connected through the 5v (to 5v) first but it got massively hot, and didn't manage to get it working on HSPI though

@Bob, @Deva: I tried both, and both failed. Bob: Yours did not compile, which tells me you have a file with symbolic constants that's different than mine. I tried it using 5 for the CS_PIN, no luck. Where did you get your include files?

I started to wonder if my SD board was bad, so I substituted a second one with the same results, so that seems unlikely. Not sure what to do now.

Where did you get your include files?

I didn't specifically get them from anywhere, I just let the compiler do its stuff and it used these

Multiple libraries were found for "SPI.h"
 Used: C:\Users\Bob\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\SPI
Multiple libraries were found for "SD.h"
 Used: C:\Users\Bob\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\SD

I know little or nothing about 3rd party support for boards but have always imagined that appropriate libraries were installed and used based on the board selected as the target and the location of the files used would seem to support this

What error did you get when compiling ?

As an aside, I cannot compile code for the SD card reader when using an ESP8266 but I have never got to the bottom of it. Like you with the 32, I have tried multiple variations of code with the 8266 to no avail

The SD.h that is part of Expressifs core for ESP32 fo Arduino works fine for me too.

I have noticed that if you want to open a file for append (write to end of file), you need to use;

SD.open(filename, FILE_APPEND);

I have noticed that if you want to open a file for append (write to end of file), you need to use;

That caught me out too. Testing data logging once a minute for 24 hours using FILE_WRITE resulted in a log file with just one entry (the most recent) in it. At least it proved the value of testing !

UKHeliBob:
That caught me out too. Testing data logging once a minute for 24 hours using FILE_WRITE resulted in a log file with just one entry (the most recent) in it. At least it proved the value of testing !

I was adding SD logging to one of my LoRa GPS tracker programs and as part of that I needed a test program that would keep opening appending and closing a file, just like you would with a GPS logger. The test program was intially written for ATmega, and it worked fine with FILE_WRITE.

The same test program worked on ESP32 with only the SDCS pin number needing to be changed, but with FILE_WRITE it only ever wrote to the first line of the file.

The SD test program on the ESP32 works at 20Mhz CPU clock too, heaps of power saving.............

@srnet: Yes, I did modify the T4.1 program for appending and that does work. However, my code never gets past the "failed to initialize" (e.g., SD.begin()) if block when using the ESP32. As I recall, it appears that the default SD.open() method is for appending on the library I'm using for the T4.1. However, the Teensy patch, of course, has a different core library.

@Bob: the error was an undefined error for the SD_CS_PIN, which means it doesn't know about D8, which is why I asked about the include file.

Which SD.h file does your program use when you compile it ?

What happens if you use pin 5 for the CS pin ?

@Bob: my original code used the mySD.h header file and library, which I mentioned earlier. I then tried plain SD.h. However, now that you've raised that question, I need to check the ESP32 core library to see if it has a different SD.h header file. I'm starting to think that <SD.h> may be reading the Arduino library. I'll check that and report back.

@Bob: I removed my SD library from the libraries subdirectory, thinking the ESP32 core SD might make a difference...wrong again. Looking at my board more closely, if D8 corresponds to GPIO08, my board doesn't even bring that pin out. I thought I read that pins 8-12 are used to interface to external memory, but I can't find out where I might have read that.

More digging...

What kind of board do you actually have ? Mine is very similar to the ESP32 DEVKIT v1 DOIT with 36 pins as shown here ( just the 3.3v pin and the GND pin (next to the 5v pin) are in a different spot, rotated 180 degrees) and the writing on the module is very hard to read..
My SD card module is a little different than yours, it's not a 'micro' SD, but as you said it works on other boards, and i had the same experience, it works, but once i connected it to the VSPI of the ESP32 it also actually just worked straight away.

@Deva: Specifically, which pins did you use? This is the board I'm using.

i had left it in the comments of the sketch (i modified the original example to remind myself)

SD Card | ESP32
GND GND
3.3v 3.3v
5v - your pin
CS VSPI CS0 (GPIO5) right side 8
MOSI VSPI MOSI (GPIO23) 15
SCK VSPI CLK (GPIO18) 9
MISO VSPI MISO (GPIO 19) 10
GND -

I did it in the order that it has on my SD card, i can't quite read what's on yours.
My board is nearly the same.

@Deva: I tried those once, and just tried them again...nothing. A friend of mine has a different SD card holder and I'll try that tomorrow.

For anyone that is using the ESP32 with an SD card, be aware that currently the SD.exists(filename) function returns true if the SD card fails or is removed.

The issue with that function not working is that if you then use this sort of code to read a file;

if (logFile)                                    //if the file is available, read from it
  {
    while (logFile.available())
    {
      Serial.write(logFile.read());
    }
    logFile.close();
    return true;
  }

It locks up the ESP32.

@srnet: Mine fails on the SD.begin() method call, which tells me it doesn't even think the SD is mounted.

ESP32 API SD Pull-up Requirements[ url]Page not Found - ESP32 - — ESP-IDF Programming Guide latest documentation.