本文將運用ESP32-CAM開發板製作一個縮時攝影裝置,每隔一段時間拍攝一張照片存入microSD記憶卡。
ESP32-CAM開發板搭載的OV2640攝像頭(攝影機模組)的驅動程式,採用樂鑫公司維護的esp32-camera程式庫。目前這個程式庫支援使用下列ESP32晶片的開發板:
- ESP32
- ESP32-S2
- ESP32-S3
esp32-camera程式庫支援多種攝像頭型號,當中的四款及其最高解析度如下:
- OV2640:1600 x 1200
- OV3660:2048 x 1536
- OV5640:2592 x 1944
- OV7670:640 x 480
除了解析度352 x 288像素的 CIF(Common Intermediate Format,通用影像傳輸格式)和低解析度的JPEG影像,拍攝照片和串流影像,ESP32模組或開發板都需要配備PSRAM記憶體。
你可以採用任何ESP32開發板,自己連接攝像頭和PSRAM,或選用事先整合這些模組的開發板,例如:ESP32-CAM, M5Stack和ESP-EYE,ESP32-CAM是當前比較經濟實惠的選擇。
定義OV2640攝像頭模組的接腳
esp32-camera程式庫支援多款ESP32晶片和攝像頭模組,在初始化程式階段,我們必須定義ESP32-CAM連接OV2640攝像頭的腳位。
OV2640攝像頭模組具有下列接腳:
- SDA、SCL:I2C匯流排接線,用於傳遞設置參數給攝像頭。
- XCLK(或XVCLK):從微控器傳給攝像頭的外部時脈訊號。
- VSYNC、HREF與PCLK:垂直和水平同步時脈,以及從攝像頭傳給微控器的像素時脈訊號。
- D0~D7:從攝像頭傳送像素資料給微控器。
- RESET、PWDN:重置和斷電,用於重置攝像頭模組。
ESP32-CAM開發板的OV2640攝像頭,連接ESP32晶片的腳位定義如下:
| OV2640接腳 | 接腳中文名稱 | ESP32-CAM接腳 |
|---|---|---|
| RESET | 重置 | 無 |
| POWER DOWN | 斷電 | GPIO32 |
| SDA | I2C序列資料 | GPIO26 |
| SCL | I2C序列時脈 | GPIO27 |
| HREF/ HSYNC | 水平同步訊號 | GPIO23 |
| VSYNC | 垂直(影格)同步訊號 | GPIO25 |
| PLCK | 像素時脈 | GPIO22 |
| XCLK/XVCLK | 外部時脈 | GPIO0 |
| D0 | 資料0 | GPIO5 |
| D1 | 資料1 | GPIO18 |
| D2 | 資料2 | GPIO19 |
| D3 | 資料3 | GPIO21 |
| D4 | 資料4 | GPIO36 |
| D5 | 資料5 | GPIO39 |
| D6 | 資料6 | GPIO34 |
| D7 | 資料7 | GPIO35 |
OV2640的RESET(重置)腳不用指定對應的ESP32腳,因為從ESP32-CAM的電路圖可以看出,重置腳固定接在3.3V。

esp32-camera程式庫的esp_camera.h標頭檔,定義了一個camera_config_t結構(struct)來記錄攝像頭模組的腳位。我們要把ESP32-CAM的腳位填入這個結構:
static camera_config_t camera_config = {
.pin_pwdn = 32, // 斷電腳
.pin_reset = -1, // 重置腳,此處用-1代表「無」。
.pin_xclk = 0, // 外部時脈腳
.pin_sscb_sda = 26, // I2C資料腳
.pin_sscb_scl = 27, // I2C時脈腳
.pin_d7 = 35, // 資料腳
.pin_d6 = 34,
.pin_d5 = 39,
.pin_d4 = 36,
.pin_d3 = 21,
.pin_d2 = 19,
.pin_d1 = 18,
.pin_d0 = 5,
.pin_vsync = 25, // 垂直同步腳
.pin_href = 23, // 水平同步腳
.pin_pclk = 22, // 像素時脈腳
.xclk_freq_hz = 20000000, // 設定外部時脈:20MHz
.ledc_timer = LEDC_TIMER_0, // 指定產生XCLK時脈的計時器
.ledc_channel = LEDC_CHANNEL_0, // 指定產生XCLM時脈的通道
.pixel_format = PIXFORMAT_JPEG, // 設定影像格式:JPEG
.frame_size = FRAMESIZE_SVGA, // 設定影像大小:SVGA
.jpeg_quality = 10, // 設定JPEG影像畫質,有效值介於0-63,數字越低畫質越高。
.fb_count = 1 // 影像緩衝記憶區數量
};
根據OV2640攝像頭技術文件(PDF格式)第12頁的表8「時脈特性(Timing Characteristics)」指出,XCLK(外部時脈)頻率最低為6Mhz,典型值為24Mhz。esp32-camera程式庫提供的範例都是採用20MHz。
esp_camera程式庫透過PWM輸出時脈訊號給OV2640攝像頭,所以要指定LEDC計時器和通道(channel)。關於ESP32 PWM輸出的解析度、頻率和通道說明,請參閱《超圖解ESP32深度實作》第二章的「PWM輸出」單元。
設定影像格式和大小
OV2640攝像頭可輸出YUV, RGB和Raw RGB等色彩空間的像素資料,esp32-camera程式庫可將它們彙整並轉換成JPEG影像。
上文的camera_config_t結構的pixel_format(像素格式),接受下列影像格式常數值:
- PIXFORMAT_JPEG:JPEG影像
- PIXFORMAT_GRAYSCALE:灰階影像
- PIXFORMAT_YUV422:YUV影像
- PIXFORMAT_RGB565:RGB影像
frame_size(影格大小)接受下列影像尺寸常數值:
- FRAMESIZE_CIF:352 x 288
- FRAMESIZE_QVGA:320 × 240
- FRAMESIZE_VGA:640 × 480
- FRAMESIZE_SVGA:800 × 600
- FRAMESIZE_XGA:1024 × 768
- FRAMESIZE_SXGA:1280 × 1024
- FRAMESIZE_UXGA:1600 × 1200
使用SD_MMC.h程式庫讀寫microSD記憶卡
《超圖解ESP32深度實作》示範採用microSD記憶卡模組,或者SD轉接卡連接ESP32開發板。市面上販售的microSD記憶卡模組,大多都是像下圖這款,採用SPI介面連接微電腦控制板。

使用SPI介面連接microSD記憶卡模組,要採用SD.h程式庫讀寫記憶卡。ESP32晶片內建SD和eMMC記憶體晶片控制器,所以ESP32具備另一種讀寫SD記憶卡的方式(詳閱樂鑫官方的SDMMC Host Driver文件),它的控制電路不是透過SPI介面,而是像底下這樣;

使用ESP32內建的SD/MMC控制器連接記憶卡,程式庫要改用SD_MMC.h,它同樣內建在ESP32 Arduino開發環境,無須額外安裝。SD_MMC和SD程式庫的各種函式,名稱都一樣,所以從SD程式庫改成SD_MMC,只需要修改函式庫名稱。ESP32-CAM採用SD/MMC控制器連接記憶卡,所以程式庫使用SD_MMC
ESP32-CAM縮時攝影機的程式運作流程
縮時攝影的程式流程以及對應的函式如下。操控攝像頭相關的函式和資料型態,例如:esp_camera_init(), esp_camera_fb_get(), esp_camera_fb_return()和camera_fb_t,都由esp_camera程式庫提供。

ESP32-CAM縮時攝影機的程式原始碼
根據上文說明編寫成的縮時攝影原始碼如下,上傳程式之前,請先取出microSD記憶卡。上傳程式到ESP32-CAM開發板之後,它將每隔30秒拍攝一張照片,以photo1.jpg, photo2.jpg, photo3.jpg ,…等檔名存入microSD記憶卡。
#include <esp_camera.h>
#include <SD_MMC.h>
#define SECONDS 30 // 縮時間隔秒數
int photoNum = 0; // 縮時影像檔的編號
void setup() {
Serial.begin(115200);
Serial.println("準備拍攝縮時影像!");
// 設定攝像頭的接腳和影像格式與尺寸
static camera_config_t camera_config = {
.pin_pwdn = 32, // 斷電腳
.pin_reset = -1, // 重置腳
.pin_xclk = 0, // 外部時脈腳
.pin_sscb_sda = 26, // I2C資料腳
.pin_sscb_scl = 27, // I2C時脈腳
.pin_d7 = 35, // 資料腳
.pin_d6 = 34,
.pin_d5 = 39,
.pin_d4 = 36,
.pin_d3 = 21,
.pin_d2 = 19,
.pin_d1 = 18,
.pin_d0 = 5,
.pin_vsync = 25, // 垂直同步腳
.pin_href = 23, // 水平同步腳
.pin_pclk = 22, // 像素時脈腳
.xclk_freq_hz = 20000000, // 設定外部時脈:20MHz
.ledc_timer = LEDC_TIMER_0, // 指定產生XCLK時脈的計時器
.ledc_channel = LEDC_CHANNEL_0, // 指定產生XCLM時脈的通道
.pixel_format = PIXFORMAT_JPEG, // 設定影像格式:JPEG
.frame_size = FRAMESIZE_SVGA, // 設定影像大小:SVGA
.jpeg_quality = 10, // 設定JPEG影像畫質,有效值介於0-63,數字越低畫質越高。
.fb_count = 1 // 影像緩衝記憶區數量
};
// 初始化攝像頭
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.printf("攝像頭出錯了,錯誤碼:0x%x", err);
return;
}
if (!SD_MMC.begin()) {
Serial.println("無法掛載SD記憶卡…");
return;
}
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) {
Serial.println("沒有插入SD記憶卡…");
return;
}
}
void loop() {
camera_fb_t *fb = NULL; // 宣告儲存影像結構資料的變數
fb = esp_camera_fb_get(); // 拍照
if (!fb) {
Serial.println("無法取得影像資料…");
return;
}
// 定義影像檔名和路徑
String path = "/photo" + String(++photoNum) + ".jpg";
// 以"寫入"模式開啟新檔
File file = SD_MMC.open(path.c_str(), "w");
if (!file) {
Serial.println("無法寫入檔案");
} else {
file.write(fb->buf, fb->len); // 寫入影像資料
Serial.printf("影像檔名:%s\t寬:%d\t高:%d\n", path.c_str(), fb->width, fb->height);
}
file.close();
esp_camera_fb_return(fb);
delay(1 * SECONDS * 1000UL);
}
最後,安信可科技的ESP32-CAM官方技術文件提到:
- 請確保模組輸入電源至少5V 2A,否則圖片有可能出現水波紋雜訊。
- ESP32 GPIO32腳控制攝像頭電源,當攝像頭工作時,請把GPIO32拉低(即:輸出低電位);esp_camera程式庫會幫我們處理。
- 由於GPIO0連接攝像頭XCLK,使用時請把GPIO0懸空,請勿接高低電位。

先說新年快樂:)
測試一下”圖解ESP32″一書12-4,將溫溼度寫入SD卡的範例,使用Arduino 2.0 IDE
編譯得到底下錯誤
In file included from C:\Users\will\AppData\Local\Arduino15\libraries\SD\src/utility/Sd2Card.h:26:0,
from C:\Users\will\AppData\Local\Arduino15\libraries\SD\src/utility/SdFat.h:29,
from C:\Users\will\AppData\Local\Arduino15\libraries\SD\src/SD.h:20,
from C:\Users\will\AppData\Local\Temp\.arduinoIDE-unsaved2023025-18244-ieu867.dec6\sketch_jan25a\sketch_jan25a.ino:2:
C:\Users\will\AppData\Local\Arduino15\libraries\SD\src/utility/Sd2PinMap.h:524:2: error: #error Architecture or board not supported.
#error Architecture or board not supported.
^
Multiple libraries were found for “SD.h”
Used: C:\Users\will\AppData\Local\Arduino15\libraries\SD
Not used: C:\Users\will\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6\libraries\SD
exit status 1
Compilation error: exit status 1
顯示有兩個lib分別是預設版本(適用arduino跟avr的版本)跟esp32版本,我猜IDE使用了預設的版本,導致預設library沒有找到對應的SPI pin腳位(Sd2PinMap.h)
我的作法是將預設的SD library移除,放到別的地方,這樣編譯就會用ESP32的lib,編譯就沒有問題了
自己覺得這是取巧的辦法,不知道您有沒有其他方式可以指教一下? 謝謝
感謝告知,我也會把它移除。新年快樂!