Writing on Micro SD card with ESP32 S2 breaks card

Hi,

I am desperately trying to fix an issue that already broke 4 of my micro sd cards.

I am using a Wemos ESP32 S2 Mini with a simple 3.3V SD card reader connected over SPI.
I can successfully mount the card and read properties such as totalBytes(). But reading files fails, and writing files appears to work, but does not actually save anything to the SD card. The biggest problem is, that every now and then writing destroys the card (maybe based on how the card is partitioned or how much I write).

Board: S2 mini — WEMOS documentation
Card reader: Otronic 3.3V Micro SD card module voor ESP32 | ESP8266 - Otronic (looks slightly different than on the pictures)

I use the Arduino IDE and set the board to Lolni S2 Mini.

This is my code:

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

#define SD_POWER_PIN 38
#define SDCARD_CS 34
#define SDCARD_MOSI 35
#define SDCARD_SCLK 36
#define SDCARD_MISO 37
SPIClass SDSPI(FSPI);

void setup() {
  Serial.begin(9600);

  pinMode(SD_POWER_PIN, OUTPUT);
  digitalWrite(SD_POWER_PIN, HIGH);

  delay(200);

  pinMode(SDCARD_MISO, INPUT_PULLUP);
  SDSPI.begin(SDCARD_SCLK, SDCARD_MISO, SDCARD_MOSI, SDCARD_CS);
  if (!SD.begin(SDCARD_CS, SDSPI)) {
    Serial.println("setup SD card FAILED");
    while(1);
  }

  uint32_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.print("setupSDCard PASS. SIZE = ");
  Serial.print(cardSize);
  Serial.println(" MB");

  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
    while(1);
  }
  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");
  }

  Serial.printf("Total space: %llu\n", SD.totalBytes());
  Serial.printf("Used space: %llu\n", SD.usedBytes());

  File file;
  file = SD.open("/test.txt", FILE_WRITE);
  if (!file) {
    Serial.println("error opening file!");
    while(1);
  }

  file.println("TEST");
  file.close();

  while(1);  
}

I tried several different cards, and it is always the same behaviour except from one card which I cannot successfully mount. A different card reader didn't work either, same goes for a different ESP32 S2 Mini board.

I also tried to reformat the cards, but they only show up as 524 kb generic mass storage and formatting them fails.

Any idea on the problem or tips for debugging are very much appreciated.

Thank you!

How did you format them? Using SD Memory Card Formatter for Windows/Mac | SD Association? If not, try that.

Which SD library is being used? You might have to enable verbose output during compilation in file -> preferences in the IDE to be able to see it. As far as I know (not an ESP32 user) it has its own library but the IDE might have decided to use the standard one.

Is this


or this


or this


how your MCU looks?

because this

#define SD_POWER_PIN 38
#define SDCARD_CS 34
#define SDCARD_MOSI 35
#define SDCARD_SCLK 36
#define SDCARD_MISO 37

can be problematic depending upon the ESP32 being used. Post an image of your project with the ESP32 prominent.

I tried with SD Memory Card Formatter now, but it is the same result. The undamaged cards show up correctly with 16gb. 524 kb appears after they broke.

I enabled verbose output and it indeed uses the esp32 specific version:

Using library SD at version 2.0.0 in folder: /home/.../.arduino15/packages/esp32/hardware/esp32/2.0.6/libraries/SD 
Using library FS at version 2.0.0 in folder: /home/.../.arduino15/packages/esp32/hardware/esp32/2.0.6/libraries/FS 
Using library SPI at version 2.0.0 in folder: /home/.../.arduino15/packages/esp32/hardware/esp32/2.0.6/libraries/SPI 

The first picture is how mine looks (S2 Mini).

This is the sd card reader

What happens when you try with the spare micro sd card reader?

What happens when you don't set the SD card reader pins, I dont' with...
Here is an application I use that works for me, perhaps it can provide you with a few hints.

#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "certs.h"
#include "sdkconfig.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_AMG88xx.h>
#include <BMP388_DEV.h>
#include <ESP32Time.h>
//#include <LinearRegression2d.h>
#include "LinearRegression.h"
//#include "PeakDetection.h"
////
//PeakDetection peakDetection;
ESP32Time rtc;
WiFiClient wifiClient;
PubSubClient MQTTclient(mqtt_server, mqtt_port, wifiClient);
Adafruit_AMG88xx amgL;
Adafruit_AMG88xx amgR;
BMP388_DEV bmp388(15);
LinearRegression lr = LinearRegression();
////
const    int pixels           = 64;
const    int toFileSize       = 200;
const    int payloadSize      = 100;
int      count                = 0;
int      mqttOK               = 0;
float    outsideTemperature   = 0.0f;
bool     TimeSet              = false;
QueueHandle_t xQ_Message;
QueueHandle_t Q_toFIle;
////
struct stu_message
{
  char   payload [150] = {'\0'};
  String topic;
} x_message;
////
SemaphoreHandle_t sema_MQTT_KeepAlive;
SemaphoreHandle_t sema_mqttOK;
SemaphoreHandle_t sema_SD;
////
////
void IRAM_ATTR mqttCallback(char* topic, byte * payload, unsigned int length)
{
  // clear locations
  memset( x_message.payload, '\0', 150 );
  x_message.topic = ""; //clear string buffer
  x_message.topic = topic;
  int i = 0;
  for ( i; i < length; i++)
  {
    x_message.payload[i] = ((char)payload[i]);
  }
  x_message.payload[i] = '\0';
  xQueueOverwrite( xQ_Message, (void *) &x_message );// send data
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
// interrupt service routine for WiFi events put into IRAM
void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
      break;
    default: break;
  }
} // void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
////
void setup()
{
  String toFile = "";
  toFile.reserve( toFileSize );
  Q_toFIle = xQueueCreate( 5, sizeof(toFile) );
  x_message.topic.reserve(150);
  xQ_Message = xQueueCreate( 1, sizeof(stu_message) );
  //
  sema_SD =  xSemaphoreCreateBinary();
  sema_mqttOK =  xSemaphoreCreateBinary();
  xSemaphoreGive( sema_mqttOK );
  //
  xTaskCreatePinnedToCore( fSendToFile, "fSendToFile", 9500, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 3500, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 12000, NULL, 3, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fmqttWatchDog, "fmqttWatchDog", 2000, NULL, 3, NULL, 1 ); // assign all to core 1
  xTaskCreatePinnedToCore( fMain, "fMain", 20000, NULL, 5, NULL, 1 ); // assign all to core 1
} // setup()
////
void fMain( void *pvParameters )
{
  int sensorDelay = 125; // 8 times a second
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  bool status;
  status = amgL.begin();
  if (!status) {
    log_i( "Could not find a valid AMG88xxL sensor, check wiring!" );
    while (1);
  }
  status = amgR.begin( 0x68 );
  if (!status) {
    log_i( "Could not find a valid AMG88xxR sensor, check wiring!" );
    while (1);
  }
  bmp388.begin();                                 // Default initialisation, place the BMP388 into SLEEP_MODE
  bmp388.setTimeStandby(TIME_STANDBY_1280MS);     // Set the standby time to 1.3 seconds
  bmp388.startNormalConversion();                 // Start NORMAL continuous conversion
  //peakDetection.begin( 16, 4, 0.1); // 16 samples is 2 seconds, approx
  ///
  float  FrameL[pixels] = { 0.0f };
  float  FrameR[pixels] = { 0.0f };
  float  temperature    = 0.0f;
  float  pressure       = 0.0f;
  float  altitude       = 0.0f; // Create the temperature, pressure and altitude variables
  float  amgAvg         = 0.0f;
  String toFile         = ""; 
  toFile.reserve( toFileSize );
  double values[2];
  float  LastCorrelation = 0.0f;
  //int    peak = 0;
  //
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const  TickType_t xFrequency = sensorDelay; //delay for mS
  for (;;)
  {
    xSemaphoreTake( sema_SD, portMAX_DELAY );
    bmp388.getMeasurements(temperature, pressure, altitude);
    xSemaphoreGive( sema_SD );
    //log_i( "%f", (temperature * 1.8f) + 32.0f );
    amgL.readPixels( FrameL );
    amgR.readPixels( FrameR );
    toFile = "";
    lr.Reset();
    for (int i = 0; i < pixels; i++ )
    {
      // divide by 80 to normalize the data, 80 is the max range of the AMG88, 0 is the min range
      //lr.learn( i, FrameR[i] / 80.0f, FrameL[i] / 80.0f );
      //log_i( "%f %f", FrameR[i], FrameL[i] );
      lr.Data( float(i), FrameR[i] );
      lr.Data( float(i), FrameL[i] );
    }
    lr.Parameters( values );
    //peakDetection.add( lr.correlation() );
    //peak = peakDetection.getPeak();
    //log_i( "peak %d", peak );
    log_i( "Correlation: %f Values: Y=%f and *X + %f ", lr.Correlation(), values[0], values[1] );
    //log_i( "percent of change %f:", 100*((LastCorrelation - lr.correlation()) / LastCorrelation) );
    LastCorrelation = lr.Correlation() ;
    toFile.concat( "Correlation: " + String(lr.Correlation(), 6) + " Values: Y=" + String(values[0], 6) + " and *X + " + String(values[0], 6)  );
    toFile.concat( " Temperature:" + String(temperature) + "\n" );
    if ( toFile.length() != 0 )
    {
      toFile.concat( "\n" );
      xQueueSend( Q_toFIle, (void *) &toFile, 1 );// send data to sd card handler
    }
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    //log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete ( NULL );
}
////
void fSendToFile( void * parameter )
{
  if (!SD.begin())
  {
    log_i("Card Mount Failed");
  } else {
    xSemaphoreGive( sema_SD );
  }
  String toFile = "";
  toFile.reserve( toFileSize );
  for (;;)
  {
    if ( xQueueReceive(Q_toFIle, &toFile, portMAX_DELAY) == pdTRUE )
    {
      //log_i( "%s %d\n", toFile.c_str(), toFile.length() );
      xSemaphoreTake( sema_SD, portMAX_DELAY );
      appendFile(SD, "/data.txt", toFile.c_str() + '\n');
      xSemaphoreGive( sema_SD );
      toFile = "";
    }
    //log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
}
////
void fmqttWatchDog( void * paramater )
{
  int UpdateImeTrigger = 86400; //seconds in a day
  int UpdateTimeInterval = 86300; // 1st time update in 100 counts
  int maxNonMQTTresponse = 60;
  for (;;)
  {
    vTaskDelay( 1000 );
    if ( mqttOK >= maxNonMQTTresponse )
    {
      ESP.restart();
    }
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK++;
    xSemaphoreGive( sema_mqttOK );
    UpdateTimeInterval++; // trigger new time get
    if ( UpdateTimeInterval >= UpdateImeTrigger )
    {
      TimeSet = false; // sets doneTime to false to get an updated time after a days count of seconds
      UpdateTimeInterval = 0;
    }
    //log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
}
//////
void fparseMQTT( void *pvParameters )
{
  struct stu_message px_message;
  for (;;)
  {
    if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    {
      if ( px_message.topic == topicOK )
      {
        xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
        mqttOK = 0; // clear mqtt ok count
        xSemaphoreGive( sema_mqttOK );
      }
      if ( !TimeSet )
      {
        String temp = "";
        temp = px_message.payload[0];
        temp += px_message.payload[1];
        temp += px_message.payload[2];
        temp += px_message.payload[3];
        int year =  temp.toInt();
        temp = "";
        temp = px_message.payload[5];
        temp += px_message.payload[6];
        int month =  temp.toInt();
        temp = "";
        temp = px_message.payload[8];
        temp += px_message.payload[9];
        int day =  temp.toInt();
        temp = "";
        temp = px_message.payload[11];
        temp += px_message.payload[12];
        int hour =  temp.toInt();
        temp = "";
        temp = px_message.payload[14];
        temp += px_message.payload[15];
        int min =  temp.toInt();
        rtc.setTime( 0, min, hour, day, month, year );
        log_i( "%s  ", rtc.getTime() );
        TimeSet = true;
      }
      if ( px_message.topic == topicOutsideTemperature )
      {
        //startTheThing = true;
        outsideTemperature = String(px_message.payload).toFloat();
      }
    }
    //log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  } //for(;;)
  vTaskDelete ( NULL );
} // void fparseMQTT( void *pvParameters )
////
void MQTTkeepalive( void *pvParameters )
{
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  // setting must be set before a mqtt connection is made, setting is done in void connectToMQTT()
  //MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
  for (;;)
  {
    //check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
      MQTTclient.loop();
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
      if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    vTaskDelay( 250 ); //task runs approx every 250 mS
    //log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete ( NULL );
}
////
void connectToWiFi()
{
  int TryCount = 0;
  while ( WiFi.status() != WL_CONNECTED )
  {
    TryCount++;
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    vTaskDelay( 4000 );
    if ( TryCount == 10 )
    {
      ESP.restart();
    }
  }
  WiFi.onEvent( WiFiEvent );
} // void connectToWiFi()
////
void connectToMQTT()
{
  MQTTclient.setKeepAlive( 90 ); // needs be made before connecting
  byte mac[5];
  WiFi.macAddress(mac);
  String clientID = String(mac[0]) + String(mac[4]) ; // use mac address to create clientID
  while ( !MQTTclient.connected() )
  {
    // boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
    MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password, NULL , 1, true, NULL );
    vTaskDelay( 250 );
  }
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( topicOK );
  MQTTclient.subscribe( mqtt_topic );
} // void connectToMQTT()
////
void writeFile(fs::FS &fs, const char * path, const char * message) {
  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    log_i("Failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    log_i("File written");
  } else {
    log_i("Write failed");
  }
}
////
void appendFile(fs::FS &fs, const char * path, const char * message) {
  //log_i("Appending to file: %s\n", path);
  File file = fs.open(path, FILE_APPEND);
  if (!file)
  {
    log_i("Failed to open file for appending");
    return;
  } else {
    file.print(message);
    file.close();
  }
}
////
void renameFile(fs::FS &fs, const char * path1, const char * path2) {
  log_i("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    log_i("File renamed");
  } else {
    log_i("Rename failed");
  }
}
////
//void renameFile(fs::FS &fs, const char * path1, const char * path2) {
//  log_i("Renaming file %s to %s\n", path1, path2);
//  if (fs.rename(path1, path2)) {
//    log_i("File renamed");
//  } else {
//    log_i("Rename failed");
//  }
//}
////
void deleteFile(fs::FS &fs, const char * path) {
  log_i("Deleting file: %s\n", path);
  if (fs.remove(path)) {
    log_i("File deleted");
  } else {
    log_i("Delete failed");
  }
}
////
////
void loop() {}

What happens when you try with the spare micro sd card reader?
Both my sd card readers give the same results.

What happens when you don't set the SD card reader pins
I tried (with sd power directly connected to 3.3v on the esp), but then it simply fails to mount the sd card. Same if I provide the SS/SC pin only.

Okay, the problem is finally solved! Thanks to Idahowalker, I temporarily powered the sd card reader directly from the 3.3v pin on the Wemos ESP32 S2 Mini. With the SPI ports set through the code again, I was finally able to write to the SD card without breaking it.

I tried again with:

  pinMode(SD_POWER_PIN, OUTPUT);
  digitalWrite(SD_POWER_PIN, HIGH);

to make sure that power is the problem. Apparently, the board cannot provide sufficient power over digitalWrite.

In short: the SD card reader needs to be powered directly from the 3.3v pin on the board!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.