本文將運用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,編譯就沒有問題了
自己覺得這是取巧的辦法,不知道您有沒有其他方式可以指教一下? 謝謝
感謝告知,我也會把它移除。新年快樂!