Chụp hình và lưu hình vào thẻ nhớ MicroSD với ESP32-CAM

Chụp hình và lưu hình vào thẻ nhớ MicroSD với ESP32-CAM

Bài viết này hướng dẫn cách chụp hình và lưu vào thẻ nhớ microSD với ESP32-CAM và Arduino IDE.

ESP32-CAM ở deep sleep mode, khi ta nhấn nút reset, ESP32-CAM sẽ được đánh thức, sau đó chụp 1 tấm hình và lưu vào thẻ nhớ:

Xem thêm về deep sleep mode tại đây.

Phần cứng cần thiết

  • ESP32-CAM with OV2640
  • Thẻ nhớ microSD
  • FT232R FTDI (mạch nạp)
  • Dây cắm header cái – cái

Format thẻ nhớ

Đầu tiên cần format thẻ nhớ. Có thể sử dụng công cụ format có sẵn của Windows hoặc bất kì phần mềm format thẻ nhớ nào khác.

1. Kết nối thẻ nhớ với máy tính thông qua đầu lọc thẻ nhớ. Vào This PC, click phải chuột vào ổ đĩa thẻ nhớ và chọn Format:

2. Trong cửa sổ mới hiện ra, chọn FAT32 trong phần File system, nhấn Start để bắt đầy quá trình format.

Lưu ý: Theo thông số kỹ thuật, ESP32-CAM chỉ hỗ trợ thẻ nhớ 4 GB. Tuy nhiên khi được thử với thẻ nhớ 16GB, ESP32-CAM vẫn hoạt động bình thường.

Code

Để lập trình cho ESP32-CAM với Arduino IDE, trước tiên cần cài đặt Arduino IDE và các thư viện cần thiết. Làm theo hướng dẫn tại đây để cài đặt nếu bạn chưa cài.

Tạo chương trình mới trong Arduino IDE và copy đoạn chương trình dưới đây vào:

#include "esp_camera.h"
#include "Arduino.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"
#include <EEPROM.h> // read and write from flash memory
// 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
int pictureNumber = 0;
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
//Serial.setDebugOutput(true);
//Serial.println();
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; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Init Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
//Serial.println("Starting SD Card");
if(!SD_MMC.begin()){
Serial.println("SD Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD Card attached");
return;
}
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}
// initialize EEPROM with predefined size
EEPROM.begin(EEPROM_SIZE);
pictureNumber = EEPROM.read(0) + 1;
// Path where new picture will be saved in SD Card
String path = "/picture" + String(pictureNumber) +".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");
}
else {
file.write(fb->buf, fb->len); // payload (image), payload length
Serial.printf("Saved file to path: %s\n", path.c_str());
EEPROM.write(0, pictureNumber);
EEPROM.commit();
}
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);
Serial.println("Going to sleep now");
delay(2000);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop() {
}

Giải thích code

Đầu tiên khai báo các thư viện cần sử dụng:

#include "esp_camera.h"
#include "Arduino.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"
#include <EEPROM.h> // read and write from flash memory

Cần sử dụng thư viện EEPOM.h để làm việc với bộ nhơ flash.

#include <EEPROM.h>

Bạn có thể xem lại cách đọc, ghi dữ liệu vào bộ nhớ flash với ESP32 và Arduino IDE tại đây.

Khai báo dung lượng cần sử dụng, ở đây chỉ sử dụng 1 byte (có thể lưu 256 hình ảnh):

#define EEPROM_SIZE 1

Khai báo các chân cần sử dụng: 

// 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

Lưu ý: Nếu bạn sử dụng bản ESP32-CAM khác với bản bài viết này hướng dẫn, hãy kiểm tra lại sơ đồ chân và khai báo tương ứng với bản ESP32-CAM của bạn.

Khởi tạo biến để tạo ra tên ảnh: picture1.jpg, picture2.jpg, …

int pictureNumber = 0;

Toàn bộ chương trình chính sẽ nằm trong hàm setup(). Chương trình này chỉ chạy 1 lần khi nút reset được nhấn, ESP32-CAM thức dậy. Sau khi thực hiện xong, ESP32-CAM sẽ bắt đầu rơi vào deep sleep mode.

Cài đặt các thông số camera:

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;

Sử dụng đoạn code dưới đây cho các bản ESP32 có PSRAM:

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

Nếu ESP32-CAM không có PSRAM, hãy thêm đoạn code bên dưới:

else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}

Tiếp theo khởi tạo camera:

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

Khởi tạo thẻ nhớ microSD:

//Serial.println("Starting SD Card");
if(!SD_MMC.begin()){
Serial.println("SD Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD Card attached");
return;
}

Đoạn chương trình tiếp theo thực hiện chụp ảnh:

camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}

 Sau đó, khởi tạo EEPROM:

EEPROM.begin(EEPROM_SIZE);

Tên picture được tạo ra bởi cách ghép chuỗi picture với số được lưu trong EEPROM cộng 1:

pictureNumber = EEPROM.read(0) + 1;
String path = "/picture" + String(pictureNumber) +".jpg";

Tiếp theo là đoạn lệnh thực hiện lưu hình ảnh vào thẻ nhớ:

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");
}
else {
file.write(fb->buf, fb->len); // payload (image), payload length
Serial.printf("Saved file to path: %s\n", path.c_str());
EEPROM.write(0, pictureNumber);
EEPROM.commit();
}
file.close();

Sau khi lưu hình vào thẻ nhớ, ta lưu lại số hình ảnh hiện tại vào EEPROM để cập nhật tên hình trong lần chụp tiếp theo:

EEPROM.write(0, pictureNumber);
EEPROM.commit();

Khi ESP32-CAM chụp hình, đèn flash sẽ bật (GPIO 4):

pinMode(4, OUTPUT);
digitalWrite(4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);

Cuối cùng, bắt đầu deep sleep mode khi ESP32-CAM đã thực hiện xong các nhiệm vụ:

esp_deep_sleep_start();

ESP32-CAM sẽ ở deep sleep mode cho tới khi được nhấn nút reset.

Biên dịch và nạp chương trình:

Xem bài viết hướng dẫn nạp chương trình cho ESP32-CAM tại đây.

Sau khi nạp chương trình thành công, chờ ESP32-CAM chụp hình (bạn sẽ quan sát được ESP32-CAM chụp hình qua tín hiệu từ đèn flash), sau đó nhấn RESET để tiếp tục chụp.

Mở Serial Monitor xem các thông báo được in ra để biết chương trình hoạt động đúng chưa:

 Nếu mọi thứ đã hoạt động đúng như mong đợi, bạn có thể ngắt kết nối ESP32-CAM với mạch nạp FTDI và cấp nguồn cho ESP32-CAM thông qua nguồn điện riêng để dễ dàng di chuyển camera đi chụp hình…

Để xem các hình ảnh đã chụp được, cần kết nối thẻ nhớ vào máy tính, có thể lưu hoặc xóa các hình ảnh này thông qua các thao tác trên máy tính.

Nhận xét

Mới hơn Cũ hơn