207浏览
查看: 207|回复: 6

[项目] 【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影

[复制链接]
【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影图1

ESP32-Cam 是一款运行在 ESP32-S 芯片上并使用 OV2640 摄像头的小型摄像头模块。ESP32_Cam 也可以 OV7670 摄像头,但 OV2640 更好(更高的分辨率和内置的 JPEG 编码,这消除了 ESP32-S 的处理任务)。
ESP-32 Cam 规格ESP-32 系列
它支持 Wi-Fi (802.11b/g/n)
支持蓝牙 (4.2 带 BLE)
内置 LED 闪光灯
9 个 IO 端口
支持 UART、SPI、I2C 和 PWM
内置 micro SD 读卡器
输入电源:3.3V / 5V(据报道,5V 供电比 3.3V 更稳定)
OV2640 摄像头
2 百万像素
阵列尺寸:UXGA (1600 x 1200)
镜头尺寸:1/4 英寸(6.35 毫米)
最大图像传输速率:15 帧/秒


【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影图2

驴友花雕  中级技神
 楼主|

发表于 2024-9-12 15:14:57

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影

【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)
  实验二百三十:ESP32 CAM开发板 带OV2640摄像头模块 WIFI+蓝牙模块
  项目实验之十二:ESP32 CAM 长时延时摄影:在拍摄之间使设备休眠并记住帧号

实验开源代码

  1. /*
  2.   【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)
  3.   实验二百三十:ESP32 CAM开发板 带OV2640摄像头模块 WIFI+蓝牙模块
  4.   项目实验之十二:ESP32 CAM 长时延时摄影:在拍摄之间使设备休眠并记住帧号
  5. */
  6. #include "esp_camera.h"
  7. #include "SD_MMC.h"
  8. #include "EEPROM.h"
  9. #define EEPROM_SIZE_IN_BYTES 1 // 定义EEPROM的大小为1字节
  10. // 选择摄像头型号
  11. //#define CAMERA_MODEL_WROVER_KIT // 有PSRAM
  12. //#define CAMERA_MODEL_ESP_EYE // 有PSRAM
  13. //#define CAMERA_MODEL_M5STACK_PSRAM // 有PSRAM
  14. //#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera版本B有PSRAM
  15. //#define CAMERA_MODEL_M5STACK_WIDE // 有PSRAM
  16. //#define CAMERA_MODEL_M5STACK_ESP32CAM // 无PSRAM
  17. #define CAMERA_MODEL_AI_THINKER // 有PSRAM
  18. //#define CAMERA_MODEL_TTGO_T_JOURNAL // 无PSRAM
  19. #include "camera_pins.h"
  20. const char* photoPrefix = "/photo_"; // 照片前缀
  21. int photoNumber = 0; // 照片编号
  22. #define MICROSECONDS_IN_SECONDS 1000000 // 每秒的微秒数
  23. #define SLEEP_TIME_IN_SECONDS 120 // 睡眠时间(秒)
  24. unsigned long sleepTime = MICROSECONDS_IN_SECONDS * SLEEP_TIME_IN_SECONDS; // 睡眠时间(微秒)
  25. void setup() {
  26.   Serial.begin(115200); // 初始化串口通信,波特率为115200
  27.   //Serial.setDebugOutput(true);
  28.   //Serial.println();
  29.   Serial.println("ESP32正在唤醒..."); // 打印唤醒信息
  30.   EEPROM.begin(EEPROM_SIZE_IN_BYTES); // 初始化EEPROM
  31.   photoNumber = EEPROM.read(0); // 从EEPROM读取照片编号
  32.   Serial.println("从偏好设置加载的下一个照片编号: " + String(photoNumber)); // 打印照片编号
  33.   camera_config_t config; // 定义摄像头配置
  34.   config.ledc_channel = LEDC_CHANNEL_0;
  35.   config.ledc_timer = LEDC_TIMER_0;
  36.   config.pin_d0 = Y2_GPIO_NUM;
  37.   config.pin_d1 = Y3_GPIO_NUM;
  38.   config.pin_d2 = Y4_GPIO_NUM;
  39.   config.pin_d3 = Y5_GPIO_NUM;
  40.   config.pin_d4 = Y6_GPIO_NUM;
  41.   config.pin_d5 = Y7_GPIO_NUM;
  42.   config.pin_d6 = Y8_GPIO_NUM;
  43.   config.pin_d7 = Y9_GPIO_NUM;
  44.   config.pin_xclk = XCLK_GPIO_NUM;
  45.   config.pin_pclk = PCLK_GPIO_NUM;
  46.   config.pin_vsync = VSYNC_GPIO_NUM;
  47.   config.pin_href = HREF_GPIO_NUM;
  48.   config.pin_sscb_sda = SIOD_GPIO_NUM;
  49.   config.pin_sscb_scl = SIOC_GPIO_NUM;
  50.   config.pin_pwdn = PWDN_GPIO_NUM;
  51.   config.pin_reset = RESET_GPIO_NUM;
  52.   config.xclk_freq_hz = 20000000; // XCLK频率
  53.   config.pixel_format = PIXFORMAT_JPEG; // 像素格式
  54.   // 如果存在PSRAM IC,使用UXGA分辨率和更高的JPEG质量进行初始化
  55.   // 为更大的预分配帧缓冲区
  56.   if (psramFound()) {
  57.     config.frame_size = FRAMESIZE_UXGA;
  58.     config.jpeg_quality = 10;
  59.     config.fb_count = 2;
  60.   } else {
  61.     config.frame_size = FRAMESIZE_SVGA;
  62.     config.jpeg_quality = 12;
  63.     config.fb_count = 1;
  64.   }
  65. #if defined(CAMERA_MODEL_ESP_EYE)
  66.   pinMode(13, INPUT_PULLUP);
  67.   pinMode(14, INPUT_PULLUP);
  68. #endif
  69.   // 摄像头初始化
  70.   esp_err_t err = esp_camera_init(&config);
  71.   if (err != ESP_OK) {
  72.     Serial.printf("摄像头初始化失败,错误代码0x%x", err);
  73.     return;
  74.   }
  75.   sensor_t * s = esp_camera_sensor_get();
  76.   // 初始传感器垂直翻转,颜色有点饱和
  77.   if (s->id.PID == OV3660_PID) {
  78.     s->set_vflip(s, 1); // 翻转回来
  79.     s->set_brightness(s, 1); // 提高亮度
  80.     s->set_saturation(s, -2); // 降低饱和度
  81.   }
  82.   // 如果需要,微调图像
  83.   s->set_brightness(s, -1); // 亮度范围-2到2
  84.   //s->set_contrast(s, 1); // 对比度范围-2到2
  85.   //s->set_saturation(s, 1); // 饱和度范围-2到2
  86.   //s->set_wb_mode(s, 0); // 白平衡模式0到4
  87.   //s->set_special_effect(s, 0); // 特效0: 无, 1: 负片, 2: 灰度, 3: 红色, 4: 绿色, 5: 蓝色, 6: 棕褐色
  88.   //s->set_colorbar(s, 1); // 彩条1或0
  89.   // 降低帧大小以提高初始帧率
  90.   //s->set_framesize(s, FRAMESIZE_QVGA);
  91.   s->set_framesize(s, FRAMESIZE_XGA);
  92.   //s->set_framesize(s, FRAMESIZE_HD);
  93. #if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
  94.   s->set_vflip(s, 1);
  95.   s->set_hmirror(s, 1);
  96. #endif
  97.   Serial.println("初始化SD卡");
  98.   if (!SD_MMC.begin()) {
  99.     Serial.println("SD卡初始化失败!");
  100.     return;
  101.   }
  102.   uint8_t cardType = SD_MMC.cardType();
  103.   if (cardType == CARD_NONE) {
  104.     Serial.println("SD卡槽似乎是空的!");
  105.     return;
  106.   }
  107.   // 如果SD卡为空,则将EEPROM文件计数器重置为0
  108.   ResetPhotoNumbering();
  109.   // 拍摄第一张照片但不保存,因为它通常有绿色的色调
  110.   TakePhoto(false);
  111.   // 拍摄第二张照片并保存到SD卡
  112.   TakePhoto(true);
  113.   Serial.println("将进入深度睡眠,持续 " + String(sleepTime) + " 微秒...");
  114.   Serial.flush();
  115.   // 设置ESP32的睡眠时间间隔
  116.   esp_sleep_enable_timer_wakeup(sleepTime);
  117.   // 让ESP32进入深度睡眠
  118.   esp_deep_sleep_start();
  119. }
  120. void loop() {
  121.   // 本示例不需要循环代码
  122. }
  123. void TakePhoto(bool savePhoto) {
  124.   camera_fb_t * fb = NULL;
  125.   // 使用摄像头拍照
  126.   fb = esp_camera_fb_get();
  127.   if (!fb) {
  128.     Serial.println("摄像头拍照失败");
  129.     return;
  130.   }
  131.   if (!savePhoto) {
  132.     return;
  133.   }
  134.   String photoFileName = photoPrefix + String(photoNumber) + ".jpg";
  135.   fs::FS &fs = SD_MMC;
  136.   Serial.printf("照片文件名: %s\n", photoFileName.c_str());
  137.   File file = fs.open(photoFileName.c_str(), FILE_WRITE);
  138.   if (!file) {
  139.     Serial.println("打开文件写入模式失败");
  140.   }
  141.   else {
  142.     file.write(fb->buf, fb->len); // 负载(图像),负载长度
  143.     Serial.println("文件保存路径: " + String(photoFileName));
  144.     ++photoNumber;
  145.     if (photoNumber > 255) {
  146.       photoNumber = 0;
  147.     }
  148.     EEPROM.write(0, photoNumber);
  149.     EEPROM.commit();
  150.     Serial.println("下一个照片编号已保存到偏好设置: " + String(photoNumber));
  151.   }
  152.   file.close();
  153.   esp_camera_fb_return(fb);
  154. }
  155. void ResetPhotoNumbering() {
  156.   fs::FS &fs = SD_MMC;
  157.   File sdCardRoot = fs.open("/");
  158.   if (!sdCardRoot) {
  159.     Serial.println("打开SD卡根目录失败!");
  160.     return;
  161.   }
  162.   if (!sdCardRoot.isDirectory()) {
  163.     Serial.println("无法读取SD卡根目录!");
  164.     return;
  165.   }
  166.   File file = sdCardRoot.openNextFile();
  167.   if (file.available() > 0) {
  168.     Serial.println("SD卡不为空");
  169.   } else {
  170.     Serial.println("SD卡为空");
  171.     photoNumber = 0;
  172.     EEPROM.write(0, photoNumber);
  173.     EEPROM.commit();
  174.     Serial.println("下一个照片编号重置为0");
  175.   }
  176. }
复制代码



回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2024-9-12 15:16:23

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影

实验串口返回情况

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影图1
回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2024-9-12 15:28:41

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影

代码解释:

1、引入库和定义常量


  1. #include "esp_camera.h"
  2. #include "SD_MMC.h"
  3. #include "EEPROM.h"
  4. #define EEPROM_SIZE_IN_BYTES 1 // 定义EEPROM的大小为1字节
复制代码


esp_camera.h: 用于摄像头的初始化和操作。
SD_MMC.h: 用于SD卡的初始化和操作。
EEPROM.h: 用于EEPROM的读写操作。
EEPROM_SIZE_IN_BYTES: 定义EEPROM的大小为1字节,用于存储照片编号。

2、选择摄像头型号


  1. #define CAMERA_MODEL_AI_THINKER // 有PSRAM
  2. #include "camera_pins.h"
复制代码



CAMERA_MODEL_AI_THINKER: 选择AI Thinker摄像头模块。
camera_pins.h: 包含摄像头引脚定义。

3、定义全局变量和常量


  1. const char* photoPrefix = "/photo_"; // 照片前缀
  2. int photoNumber = 0; // 照片编号
  3. #define MICROSECONDS_IN_SECONDS 1000000 // 每秒的微秒数
  4. #define SLEEP_TIME_IN_SECONDS 120 // 睡眠时间(秒)
  5. unsigned long sleepTime = MICROSECONDS_IN_SECONDS * SLEEP_TIME_IN_SECONDS; // 睡眠时间(微秒)
复制代码



photoPrefix: 照片文件名前缀。
photoNumber: 照片编号,从EEPROM读取。
MICROSECONDS_IN_SECONDS: 每秒的微秒数。
SLEEP_TIME_IN_SECONDS: 睡眠时间(秒)。
sleepTime: 睡眠时间(微秒)。

4、初始化设置


  1. void setup() {
  2.   Serial.begin(115200); // 初始化串口通信,波特率为115200
  3.   Serial.println("ESP32正在唤醒...");
  4.   EEPROM.begin(EEPROM_SIZE_IN_BYTES); // 初始化EEPROM
  5.   photoNumber = EEPROM.read(0); // 从EEPROM读取照片编号
  6.   Serial.println("从偏好设置加载的下一个照片编号: " + String(photoNumber));
复制代码


Serial.begin(115200): 初始化串口通信,波特率为115200。
EEPROM.begin(EEPROM_SIZE_IN_BYTES): 初始化EEPROM。
photoNumber = EEPROM.read(0): 从EEPROM读取照片编号。

5、摄像头配置和初始化


  1. camera_config_t config; // 定义摄像头配置
  2.   config.ledc_channel = LEDC_CHANNEL_0;
  3.   config.ledc_timer = LEDC_TIMER_0;
  4.   config.pin_d0 = Y2_GPIO_NUM;
  5.   config.pin_d1 = Y3_GPIO_NUM;
  6.   config.pin_d2 = Y4_GPIO_NUM;
  7.   config.pin_d3 = Y5_GPIO_NUM;
  8.   config.pin_d4 = Y6_GPIO_NUM;
  9.   config.pin_d5 = Y7_GPIO_NUM;
  10.   config.pin_d6 = Y8_GPIO_NUM;
  11.   config.pin_d7 = Y9_GPIO_NUM;
  12.   config.pin_xclk = XCLK_GPIO_NUM;
  13.   config.pin_pclk = PCLK_GPIO_NUM;
  14.   config.pin_vsync = VSYNC_GPIO_NUM;
  15.   config.pin_href = HREF_GPIO_NUM;
  16.   config.pin_sscb_sda = SIOD_GPIO_NUM;
  17.   config.pin_sscb_scl = SIOC_GPIO_NUM;
  18.   config.pin_pwdn = PWDN_GPIO_NUM;
  19.   config.pin_reset = RESET_GPIO_NUM;
  20.   config.xclk_freq_hz = 20000000; // XCLK频率
  21.   config.pixel_format = PIXFORMAT_JPEG; // 像素格式
  22.   if(psramFound()){
  23.     config.frame_size = FRAMESIZE_UXGA;
  24.     config.jpeg_quality = 10;
  25.     config.fb_count = 2;
  26.   } else {
  27.     config.frame_size = FRAMESIZE_SVGA;
  28.     config.jpeg_quality = 12;
  29.     config.fb_count = 1;
  30.   }
  31.   esp_err_t err = esp_camera_init(&config);
  32.   if (err != ESP_OK) {
  33.     Serial.printf("摄像头初始化失败,错误代码0x%x", err);
  34.     return;
  35.   }
复制代码



camera_config_t config: 定义摄像头配置结构体。
config.pin_xxx: 设置摄像头引脚。
config.xclk_freq_hz: 设置XCLK频率。
config.pixel_format: 设置像素格式为JPEG。
psramFound(): 检查是否存在PSRAM,调整帧大小和JPEG质量。
esp_camera_init(&config): 初始化摄像头。


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2024-9-12 15:31:15

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影

6、传感器设置


  1. sensor_t * s = esp_camera_sensor_get();
  2.   if (s->id.PID == OV3660_PID) {
  3.     s->set_vflip(s, 1); // 翻转回来
  4.     s->set_brightness(s, 1); // 提高亮度
  5.     s->set_saturation(s, -2); // 降低饱和度
  6.   }
  7.   s->set_brightness(s, -1); // 亮度范围-2到2
  8.   s->set_framesize(s, FRAMESIZE_XGA);
复制代码


sensor_t * s = esp_camera_sensor_get(): 获取摄像头传感器。
s->set_vflip(s, 1): 垂直翻转图像。
s->set_brightness(s, 1): 设置亮度。
s->set_saturation(s, -2): 设置饱和度。
s->set_framesize(s, FRAMESIZE_XGA): 设置帧大小。

7、初始化SD卡

  1. Serial.println("初始化SD卡");
  2.   if(!SD_MMC.begin()){
  3.     Serial.println("SD卡初始化失败!");
  4.     return;
  5.   }
  6.   uint8_t cardType = SD_MMC.cardType();
  7.   if(cardType == CARD_NONE){
  8.     Serial.println("SD卡槽似乎是空的!");
  9.     return;
  10.   }
  11.   ResetPhotoNumbering();
复制代码


SD_MMC.begin(): 初始化SD卡。
SD_MMC.cardType(): 检查SD卡类型。
ResetPhotoNumbering(): 重置照片编号。

8、拍照和保存照片



  1. TakePhoto(false); // 拍摄第一张照片但不保存
  2.   TakePhoto(true); // 拍摄第二张照片并保存到SD卡
  3.   Serial.println("将进入深度睡眠,持续 " + String(sleepTime) + " 微秒...");
  4.   Serial.flush();
  5.   esp_sleep_enable_timer_wakeup(sleepTime);
  6.   esp_deep_sleep_start();
  7. }
复制代码


TakePhoto(false): 拍摄第一张照片但不保存。
TakePhoto(true): 拍摄第二张照片并保存到SD卡。
esp_sleep_enable_timer_wakeup(sleepTime): 设置ESP32的睡眠时间间隔。
esp_deep_sleep_start(): 让ESP32进入深度睡眠。

9、拍照函数


  1. void TakePhoto(bool savePhoto) {
  2.   if (!savePhoto) {
  3.     return;
  4.   }
  5.   camera_fb_t * fb = esp_camera_fb_get();  
  6.   if (!fb) {
  7.     Serial.println("摄像头拍照失败");
  8.     return;
  9.   }
  10.   String photoFileName = photoPrefix + String(photoNumber) + ".jpg";
  11.   fs::FS &fs = SD_MMC;
  12.   Serial.printf("照片文件名: %s\n", photoFileName.c_str());
  13.   File file = fs.open(photoFileName.c_str(), FILE_WRITE);
  14.   if (!file) {
  15.     Serial.println("打开文件写入模式失败");
  16.   } else {
  17.     file.write(fb->buf, fb->len); // 写入图像数据
  18.     Serial.println("文件保存路径: " + String(photoFileName));
  19.     ++photoNumber;
  20.     if (photoNumber > 255) {
  21.       photoNumber = 0;
  22.     }
  23.     EEPROM.write(0, photoNumber);
  24.     EEPROM.commit();
  25.     Serial.println("下一个照片编号已保存到偏好设置: " + String(photoNumber));
  26.   }
  27.   file.close();
  28.   esp_camera_fb_return(fb);
  29. }
复制代码


TakePhoto(bool savePhoto): 拍照函数。
camera_fb_t * fb = esp_camera_fb_get(): 获取摄像头帧缓冲区。
String photoFileName = photoPrefix + String(photoNumber) + “.jpg”: 创建照片文件名。
File file = fs.open(photoFileName.c_str(), FILE_WRITE): 打开文件写入模式。
file.write(fb->buf, fb->len): 写入图像数据。
++photoNumber: 增加照片编号。
EEPROM.write(0, photoNumber): 更新EEPROM中的照片编号。
EEPROM.commit(): 提交EEPROM更改。
esp_camera_fb_return(fb): 释放帧缓冲区。

回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2024-9-12 15:32:51

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影

10、重置照片编号函数

  1. void ResetPhotoNumbering() {
  2.   fs::FS &fs = SD_MMC;
  3.   File sdCardRoot = fs.open("/");
  4.   if (!sdCardRoot) {
  5.     Serial.println("打开SD卡根目录失败!");
  6.     return;
  7.   }
  8.   if (!sdCardRoot.isDirectory()) {
  9.     Serial.println("无法读取SD卡根目录!");
  10.     return;
  11.   }
  12.   File file = sdCardRoot.openNextFile();
  13.   if (file) {
  14.     Serial.println("SD卡不为空");
  15.   } else {
  16.     Serial.println("SD卡为空");
  17.     photoNumber = 0;
  18.     EEPROM.write(0, photoNumber);
  19.     EEPROM.commit();
  20.     Serial.println("下一个照片编号重置为0");
  21.   }
  22. }
复制代码


fs::FS &fs = SD_MMC: 使用SD_MMC文件系统。
File sdCardRoot = fs.open(“/”): 打开SD卡根目录。
if (!sdCardRoot): 检查是否成功打开根目录。
if (!sdCardRoot.isDirectory()): 检查根目录是否为目录。
File file = sdCardRoot.openNextFile(): 打开根目录中的下一个文件。
if (file): 检查SD卡是否不为空。
if (!file): 如果SD卡为空,重置照片编号为0,并更新EEPROM。

11、主循环函数

  1. void loop() {
  2.   // 本示例不需要循环代码
  3. }
复制代码


void loop(): 主循环函数。在这个示例中,不需要在循环中执行任何操作,因为所有逻辑都在setup()函数中完成。

总结
这个代码实现了以下功能:
1、初始化ESP32-CAM和SD卡。
2、从EEPROM读取照片编号。
3、配置摄像头参数并初始化摄像头。
4、拍摄照片并保存到SD卡。
5、更新EEPROM中的照片编号。
6、进入深度睡眠以节省电力。
7、在SD卡为空时重置照片编号。
这些功能使你的ESP32-CAM能够定期拍照并保存到SD卡,同时保持低功耗模式。


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2024-9-12 15:35:35

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影

实验场景图

【花雕学编程】Arduino动手做(230)--深度休眠长延时摄影图1
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail