Arduino 合宙 ESP32 S3 + OV2640 实现低成本SD存储卡相机(ESP32连接SD模块引脚)

合宙ESP32 S3 板载16M flash,8m psram和一个FPC相机接口,价格却不到30元,无疑比价格将近50元的第三方ESP32 S3和将近30的ESP32 Cam更具性价比。

但是虽然板载FPC,由于接口冲突,导致相机与psram不能同时开启,作为ESP32 Cam的替代品来看,还缺少了板载SD卡,而且作为一块发布不久的开发板,网上资料资料非常少,甚至连乐鑫的ESP32 S3开发板关于如何用Arduino配置连接SD卡模块的资料都很少。

但是经过多次试错终于发现了连接方法,Arduino ESP32中默认使用VSPI模式连接,而且官方示例中也没有怎么定义引脚,但是S3中用VSPI模式连接会报错,只能用HSPI连接

//SDCard
#include <SD.h>
#include <SPI.h>
 
SPIClass sdSPI(HSPI);    // 使用vspi模式会报错
#define SD_MISO     17
#define SD_MOSI     16
#define SD_SCLK     18
#define SD_CS       14

void SDCheck() {    // 测试SD卡连接
  uint8_t cardType = SD.cardType();
 
  if (cardType == CARD_NONE)
  {
    Serial.println("未连接存储卡");
    return;
  }
  else 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("挂载了未知存储卡");
  }
  Serial.printf("存储卡总大小是: %lluMB \n", SD.cardSize() / (1024 * 1024)); // "/ (1024 * 1024)"可以换成">> 20"
  Serial.printf("文件系统总大小是: %lluB \n", SD.totalBytes());
  Serial.printf("文件系统已用大小是: %lluB \n", SD.usedBytes());
}

void setup() {
  bool SDstart = false;
  Serial.begin(115200);
  sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, sdSPI))
  {
    Serial.println("存储卡挂载失败");
    return;
  }

  SDCheck();

 相机部分配置:

//Cam
#include "esp_camera.h"
#include "esp_timer.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems

//cam gpio
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 39
#define SIOD_GPIO_NUM 21
#define SIOC_GPIO_NUM 46

#define Y2_GPIO_NUM 34
#define Y3_GPIO_NUM 47
#define Y4_GPIO_NUM 48
#define Y5_GPIO_NUM 33
#define Y6_GPIO_NUM 35
#define Y7_GPIO_NUM 37
#define Y8_GPIO_NUM 38
#define Y9_GPIO_NUM 40

#define VSYNC_GPIO_NUM 42
#define HREF_GPIO_NUM 41
#define PCLK_GPIO_NUM 36

void setup() {    // 相机配置
  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_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size = FRAMESIZE_SVGA;
  config.pixel_format = PIXFORMAT_JPEG;
  // config.grab_mode = CAMERA_GRAB_LATEST;
  config.grab_mode = config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_DRAM;
  config.jpeg_quality = 30;
  config.fb_count = 1;    // 没有psram建议写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;
  }
}

获取图片帧函数:

void getimg(){
  camera_fb_t *fb = NULL;
  fb = esp_camera_fb_get();

  // if (!fb)
  // {
  //   Serial.println("Camera capture failed");
  //   return;
  // }
  // else
  // {
  //   Serial.println("Camera Captured");
  // }

  String filePath = dirPath + "/" + String(count) + ".jpg";

  // Serial.println(filePath);

  File file = SD.open(filePath,FILE_WRITE);
  if(file){
    file.write(fb->buf,fb -> len);
  }
  file.close();
  count ++;
  esp_camera_fb_return(fb);
  fb = NULL;
}

SD卡中文件夹存储代码,合宙这块板貌似没有板载RTC,获取不了准确的时间,而且不连接WiFi,所以只能计数命名

void setup() {
  bool SDstart = false;
  Serial.begin(115200);
  sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, sdSPI))
  {
    Serial.println("存储卡挂载失败");
    return;
  }else{
    while (!SD.exists("/Camera")) {
      SD.mkdir("/Camera");
      SDstart = true;
      Serial.print(".");
    }
    if(!SPIFFS.begin()){
      Serial.println("mount SPIFFS failed!");
    }else {
      if(!SPIFFS.exists("/Statue")){
        SPIFFS.mkdir("/Statue");
        File file = SPIFFS.open("/Statue",FILE_WRITE);
        file.write(1);
        file.close();
      }
      File file = SPIFFS.open("/Statue",FILE_READ);
      if(file.read() == 1){
        file = SPIFFS.open("/Statue",FILE_WRITE);
        file.write(0);
        file.close();
        Serial.println("start");
      } else {
        file = SPIFFS.open("/Statue",FILE_WRITE);
        file.write(1);
        file.close();
        Serial.println("stop");
        while (1);
      }
      if(SDstart == true){
        file = SPIFFS.open("/count.txt",FILE_WRITE);
        file.println("1");
        file.close();
      }else{
        if(!SPIFFS.exists("/count.txt")){
          Serial.println("create file Success!");
          file = SPIFFS.open("/count.txt",FILE_WRITE);
          file.println((String)fileCount);
          Serial.println(fileCount);
          file.close();
        }else{
          Serial.println("file exist!");
          File file = SPIFFS.open("/count.txt",FILE_READ);
          fileCount = atoi(file.readStringUntil('\n').c_str());
          file.close();
          Serial.println(fileCount);
          file = SPIFFS.open("/count.txt",FILE_WRITE);
          file.println(String(++fileCount));
          file.close();
        }
      }
    }
    SPIFFS.end();
    dirPath = "/Camera/" + (String)fileCount;
    while (!SD.exists(dirPath.c_str())) {
      SD.mkdir(dirPath.c_str());
      Serial.print(".");
    }
  }

完整代码:

//SDCard
#include <SD.h>
#include <SPI.h>
//File
#include "SPIFFS.h"
//Cam
#include "esp_camera.h"
#include "esp_timer.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems

SPIClass sdSPI(HSPI);
#define SD_MISO     17
#define SD_MOSI     16
#define SD_SCLK     18
#define SD_CS       14

//cam gpio
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 39
#define SIOD_GPIO_NUM 21
#define SIOC_GPIO_NUM 46

#define Y2_GPIO_NUM 34
#define Y3_GPIO_NUM 47
#define Y4_GPIO_NUM 48
#define Y5_GPIO_NUM 33
#define Y6_GPIO_NUM 35
#define Y7_GPIO_NUM 37
#define Y8_GPIO_NUM 38
#define Y9_GPIO_NUM 40

#define VSYNC_GPIO_NUM 42
#define HREF_GPIO_NUM 41
#define PCLK_GPIO_NUM 36

String dirPath;
uint32_t count = 0;
uint16_t fileCount = 1;

void getimg(){
  camera_fb_t *fb = NULL;
  fb = esp_camera_fb_get();
  String filePath = dirPath + "/" + String(count) + ".jpg";
  // Serial.println(filePath);
  File file = SD.open(filePath,FILE_WRITE);
  if(file){
    file.write(fb->buf,fb -> len);
  }
  file.close();
  count ++;
  esp_camera_fb_return(fb);
  fb = NULL;
}

void setup() {
  bool SDstart = false;
  Serial.begin(115200);
  sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, sdSPI))
  {
    Serial.println("存储卡挂载失败");
    return;
  }else{
    while (!SD.exists("/Camera")) {
      SD.mkdir("/Camera");
      SDstart = true;
      Serial.print(".");
    }
    if(!SPIFFS.begin()){
      Serial.println("mount SPIFFS failed!");
    }else {
      if(!SPIFFS.exists("/Statue")){
        SPIFFS.mkdir("/Statue");
        File file = SPIFFS.open("/Statue",FILE_WRITE);
        file.write(1);
        file.close();
      }
      File file = SPIFFS.open("/Statue",FILE_READ);
      if(file.read() == 1){
        file = SPIFFS.open("/Statue",FILE_WRITE);
        file.write(0);
        file.close();
        Serial.println("start");
      } else {
        file = SPIFFS.open("/Statue",FILE_WRITE);
        file.write(1);
        file.close();
        Serial.println("stop");
        while (1);
      }
      if(SDstart == true){
        file = SPIFFS.open("/count.txt",FILE_WRITE);
        file.println("1");
        file.close();
      }else{
        if(!SPIFFS.exists("/count.txt")){
          Serial.println("create file Success!");
          file = SPIFFS.open("/count.txt",FILE_WRITE);
          file.println((String)fileCount);
          Serial.println(fileCount);
          file.close();
        }else{
          Serial.println("file exist!");
          File file = SPIFFS.open("/count.txt",FILE_READ);
          fileCount = atoi(file.readStringUntil('\n').c_str());
          file.close();
          Serial.println(fileCount);
          file = SPIFFS.open("/count.txt",FILE_WRITE);
          file.println(String(++fileCount));
          file.close();
        }
      }
    }
    SPIFFS.end();
    dirPath = "/Camera/" + (String)fileCount;
    while (!SD.exists(dirPath.c_str())) {
      SD.mkdir(dirPath.c_str());
      Serial.print(".");
    }
  }
  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_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size = FRAMESIZE_SVGA;
  config.pixel_format = PIXFORMAT_JPEG; 
  config.grab_mode = config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_DRAM;
  config.jpeg_quality = 30;
  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;
  }
}

void loop() {
  getimg();
  delay(100);
}

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值