ESP32-CAM開發板(二):esp32-camera程式庫與縮時攝影程式

本文將運用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):從微控器傳給攝像頭的外部時脈訊號。
  • VSYNCHREFPCLK:垂直和水平同步時脈,以及從攝像頭傳給微控器的像素時脈訊號。
  • D0~D7:從攝像頭傳送像素資料給微控器。
  • RESET、PWDN:重置和斷電,用於重置攝像頭模組。

ESP32-CAM開發板的OV2640攝像頭,連接ESP32晶片的腳位定義如下:

OV2640接腳接腳中文名稱ESP32-CAM接腳
RESET重置
POWER DOWN斷電GPIO32
SDAI2C序列資料GPIO26
SCLI2C序列時脈GPIO27
HREF/ HSYNC水平同步訊號GPIO23
VSYNC垂直(影格)同步訊號GPIO25
PLCK像素時脈GPIO22
XCLK/XVCLK外部時脈GPIO0
D0資料0GPIO5
D1資料1GPIO18
D2資料2GPIO19
D3資料3GPIO21
D4資料4GPIO36
D5資料5GPIO39
D6資料6GPIO34
D7資料7GPIO35

OV2640的RESET(重置)腳不用指定對應的ESP32腳,因為從ESP32-CAM的電路圖可以看出,重置腳固定接在3.3V。

OV2640攝像頭的RESET(重置)腳電路

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記憶卡模組

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

microSD記憶卡電路

使用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懸空,請勿接高低電位。
Posts created 483

2 thoughts on “ESP32-CAM開發板(二):esp32-camera程式庫與縮時攝影程式

  1. 先說新年快樂:)
    測試一下”圖解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,編譯就沒有問題了
    自己覺得這是取巧的辦法,不知道您有沒有其他方式可以指教一下? 謝謝

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top