You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 Wrover开发板SD卡文件删除功能失效求助

ESP32 SD卡图片删除功能失效问题解决

问题概述

使用Freenove ESP32 Wrover开发板实现拍照并存储到SD卡的功能正常,但在setup中编写的删除SD卡所有图片的代码无法工作。串口输出显示所有文件都找不到,SD.exists()和SD.remove()始终返回0。

原代码

//Lib Esp32
#include "SD.h"
#include "SPI.h"
#include "esp_camera.h"
#include "FS.h"                // SD Card ESP32
#include "SD_MMC.h"            // SD Card ESP32
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"

#define EEPROM_SIZE 1

//Pins module Carte SD 
#define MOSI 14
#define MISO 32
#define CLK 13
#define SS 33

#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    21
#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      19
#define Y4_GPIO_NUM      18
#define Y3_GPIO_NUM       5
#define Y2_GPIO_NUM       4
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22

camera_config_t config;

File root;
int fileCountOnSD = 0; // for counting files

#define CAMERA_MODEL_WROVER_KIT

void setup() {
  
  Serial.begin(115200);
  Serial.println();
  Serial.println();
  
  Serial.println("//------------Initialisation-------------//");

  config_init();

  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 5;
    config.fb_count = 2;
  } 
  else {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 5;
    config.fb_count = 1;
  }

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();
  s->set_vflip(s, 1);        //1-Upside down, 0-No operation
  s->set_hmirror(s, 0);      //1-Reverse left and right, 0-No operation
  s->set_brightness(s, 1);   //up the blightness just a bit
  s->set_saturation(s, -1);  //lower the saturation

  //permet de définir les pins de la carte pour comm SPI 
  SPI.begin(CLK, MISO, MOSI, SS);

  //on vérifie si module SD présent
  if(!SD.begin(SS)){
    Serial.println("Card Mount Failed");
    //return;
  }
  else{
    Serial.println("Card Mount Ok");
  }
  uint8_t cardType = SD.cardType();

  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    //return;
  }

  root = SD.open("/");
  countingFiles(root, 0);

  // Now print the total files count
  Serial.println(F("fileCountOnSD: "));
  Serial.println(fileCountOnSD);

  for(int i=1;i<=fileCountOnSD;i++){
    String stringToDelete = "picture"+String(i)+".jpg";
    if(SD.exists(stringToDelete.c_str())){
      SD.remove(stringToDelete.c_str());
    }
    else{
      Serial.println("no file to remove");
    }
  }

  countingFiles(root, 0);
  Serial.println("after remove : ");
  Serial.println(fileCountOnSD);
  
  Serial.println("done!");

  Serial.println("//------------Fin Initialisation-------------//");

}

void loop() {

  //partie caméra
  camera_fb_t * fb = NULL;

  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    return;
  }
  else{
    Serial.println("Camera capture ok");
    fileCountOnSD++;
    String path = "/picture" + String(fileCountOnSD) +".jpg";
    File file = SD.open(path.c_str(), FILE_WRITE);
    if(!file){
      Serial.println("Failed to open file in writing mode");
    } 
    else {
    file.write(fb->buf, fb->len); // payload (image), payload length
    Serial.printf("Saved file to path: %s\n", path.c_str());
    }
    file.close();
  }

  esp_camera_fb_return(fb);
  //tempo d'1s
  delay(1000);
}

//Fonction d'initialisation de la caméra 
void config_init() {
  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;
  config.fb_count = 1;
}

void countingFiles(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    // for each file count it
    fileCountOnSD++;
  }
}

串口输出

22:55:10.433 -> //------------Initialisation-------------//
22:55:10.697 -> Card Mount Ok
22:55:17.256 -> fileCountOnSD: 
22:55:17.256 -> 8
22:55:17.256 -> no file to remove
22:55:17.256 -> no file to remove
22:55:17.256 -> no file to remove
22:55:17.256 -> no file to remove
22:55:17.256 -> no file to remove
22:55:17.256 -> no file to remove
22:55:17.256 -> no file to remove
22:55:17.256 -> no file to remove
22:55:17.300 -> after remove : 
22:55:17.300 -> 8
22:55:17.300 -> done!

问题原因与修复方案

1. 路径不匹配(核心问题)

拍照时保存文件使用的是绝对路径:/pictureX.jpg,但删除时使用的是相对路径pictureX.jpg。ESP32的SD库要求使用绝对路径才能正确定位根目录下的文件,相对路径会在当前工作目录(默认不是根目录)查找,导致找不到文件。

修复:修改删除循环中的路径字符串,添加根目录斜杠:

String stringToDelete = "/picture"+String(i)+".jpg";

2. 文件计数与遍历逻辑问题

  • 原countingFiles函数会统计所有条目(包括目录),导致fileCountOnSD数值不准确,可能包含非图片文件或目录。
  • 第一次遍历root后,文件指针已经移动到目录末尾,第二次调用countingFiles时不会重新遍历,导致计数不变。

修复:

  • 重写countingFiles函数,只统计文件(排除目录),并在每次统计前重置计数:
void countingFiles(File dir, int numTabs) {
  fileCountOnSD = 0; // 重置计数
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      break;
    }
    if(!entry.isDirectory()){ // 仅统计文件
      fileCountOnSD++;
    }
    entry.close(); // 关闭文件句柄,避免资源泄漏
  }
}
  • 第二次统计前重新打开根目录,重置文件指针:
root = SD.open("/"); // 重新打开根目录
countingFiles(root, 0);

3. 更可靠的删除逻辑(推荐)

依赖序号遍历删除的方式存在缺陷:如果SD卡上存在非连续命名的图片文件,或者有其他非图片文件,会导致漏删或误判。直接遍历所有文件并筛选.jpg后缀的文件删除更可靠:

// 替换原有的删除循环
root = SD.open("/");
while(true){
  File entry = root.openNextFile();
  if(!entry) break;
  if(!entry.isDirectory()){
    String fileName = entry.name();
    if(fileName.endsWith(".jpg")){
      if(SD.remove(fileName)){
        Serial.printf("Deleted: %s\n", fileName.c_str());
      } else {
        Serial.printf("Failed to delete: %s\n", fileName.c_str());
      }
    }
  }
  entry.close();
}

内容的提问来源于stack exchange,提问作者superneiluj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 03:47:07