2. ソフトとハードの解析
(ESP32-CAMを使う)

更新日: 2026.06.16

概要

今回はESP32-CAMのソフトとハードの解析です。ハード、ソフトの順に説明して行きます。

ハードの解析

”ESP32-CAM Video Streaming and Face Recognition with Arduino IDE” にヘッダーピンの説明が

ESP32-cam pin assigne

"https://github.com/SeeedDocument/forum_doc/blob/master/reg/ESP32_CAM_V1.6.pdf" に回路図が有りました。

Schematic of ESP32-CAM

ヘッダーピン

片側8ピン、計16本のヘッダーピンが出ています。

右側

  • GPIO1,3: シリアル通信用
  • GPIO0: 起動時のモード選択
  • GPIO16: 内部PSRAMの制御

左側

  • GPIO2は起動時に関係するポート。使用を避けます。
  • その他のピンはSDカードに配線されています。
  • 信号の注記からSD_MMCの信号線4本モードと思われます。
  • 下記のコードでSDカードにデータを保存出来ました。
  • SD_MMC.ino arduino
    #include "FS.h" #include <SD_MMC.h> void setup() { File fo; SD_MMC.begin(); fo = SD_MMC.open("/test.txt", FILE_WRITE); fo.println("This is test.\nThis is test."); fo.close(); SD_MMC.end(); } void loop() { }
  • SD_MMCの4ビットモードを1ビットモードにすればポートが3個フリーになります。
    • "SD_MMC.begin();" を "SD_MMC.begin("/sdmmc", true);"に変更して1ビットモードを試したところ問題なく動きました。
    • これでポートGPIO4,12,13の3ポートがフリーになります。
  • SD_MMCは使用条件が厳しいのでSD_SPIでSDカードが使用出来ないか確認して見ました。
  • SD_SPI.ino arduino
    #include "FS.h" #include <SD.h> #define sd_sck 14 #define sd_mosi 15 #define sd_ss 13 #define sd_miso 2 void setup() { File fo; SPI.begin(sd_sck, sd_miso, sd_mosi, sd_ss); SD.begin(sd_ss); fo = SD.open("/test1.txt", FILE_WRITE); fo.println("This is test.\nThis is test."); fo.close(); SD.end(); } void loop() { }
  • これも動きました。これで
    • SD_MMCの1ビットモードを使えばGPIO4,12,13が
    • SPIを使えばGPIO4,12がフリーになります。

開発が終わればシリアル通信は必要無いので、GIPO1,3がフリーになるとすればヘッダーピンに出ているポートで ユーザが使えるポートはGPIO4,12,13,1,3あたりになる

フラッシュ

回路図からGPIO4につながっている事がわかりました。

Result of Free_Font_Demo

下記でオンオフ出来ました。

void setup() {
pinMode(4, OUTPUT);
}

void loop() {
digitalWrite(4, HIGH);
delay(250);
digitalWrite(4, LOW);
delay(250);
}

フラッシュのポートがGPIOの4と分かったのでフリーポートは 1,3,12,13 の4つ

ボード上の小さいLED

これはGPIO33につながっていました。ヘッダーピンには出ていません。

Result of Free_Font_Demo

ソフトの解析

ピン定義

"board_config.h" でボードの種類を指定し、ピン定義は "camera_pins.h" で行っています。 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

// 4 for flash led or 33 for normal led
#define LED_GPIO_NUM 4

初期化

cam_init.ino arduino
void init_cam() { 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; //画像のフォーマット config.frame_size = FRAMESIZE_SVGA; //画面のサイズ 今回は800x600に指定 config.grab_mode = CAMERA_GRAB_LATEST; // 最新のフレームを指定 config.fb_location = CAMERA_FB_IN_PSRAM; // PSRAMを利用 config.fb_count = 2; config.jpeg_quality = 10; // 画質設定(10〜12バランスが良いです) // カメラの初期化実行 esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x\n", err); return; // 初期化に失敗した場合はここで処理を止める } Serial.println("Camera Init Success! (800x600 mode)"); sensor_t *s = esp_camera_sensor_get(); // initial sensors are flipped vertically and colors are a bit saturated if (s->id.PID == OV3660_PID) { s->set_vflip(s, 1); // flip it back s->set_brightness(s, 1); // up the brightness just a bit s->set_saturation(s, -2); // lower the saturation } }

AI-THINKERを前提に "WebCameraSever.ino" の "setup()" を書き換えています。

  • 2から20行
    • ここでピンが設定されます。
  • 21行:config.xclk_freq_hz = 20000000;
    • 動画の滑らかさに関係する設定。デフォルトは20MHz(MAX)です。
    • 動画が遅い時は値を小さくすれば改善されますが動きがぎこちなくなります。
    • 状況に合わせて調整。
  • 22行 画像のフォーマット:config.pixel_format = PIXFORMAT_JPEG;
    • カメラはJPEG形式で圧縮したデータを出力。
    • 通常JPEGを指定。
  • 23行 画像の解像度: config.frame_size = FRAMESIZE_SVGA;
    • 今回は FRAMESIZE_SVGA(800x600) を指定しています。
    • 解像度を大きくするとESP32の処理が追いつきません。
    • 指定出来る解像度
      定義値(framesize_t)解像度(幅 × 高さ)アスペクト比
      FRAMESIZE_QQVGA160 × 1204:3
      FRAMESIZE_QCIF176 × 14411:9
      FRAMESIZE_HQVGA240 × 1764:3
      FRAMESIZE_240X240240 × 2401:1
      FRAMESIZE_QVGA320 × 2404:3
      FRAMESIZE_CIF400 × 2964:3
      FRAMESIZE_HVGA480 × 3203:2
      FRAMESIZE_VGA640 × 4804:3
      FRAMESIZE_SVGA800 × 6004:3
      FRAMESIZE_XGA1024 × 7684:3
      FRAMESIZE_HD1280 × 72016:9
      FRAMESIZE_SXGA1280 × 10245:4
      FRAMESIZE_UXGA1600 × 12004:3
  • 25行 最新の画像を取得:
    • ESP32の処理の関係で最新の画像が取得出来ない事が有るようです。
    • この指定で取得する画像は常に最新となります。
  • 26〜28行 PSRAM有り前提
    • AI-THINKER はPSRAMを持っているのでPSRAM有りを前提にしています。
      • 26行:config.fb_location = CAMERA_FB_IN_PSRAM; <- PSRAM有り宣言
      • 27行:画像用バッファー <- 有りの場合2。なしなら1。
      • 28行:画像の粗さ。数字が大きくなると荒くなります。でも処理は軽くなる
  • 31行 ここで初期化:esp_err_t err = esp_camera_init(&config);
    • 設定したコンフィグ(config)でカメラを初期化します。
  • 38行以降 カメラがOV3660の場合の対応
    • カメラがOV3660の場合(大抵はOV2640)、画面を上下反転し画質を若干調整する必要があるようです

カメラの設定

ESP32-CAMの各種設定(画面の明るさ、シャープネス等)は構造体の値を変更することにより行います。 まず、sensor_t *s = esp_camera_sensor_get(); で構造体を作成し、関数 s->set_xxxxxx(s, 1); を使って構造体の値を設定します。 設定出来る項目を下記にまとめました。

設定項目設定値の範囲設定値目的・効果
set_brightness-2 〜 20明るさ調整
set_contrast-2 〜 20コントラスト
set_saturation-2 〜 20彩度
set_sharpness-2 〜 20シャープネス
set_denoise0 (オフ) / 1 (オン)1ノイズ除去
set_ae_level-2 〜 20自動明るさ調整
set_exposure_ctrl0 (オフ) / 1 (オン)1自動露出
set_gain_ctrl0 (オフ) / 1 (オン)1自動ゲイン
set_whitebal0 (オフ) / 1 (オン)1自動ホワイトバランス
set_wb_mode0〜4 0ホワイトバランスモード
set_special_effect0〜6 0特殊効果
set_vflip0 (オフ) / 1 (オン)0上下反転
set_hmirror0 (オフ) / 1 (オン)0左右反転
set_framesizeFRAMESIZE_VGA等 0画像の解像度
  • set_wb_mode(ホワイトバランスモード)
    • 0 : 自動 (Auto)
    • 1 : 晴天 / 太陽光 (Sunny)
    • 2 : 曇天 (Cloudy)
    • 3 : 日陰 / 屋内 (Office)
    • 4 : 蛍光灯 (Home)
  • set_special_effect(特殊効果)
    • 0 : なし (Normal)
    • 1 : ネガ反転 (Negative)
    • 2 : モノクロ (Grayscale)
    • 3 : 赤み強調 (Red Tint)
    • 4 : 緑み強調 (Green Tint)
    • 5 : 青み強調 (Blue Tint)
    • 6 : セピア (Sepia)

画像の取得

画像の取得は下記の様に行います。

camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
  • camera_fb_t * fb = NULL;
    • camera_fb_t型のインスタンスを宣言。
    • これが画像を収めるバッファーになります。
  • fb = esp_camera_fb_get();
    • 関数 esp_camera_fb_get()で画像を取得出来ます。
    • "fb" バッファーに保存された画像を必要に応じて処理(表示や保存)します。
  • esp_camera_fb_return(fb);
    • 処理が終わったら "fb" バッファーを開放します。

動画の取得

ESP32-CAMは動画を出力していません。クライアントのリクエストに対し静止画(jpeg)を送りパラパラ漫画の様に次々と ブラウザが表示しているだけです。よって動画の取得は、
   ”ESP32-CAMにhttp-serverを立てクライアント要求に対しひたすら静止画を送る”
となります。

写真を撮ってSDカードに保存するスケッチ

ESP32-CAM(AI-THINKER)で写真を撮り、SDカードに保存するスケッチの例です

sample_1.ino arduino
#include "esp_camera.h" #include "Arduino.h" #include "FS.h" #include "SD_MMC.h" // ピン設定 #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 #define LED_GPIO_NUM 4 void setup() { Serial.begin(115200); delay(300); //カメラの初期化 init_cam(); //写真の取得 camera_fb_t * fb = NULL; fb = esp_camera_fb_get(); //写真をSDカードへ保存 SD_MMC.begin(); File file = SD_MMC.open("/test.jpg", FILE_WRITE); file.write(fb->buf, fb->len); file.close(); //バッファーの開放 esp_camera_fb_return(fb); //終了 フラシュが一瞬光る pinMode(LED_GPIO_NUM, OUTPUT); digitalWrite(LED_GPIO_NUM, HIGH); delay(300); digitalWrite(LED_GPIO_NUM, LOW); } void init_cam() { 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; //画像のフォーマット config.frame_size = FRAMESIZE_SVGA; //画面のサイズ 今回は800x600に指定 config.grab_mode = CAMERA_GRAB_LATEST; // 最新のフレームを指定 config.fb_location = CAMERA_FB_IN_PSRAM; // PSRAMを利用 config.fb_count = 2; config.jpeg_quality = 10; // 画質設定(10〜12バランスが良いです) // カメラの初期化実行 esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x\n", err); return; // 初期化に失敗した場合はここで処理を止める } Serial.println("Camera Init Success! (800x600 mode)"); sensor_t *s = esp_camera_sensor_get(); // initial sensors are flipped vertically and colors are a bit saturated if (s->id.PID == OV3660_PID) { s->set_vflip(s, 1); // flip it back s->set_brightness(s, 1); // up the brightness just a bit s->set_saturation(s, -2); // lower the saturation } }

これでSDカードのルートに写真が "test.jpg" と保存されます。

次回は

ESP32-CAMの概要が分かったので、次回から監視カメラの製作に入ります。

SINCE 2026