ESP32 with SD and SD_MMC don't work together very well.

I found a sketch that lets you send and email attachments from an SD card and am trying to merge it with another sketch that lets you take a picture when triggered by sound or PIR. (from amazon 20 bucks).

Well, it will do either it seems, but merging them only works once in a while. I use EEPROM to write a save a number, so if, when it wakes up it sees 0 it takes a picture and saves a 1, then wdt resets itself. On reset it will send me the picture. Using serial monitor I notice most of the time I get SD card error. However if I release the SD card and then just put it back in it usually works the next time. Not practical however.

Has anyone had better luck with this? Seems to be SD vs SD_MMC. once you run one you can't run the other.

Seeing your code might help.

#include "esp_system.h"

//  internet stuff

//#include <ESP32WiFi.h>
const int httpPort = 80;
const unsigned long port = 80;
#include "ESP32_MailClient.h"
WiFiClient client;
bool Success = false;
//*********/
//  thingspeak stuff
#include "ThingSpeak.h"
unsigned long myChannelNumber =0;  //27831522
const    char *myReadAPIKey   = " ";
const    char *WriteAPIKey    = " ";
unsigned  int myFieldNumber   = 7;
//const uint16_t port = 80;

unsigned long ReadThingsTime       = 10;
unsigned long ReadThingsInt        = 900000;
bool ThingSpeakStatus       = false;
//*********/
//*********start of stuff from rui
#include "ESP32_MailClient.h"
#include "SD.h"
#include "SPIFFS.h"
const char* ssid = " ";
const char* password = " ";
#define emailSenderAccount    " gmail.com"
#define emailSenderPassword   " 1"
#define emailRecipient        " @gmail.com"
//#define emailRecipient        " @aol.com"
#define smtpServer            "smtp.gmail.com"
#define smtpServerPort        465
#define emailSubject          "hello  "
// The Email Sending data object contains config and data to send
SMTPData smtpData;
// Callback function to get the Email sending status
void sendCallback(SendStatus info);
//*********end  of stuff from rui ****************
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "fd_forward.h"
#include "FS.h"
#include "SD_MMC.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
#include <EEPROM.h>

//define the number of bytes you want to access
#define EEPROM_SIZE 1
//Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM   32
#define RESET_GPIO_NUM  -1
#define XCLK_GPIO_NUM    0
#define SIOD_GPIO_NUM   26
#define SIOC_GPIO_NUM   27
#define Y9_GPIO_NUM  35
#define Y8_GPIO_NUM  34
#define Y7_GPIO_NUM  39
#define Y6_GPIO_NUM  36
#define Y5_GPIO_NUM  21
#define Y4_GPIO_NUM  19
#define Y3_GPIO_NUM  18
#define Y2_GPIO_NUM  5
#define VSYNC_GPIO_NUM  25
#define HREF_GPIO_NUM   23
#define PCLK_GPIO_NUM   22

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
}

void loop() {
  //initialize EEPROM with predefined sizeSDMMC_INTMASK_SBE
  EEPROM.begin(EEPROM_SIZE);
  //int FunctionThisTime = EEPROM.read(0);
  switch (EEPROM.read(0)) {
    case 0:
      TakeThePicture();
      EEPROM.write(0, 1);
      break;
    case 1:
      ReadThingSpeak();
      EEPROM.write(0, 2);
      break;
    case 2:
      SendThePicture();
      EEPROM.write(0, 0);
      break;
    default:
      EEPROM.write(0, 0);
      break;
  }
  EEPROM.commit();
  delay(60000);
  ets_printf("reboot\n");
  esp_restart();
}
void TakeThePicture() {
  Serial.printf(">>>TakeThePicture<<<");
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);      //disable brownout detector
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  pinMode(4, INPUT);
  digitalWrite(4, LOW);
  rtc_gpio_hold_dis(GPIO_NUM_4);
  pinMode(GPIO_NUM_13, INPUT);
  if (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
    Serial.printf("psram found statement");
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
    Serial.printf("using psram else statment");
  }
  esp_err_t err = esp_camera_init(&config);   //Init Camera
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error");
    Success = false;
    return;
  } else {
    Serial.println("Init camera OK");
    Success = true;
  }
  Serial.println("Starting SD Card");
  delay(1500);

  //MailClient.sdBegin(14, 2, 15, 13); // (SCK, MISO, MOSI, SS)
  //SD_MMC.begin("/sdcard", true);
  if (!SD_MMC.begin()) {
    Serial.println("SD Card Mount Failed");
    Success = false;
    return;
  } else {
    Serial.println("SD Card Mount OK");
    Success = true;
  }
  uint8_t cardType = SD_MMC.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("No SD Card attached");
    Success = false;
    return;
  } else {
    Serial.println("SD Card attached");
    Success = true;
  }
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();//Take Picture with Camera
  if (!fb) {
    Serial.println("Camera capture failed");
    Success = false;
    return;
  } else {
    Serial.println("Camera capture OK");
    Success = true;
  }
  String path = "/camPix.jpg";
  fs::FS &fs = SD_MMC;
  Serial.printf("Picture file name: %s\n", path.c_str());
  File file = fs.open(path.c_str(), FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file in writing mode");
    Success = false;
  }
  else {
    file.write(fb->buf, fb->len); // payload (image), payload length
    Serial.printf("Saved file to path: %s\n", path.c_str());
    Success = true;
  }
  file.close();
  esp_camera_fb_return(fb);
  //Turns off the ESP32-CAM white on-board LED (flash) connected to GPIO 4
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  rtc_gpio_hold_en(GPIO_NUM_4);
  delay(2000);
}
void ReadThingSpeak() {
  Serial.println("----starting thingspeak-----");
  //this is Device 60 in   system of devices.
  //Codes 60=Take and send Picture
  //      61=Take picture
  //      62=Send Picture
  //      63=Trigger watchdog reset.
  ThingSpeak.begin(client);  //Initialize ThingSpeak
  WiFiConnect();
  long request = ThingSpeak.readLongField(myChannelNumber, myFieldNumber, myReadAPIKey);
  int statusCode = ThingSpeak.getLastReadStatus();
  if (statusCode == 200) {
    Serial.print("ThingSpeak: " ); Serial.println(request, DEC);
    if (request == 5) {
      TSupdated();
      //take picture
    }
    if (request == 62) {
      TSupdated();
      //send picture
    }
    if (request == 63) {
      TSupdated();
      // ResetDevice();
    }
  }
}
void TSupdated() {
  int CallOut = ThingSpeak.writeField(myChannelNumber, myFieldNumber, 6, WriteAPIKey);
  if (CallOut == 200) {
    Serial.println("field7=6 Completed OK ");
    Success = true;
  }
  else {
    Serial.println("Problem updating " + String(CallOut));
    Success = false;
  }
}
void sendCallback(SendStatus msg) {
  // Print the current status
  Serial.println(msg.info());

  // Do something when complete
  if (msg.success()) {
    Serial.println("----------------");
  }
}
void SendThePicture() {
  Serial.println("***Preparing to send email***");
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);      //disable brownout detector
  smtpData.setLogin(smtpServer, smtpServerPort, emailSenderAccount, emailSenderPassword);
  // For library version 1.2.0 and later which STARTTLS protocol was supported,the STARTTLS will be
  // enabled automatically when port 587 was used, or enable it manually using setSTARTTLS function.
  smtpData.setSTARTTLS(true);
  smtpData.setSender("ESP32", emailSenderAccount);
  smtpData.setPriority("Normal");
  smtpData.setSubject(emailSubject);
  smtpData.setMessage("<div style=\"color:#2f4468;\"><h1>Hi!</h1><p>- Sent from up north</p></div>", true);
  smtpData.addRecipient(emailRecipient);
  //smtpData.addRecipient("YOUR_RECIPIENT_EMAIL_ADDRESS@example.com");
  delay(2000);
  yield();
  //testMailClient.sdBegin(14, 2, 15, 13); // (SCK, MISO, MOSI, SS)
  //testif (!SD.begin()) {
   if (!SD_MMC.begin()) {
    Serial.println("Card Mount Failed in loop");
    return;
  }
  smtpData.addAttachFile("/test.txt");
  smtpData.setFileStorageType(MailClientStorageType::SD);
  smtpData.setSendCallback(sendCallback);
  WiFiConnect();
  delay(2000);
  if (!MailClient.sendMail(smtpData)) {
    Serial.println("Error sending Email, " + MailClient.smtpErrorReason());
  }
  //Clear all data from Email object to free memory
  smtpData.empty(); yield();
}
void WiFiConnect() {
  Serial.print("Connecting");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(200);
  }
  Serial.println();
  Serial.println("WiFi connected.");
}

I had to remove some comments to get it to fit under the size limits but that should compile with esp32 Wrover module huge app 3 spiffs.

I use EEPROM to write a save a number, so if, when it wakes up it sees 0 it takes a picture and saves a 1, then wdt resets itself. On reset it will send me the picture. Using serial monitor I notice most of the time I get SD card error. However if I release the SD card and then just put it back in it usually works the next time. Not practical however.

Sorry, but this quite a stupid way to organize your program. You store the state of your sketch in EEPROM (once a minute, the EEPROM will die after 70-80 days or so) and then reboot the ESP. Is there a reason to use such a horrible way to run through 4 stages every minute? I mean other than wearing out the hardware relatively fast.

You're using a lot of ESP internals (like ets_printf()). Do you know what you do there or do you just think that's cool?

that was for testing. when complete it would read thingspeak and only operate then. the esp stuff was in the sample that came with it. I have no idea what those actually do.

as far as the hardware wearing out, I was under the impression you had at least 100000 cycles or write. Also, I could set it to skip to the next byte after say 50000.

Using EEPROM was a last ditch effort. It works every time too, if you release and reinsert the sd card between actions.

as far as the hardware wearing out, I was under the impression you had at least 100000 cycles or write. Also, I could set it to skip to the next byte after say 50000.

Yes, 100000 write cycles and 100000 minutes are about 70 days.

I'm not sure how EEPROMs are organized but probably the have blocks as flash memory and write complete blocks. So using the next byte won't change anything. And how would you know that you had 50000 cycles?

Using EEPROM was a last ditch effort. It works every time too, if you release and reinsert the sd card between actions.

Why are you using the restart feature to create something that is almost identical to a simple loop? Have you tried to eliminate the EEPROM writing/reading and remove the esp_restart() line?

Easy use another byte to track the number of weeks the thing had been in service. so the last minute of the last hour of the week you update then delay a minute to let that minute rollover so you don't update again.

but this EEPROM stuff was only to see if there were pointers/registers etc., stepping on each other by running SD and SD_MMC concurrently. I was not necessarily going to run it that way, and even if I did the breaks between modules would have been much longer because they would have been triggered thru a separate file updated manually.

Have you tried to convert all of say SD_MMC to SD instead of trying to use both?

You don't appear to say what Arduino you are using, but if you have SD and SD_MMC with each having a 512 byte buffer, coupled with you using the String class, and a buffer in use with the camera memory usage is likely to be very high and could easily result in unexpected problems.

I have and email can only use SD photo only the other

What part of using SD fails? Looking at the code it appears that it simply opens a file, writes a buffer and closes.

the include lib is not designed for SD_MMC

Sorry I dont follow you. If you remove SD_MMC and replacce any references to it with SD what errors do you get / why does it not work?

you get sd card mount failed.

What ESP32 board are you using? Is the SD card slot on the ESP32 board or is it seperate in which case which one?

The SD_MMC library uses a different mechanism to access the SD card than the SD library, so it is highly unlikely that you can access the SD card using both, you will need to opt for one or the other depending on yor hardware I believe. This probably explains why you have to reinsert the SD card - though I wouldn't have expected both libraries to work.

Which email library are you using?

this is the email stuff I copied from the Rui Santos randomnerd website.

//*********start of stuff from rui
#include "ESP32_MailClient.h"
#include "SD.h"
#include "SPIFFS.h"

the camera portion came from GitHub as provided by what was included
from the device I bought from Amazon.

#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "fd_forward.h"
#include "FS.h"
#include "SD_MMC.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
#include <EEPROM.h>

it works if one unblocks and reclicks the card between program executions.

Wrover version, whatever that means. And with the same module you can also specify AI-thinker as the module. No idea what these mean in reality.

Have you seen/read this: ESP32 CAM send email with attachment from SD card; problem – no attachment in email

Not sure if it would help you or not.

yes I did read that! I got to where is said it didn't work by putting it to sleep, so I incorporated the EEPROM switch function which did not work well either. but I will read the rest of it now. thanks.