I have a ESP32 AIthinker CAM. I have the ESP32 CAM book . I successfully used the code in the book to take a photo and send the photo as an attachment to my Gmail (great!). As similar article is on:
https://randomnerdtutorials.com/esp32-send-email-smtp-server-arduino-ide/
Instead of using SPIFFS I wish to attach a file from the SD card. I believe that SPIFFS memory is only about 1.3 MB and it also has a limited life of writes (100,000?), regardless it’s too small. All of the examples in the book uses SPIFFS, for sending emails. It gives an example on how to store a picture on a SD card. This website has a similar tutorial for SD card saving https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/
I tried to merge the codes of both examples, but the email never has the attachment. Even when I just try and send a saved photo “/photo.jpg” it does not work. I did change the code as follows;
smtpData.setFileStorageType(MailClientStorageType::SD);
Is there a reason why the book never tried this because it can not be done? Or am I missing one minor setting?
I was able to send an email with SD attachment using the code from indestructables
https://www.instructables.com/id/Motion-Triggered-Image-Capture-and-Email/
However the image quality is very very bad; unacceptable (very dark with a green tinge).
The indestructibles uses SD.h library whilst the Random Nerds code uses SD_MMC.h .
Can anyone give clues why the SD.h file saving method makes a terrible quality picture and/or why can’t the SD_MMC.h attach the picture in the email?
Hi Steven.
After searching for a while, it seems that the MailClient library doesn’t support the SD_MMC library.
The SD library works well with the MaiClient library and I’m also able to send the photo. But, the photos with the SD library are more green than the others and worst quality.
I think it may be because SD and SD_MMC are different:
“SD runs on SPI, and SD_MMC uses the SDMMC hardware bus on the ESP32.”
https://github.com/espressif/arduino-esp32/tree/master/libraries/SD#faq
It should be possible to attach the picture to the email using SD_MMC, but maybe not with this library.
Regards,
Sara
Sara
Thank you for your response. It saved me a lot of time. Please consider updating your web page that the SD_MMC.h library can not send email with attachments from SD card and the SD.h library saves terrible photos.
I have found a partial fix but need help in one final part.
I put both SD.h and SD_MMC.h into one function. It first saves the photo using the SD_MMC.h library and then emails using the SD.h library. It successfully saved the photo on the SD card using SD_MMC.h, but then the SD card did not initialise/mount with the SD.h library. To fix this after the photo was taken using SD_MMC.h the ESP went to deepsleep for 10ms and when it woke up the SD card was able to initialise/mount with SD.h library and email with attachment was sent.
After the email was sent I put the ESP32 to sleep again so the server code and SD_MMC.h could load up again and be ready for the next photo. Unfortunately the SD card could not intitlise/mount and had an error
Starting SD Card MMC
E (4862) sdmmc_common: sdmmc_init_ocr: send_op_cond (1) returned 0x107
SD Card Mount Failed MMC
Failed to open directory
I did try ESP.restart(); , but no success. I also tried pressing the rest button on the board with no success. I was only able to load the SD_MMC.h library by cutting the power and turning it on again. Is there a software way to flush all memory out of the ESP as if it was a power turn off? Or is there a way to dismount the SDcard via software? Also why does discionnecting the power fix the problem, but not reset, I had a similar issue with DS18B20 temperature sensors only fixing thier zero readings only when the power was unplugged.
Hi Steven.
I searched for a while, and the SD.h library doesn’t have any function to close/stop the sd card.
There is a way to disable SDMMC peripherals, but I don’t know exactly how to use it with Arduino IDE: https://docs.espressif.com/projects/esp-idf/en/v3.0.9/api-reference/storage/sdmmc.html#protocol-layer-apis
Can you share the snippet of code that does what you mention? If I take a look at the code, maybe I can find something.
Regards,
Sara
Sara
Thank you. I do not understand how to use these commands, I am learning amateur (duct tape programmer!) trying to make useful electronics for personal and work (agriculture). So I have abandoned trying to get SD.h and SD_MMC.h working together. I have worked on another way to send photos by email and save a copy on the SD by joining two of your projects together (SFIFFS photo email and save photo to SD card). I will post this code soon for I found a little issue that adds a second or two to the procedure, but it works..
Hi Steven.
That’s great.
I’m happy that you’ve found a workaround for your project and got it working.
If you want, please share your code with us.
Regards,
Sara
Sara
I managed to get the code working to to take a photo, store the photo on a SD card and send the photo as a email attachment.
Below is the code and descriptions of minor problems I could not fix. I could not get the code to inject the file name above the picture to work.
/********* Rui Santos Complete instructions at https://RandomNerdTutorials.com/esp32-cam-projects-ebook/ 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. This code is a merging of two code examples from the Rui Santos ESP32 CAM book (good book) from the pages below to take a photo, store the photo on a SD card and send the photo as a email attachment. p145 Module 2 Unit 6: Web Server SD Card Photo Manager: Capture, View and Delete p201 Module 3 Unit 3: Take and Email Photo with a Web Server Similar code can be found on their website.ESP32-CAM Take Photo and Display in Web ServerESP32-CAM Take Photo and Save to MicroSD CardI am a hobbyist programmer so others might be able to make improvements or fixes. It was merged to overcome the problem that the SD_MMC.h library was able to store good quality photos on the SD card but it was not compatible with the ESP32_MailClient.h and unable to send the photo as an attachment from the SD card in an email. The SPIFFS.h is compatible with the ESP32_MailClient.h so this code takes a photo, stores it on the SD and in the SPIFFS memory on the ESP32, then the ESP32_MailClient.h is able to use this file in the SPIFFS for an email attachment. The SD.h library can also take a photo and store it on the SD card and then attach it to an email, however the photo quality is very bad with all photos having a green tinge, so this method was abandoned. Some problems with this sketch are: it that it took 8-12 attempts to "Put the picture data into the file in SPIFFS ". I was putting the new file in SPIFFS and then delete the old one, so I changed the code to first delete the old file then put the new one in and now it takes 3-5 attempts to put the file in SPIFFS (still not good). Sometimes the picture does not show and I need to reboot the ESP CAM, refreshing the screen does not work. Inserting "img { max-width:100%; height:auto;}" in the style of the web page will not show the picture. The original code from Random Nerd Tutorials did have a feature to view and delete picture on the SD card. However it seemed to cause the page to sometimes not present. I can provide this sketch if someone wants to try it. Apart from the SPIFFS file write issue these problems are not critical for me for I will probably not need the server and I will put a timer to email myself a photo every hour. I would like to find a way to send the photo to a special website that organises and displays photos. *********/ #include "WiFi.h" #include "esp_camera.h" #include "esp_timer.h" #include "img_converters.h" #include "Arduino.h" #include "soc/soc.h" // Disable brownout problems #include "soc/rtc_cntl_reg.h" // Disable brownout problems #include "driver/rtc_io.h" #include <ESPAsyncWebServer.h> #include <StringArray.h> #include <SPIFFS.h> #include <FS.h> #include "ESP32_MailClient.h" //--------------------------------------------------- extra #include "SD_MMC.h" // SD Card ESP32 #include "time.h" int maxNumberFilesSaveInSPIFFS = 0; //SPIFFS is about 1300KB and each picture is 100KB, so can only store about 10 photos // Keep track of number of pictures unsigned int pictureNumber = 0; String path = ""; //File root; // NTP Server - this is to put a time stanp in the file name const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 3600 * 10; const int daylightOffset_sec = 3600; #define LED_BUILTIN 4 //------------------------------------------------------ // Replace with your network credentials const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // To send Email using Gmail use port 465 (SSL) and SMTP Server smtp.gmail.com // YOU MUST ENABLE less secure app option https://myaccount.google.com/lesssecureapps?pli=1 #define emailSenderAccount "example@example.com" #define emailSenderPassword "examplePassword" #define smtpServer "smtp.gmail.com" #define smtpServerPort 465 #define emailSubject "ESP32-CAM Photo Captured" //#define emailRecipient "example@example.com" // Default Recipient Email Address String inputMessage = "example@example.com"; // Create AsyncWebServer object on port 80 AsyncWebServer server(80); boolean takeNewPhoto = false; String lastPhoto = ""; // OV2640 camera module pins (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 const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE HTML><html> <head> <title>ESP32-CAM Capture & Email Photo</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body { text-align:center; } </style> </head> <body> <h2>ESP32-CAM Capture & Email Photo</h2> <p>It might take more than 5 seconds to capture a photo.</p> <p> <button onclick="capturePhoto()">CAPTURE PHOTO</button> <button onclick="rotatePhoto();">ROTATE</button> <button onclick="location.reload();">REFRESH PAGE</button> <button onclick="emailPhoto();">EMAIL PHOTO</button> <!--<button onclick="window.open('/list','_blank')">VIEW AND DELETE PHOTOS</button>--> </p> <div><img src="saved-photo" id="photo"></div> <h2>Set Recipient Email Address</h2> <form action="/get"> Email Address <input type="email" name="email_input" value="%EMAIL_INPUT%" required> <input type="submit" value="UPDATE"> </form> </body> <script> function capturePhoto() { var xhr = new XMLHttpRequest(); xhr.open('GET', "/capture", true); xhr.send(); } function emailPhoto() { var xhr = new XMLHttpRequest(); xhr.open('GET', "/email-photo", true); xhr.send(); } function rotatePhoto(){ var img = document.getElementById("photo"); deg += 90; if(isOdd(deg/90)){ document.getElementById("container").className = "vert"; } else{document.getElementById("container").className = "hori";} img.style.transform = "rotate(" + deg + "deg)"; } function isOdd(n){return Math.abs(n % 2)==1;} </script> </html>)rawliteral"; // Replaces placeholder - I do not know why this is here, came from the original code String processor(const String& var){ //Serial.println(var); if(var == "EMAIL_INPUT"){ return inputMessage; } else if(var == "PER"){ return "%"; } return String(); } // Flag variable to keep track if email notification was sent or not bool emailSent = true; const char* PARAM_INPUT_1 = "email_input"; // The Email Sending data object contains config and data to send SMTPData smtpData; //=============================================================================== void setup() { // Serial port for debugging purposes Serial.begin(115200); Serial.println(" "); Serial.println(" Starting setup "); // Connect to Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } if (!SPIFFS.begin(true)) { Serial.println("An Error has occurred while mounting SPIFFS"); ESP.restart(); } else { delay(500); Serial.println("SPIFFS mounted successfully"); } // Get the last file in SPIFFS and put it into lastPhoto variable listAllFilesSPIFFS(); // Initialize MicroSD Serial.println(" "); Serial.println("Initializing the MicroSD card module... "); initMicroSDCard(); pinMode(LED_BUILTIN, OUTPUT); // set pin to input Serial.println("LED turned OFF"); // Turn Off LED to save power // Print ESP32 Local IP Address Serial.print("IP Address: http://"); Serial.println(WiFi.localIP()); // Turn-off the 'brownout detector' WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // OV2640 camera module 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; if (psramFound()) { config.frame_size = FRAMESIZE_UXGA; config.jpeg_quality = 10; config.fb_count = 2; Serial.println("Camera psramFound FRAMESIZE_UXGA"); } else { config.frame_size = FRAMESIZE_SVGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA config.jpeg_quality = 12; //10-63 lower number means higher quality config.fb_count = 1; // config.frame_size = FRAMESIZE_SXGA; // config.jpeg_quality = 18; // config.fb_count = 2; // Serial.println("Camera psram not Found FRAMESIZE_SXGA"); Serial.println("Camera psram not Found FRAMESIZE_SVGA"); } // Camera init esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); ESP.restart(); } else{ Serial.println("Camera initialise success"); } // Route for root / web page server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", index_html, processor); }); server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) { takeNewPhoto = true; request->send_P(200, "text/plain", "Taking Photo"); }); server.on("/saved-photo", HTTP_GET, [](AsyncWebServerRequest * request) { request->send(SPIFFS, lastPhoto, "image/jpg", false); }); server.on("/email-photo", HTTP_GET, [](AsyncWebServerRequest * request) { emailSent = false; request->send_P(200, "text/plain", "Sending Photo"); }); // Receive an HTTP GET request at <ESP_IP>/get?email_input=<inputMessage> server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { // GET email_input value on <ESP_IP>/get?email_input=<inputMessage> if (request->hasParam(PARAM_INPUT_1)) { inputMessage = request->getParam(PARAM_INPUT_1)->value(); } else { inputMessage = "No message sent"; } Serial.println(inputMessage); request->send(200, "text/html", "HTTP GET request sent to your ESP.<br><a href=\"/\">Return to Home Page</a>"); }); // Start server server.begin(); } // end setup //================================================================================= void loop() { if (takeNewPhoto) { capturePhotoSaveSpiffsAndSD(); takeNewPhoto = false; Serial.println("End of taking photo"); } if (!emailSent){ String emailMessage = "Photo captured and emailed using an ESP32-CAM."; if(sendEmailNotification(emailMessage)) { Serial.println(emailMessage); emailSent = true; } else { Serial.println("Email failed to send"); } } delay(1); } //========================================================================= // Check if photo capture was successful bool checkPhoto( fs::FS &fs ) { Serial.println(" "); Serial.println("in checkPhoto( fs::FS &fs ) "); File f_pic = fs.open( path ); //Serial.println("path: " + path); unsigned int pic_sz = f_pic.size(); Serial.println("pic_sz: " + String(pic_sz)); return ( pic_sz > 100 ); } //========================================================================== // Capture Photo and Save it to SPIFFS & SD card void capturePhotoSaveSpiffsAndSD( void ) { Serial.println(" "); camera_fb_t * fb = NULL; // pointer bool ok = 0; // Boolean indicating if the picture has been taken correctly do { // Take a photo with the camera Serial.println(" "); Serial.println("--------------- Taking a photo --------------"); // flash the led so you know a photo was taken digitalWrite(LED_BUILTIN, HIGH); Serial.println("LED turned ON"); fb = esp_camera_fb_get(); digitalWrite(LED_BUILTIN, LOW); Serial.println("LED turned OFF"); if (!fb) { Serial.println("Camera capture failed"); return; }else { Serial.println("Camera capture success"); } } while (!fb); // loop until photo taken configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); File file; //get time for file name format and make file name struct tm timeinfo; char now[20]; getLocalTime(&timeinfo); strftime(now, 20, "%Y%m%d_%H%M%S", &timeinfo); // Format Date & Time String path = "/" + String(now) +".jpg"; lastPhoto = path; Serial.printf("SD Picture file name: %s\n", path.c_str()); listAllFilesSPIFFS(); deleteOldSPIFFSfiles(); listAllFilesSPIFFS(); do{ // SPIFFS Photo file name Serial.println(" "); Serial.println(" Saving to SPIFFS "); Serial.printf("SPIFFS picture file name: %s\n", path ); Serial.println(" "); file = SPIFFS.open(path, FILE_WRITE); // Insert the data in the photo file if (!file) { Serial.println("Failed to open file in writing mode"); } else{ Serial.println("Success open file in SPIFFS writing mode"); } } while (!file); // loop until file is opened int spiffsFileSize; do { // this often needs up to 4 attempts to be successful, do not knoe why Serial.println(" "); Serial.println("Putting picture data into the file in SPIFFS "); //delay(1000); file.write(fb->buf, fb->len); // payload (image), payload length spiffsFileSize = file.size(); Serial.println(" bytes"); } while ( spiffsFileSize == 0 ); Serial.print("File name: "); Serial.println(path); Serial.print(" "); Serial.print(file.size()); spiffsFileSize = file.size(); Serial.println(" bytes"); file.close(); // listAllFilesSPIFFS(); // deleteOldSPIFFSfiles(); // listAllFilesSPIFFS(); double totalSpaceSPIFFS = SPIFFS.totalBytes()/ (1024 ); double usedSpaceSPIFFS = SPIFFS.usedBytes() / (1024); int percentUsedSpaceSPIFFS = round((usedSpaceSPIFFS / totalSpaceSPIFFS)*100); Serial.println("totalSpaceSPIFFS KB: "+ String(totalSpaceSPIFFS)); Serial.println("usedSpaceSPIFFS KB: "+ String(usedSpaceSPIFFS)); Serial.println("percentUsedSpaceSPIFFS: "+ String(percentUsedSpaceSPIFFS)); takeNewPhoto = false; // extra SD CARD write - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Serial.println(" "); Serial.println(" Saving to SD card "); // Path where new picture will be saved in SD Card // Save picture to microSD card fs::FS &fs = SD_MMC; File file2 = fs.open(path.c_str(),FILE_WRITE); if(!file2){ Serial.println("Failed to open file in writing mode"); } else { Serial.println("Success open file in writing mode"); file2.write(fb->buf, fb->len); // payload (image), payload length Serial.printf(" Saved: %s\n", path.c_str()); //listDirectory(SD_MMC); } file2.close(); // listDirSD(SD_MMC, "/", 0); // list files in SD card in serial unsigned long totalSpaceSD = SD_MMC.totalBytes()/ (1024 * 1024); unsigned long usedSpaceSD = SD_MMC.usedBytes() / (1024); int percentUsedSpaceSD = round(((usedSpaceSD *1024)/ totalSpaceSD)*100); Serial.println("totalSpaceSD MB: "+ String(totalSpaceSD)); Serial.println("usedSpaceSD KB: "+ String(usedSpaceSD)); Serial.println("percentUsedSpaceSD: "+ String(percentUsedSpaceSD)); // Could write code similar to SPIFFS to delete old files if SD card becomes full, but did not because // if CAM takes 12 photo per day there is enough space on 32G SD card for 50 years // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //return the frame buffer back to the driver for reuse esp_camera_fb_return(fb); } //===================================================================================================== bool sendEmailNotification(String emailMessage){ Serial.println(" "); // Set the SMTP Server Email host, port, account and password 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); // Set the sender name and Email smtpData.setSender("ESP32", emailSenderAccount); // Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest) smtpData.setPriority("High"); // Set the subject smtpData.setSubject(emailSubject); // Set the message with HTML format smtpData.setMessage(emailMessage, true); // Add recipients smtpData.addRecipient(inputMessage); // Add attach files from SPIFFS smtpData.addAttachFile(lastPhoto, "image/jpg"); // Set the storage type to attach files in your email (SPIFFS) smtpData.setFileStorageType(MailClientStorageType::SPIFFS); smtpData.setSendCallback(sendCallback); // Start sending Email, can be set callback function to track the status if (!MailClient.sendMail(smtpData)) { Serial.println("Error sending Email, " + MailClient.smtpErrorReason()); return false; } // Clear all data from Email object to free memory smtpData.empty(); return true; } //============================================================================ // Callback function to get the Email sending status void sendCallback(SendStatus msg) { //Print the current status Serial.println(msg.info()); } //============================================================================ void listAllFilesSPIFFS(){ // extra Serial.println(" "); Serial.println("In listing all files in SPIFFS"); File root = SPIFFS.open("/"); File file = root.openNextFile(); while(file){ Serial.print("FILE: "); Serial.println(file.name()); //Serial.println("file.name(): " + string(file.name())); //Serial.println("file.name(): " + file.name()); //Serial.print("file.length(): "); //Serial.println(file.length()); Serial.print("File size KB: "); Serial.println(file.size()/1024); lastPhoto = file.name(); file = root.openNextFile(); } } //================================================================================ void deleteOldSPIFFSfiles(){ // extra Serial.println(" "); Serial.println("In delete Old SPIFFS files"); File root = SPIFFS.open("/"); File file = root.openNextFile(); int countFileNames =0; while(file){ // count the number of files on the SPIFFS memeory file = root.openNextFile(); countFileNames++; } Serial.print("countFileNames: "); Serial.println(countFileNames); String fileNames[countFileNames]; // make an array to store the file names File root2 = SPIFFS.open("/"); // I could not get it to cycle though the files again in "while" unless I opened SPIFFS again? File file2 = root2.openNextFile(); countFileNames =0; while(file2){ // put file names into an array Serial.print("File name in array : "); Serial.println(file2.name()); fileNames[countFileNames]= file2.name(); Serial.print("File size in array: "); Serial.println(file2.size()); lastPhoto = file2.name(); countFileNames++; file2 = root2.openNextFile(); } // check if need to delete some files and if so delete tthem if( countFileNames > maxNumberFilesSaveInSPIFFS ){ // if exceed maximum allowable number of files in SPIFFS, then delete old files int numberFilesToDelete = countFileNames - maxNumberFilesSaveInSPIFFS; for (int i = 0; i < numberFilesToDelete; i++) { Serial.println(""); Serial.print("File name to delete: "); Serial.println(fileNames[i]); deleteFile(SPIFFS, fileNames[i]); } } } // end of deleteOldSPIFFSfiles() //===================================================================== //void deleteFile(fs::FS &fs, const char * path){ // I had problems with char? void deleteFile(fs::FS &fs, String path){ Serial.printf("Deleting file: %s\n", path); if(fs.remove(path)){ Serial.println("File deleted"); } else { Serial.println("Delete failed"); } } //===================================================================== void initMicroSDCard(){ // extra // Start Micro SD card Serial.println(" "); Serial.println("Starting SD Card"); //if(!SD_MMC.begin()){ if(!SD_MMC.begin("/sdcard", true)){ Serial.println("SD Card Mount SD_MMC Failed"); return; } else { Serial.println("SD Card Mount SD_MMC Success"); } uint8_t cardType = SD_MMC.cardType(); if(cardType == CARD_NONE){ Serial.println("No SD Card attached"); return; } }