概要
今回はESP32-CAMのソフトとハードの解析です。ハード、ソフトの順に説明して行きます。
ハードの解析
”ESP32-CAM Video Streaming and Face Recognition with Arduino IDE” にヘッダーピンの説明が
"https://github.com/SeeedDocument/forum_doc/blob/master/reg/ESP32_CAM_V1.6.pdf" に回路図が有りました。
ヘッダーピン
片側8ピン、計16本のヘッダーピンが出ています。
右側
- GPIO1,3: シリアル通信用
- GPIO0: 起動時のモード選択
- GPIO16: 内部PSRAMの制御
左側
- GPIO2は起動時に関係するポート。使用を避けます。
- その他のピンはSDカードに配線されています。
- 信号の注記からSD_MMCの信号線4本モードと思われます。
- 下記のコードでSDカードにデータを保存出来ました。
- SD_MMCの4ビットモードを1ビットモードにすればポートが3個フリーになります。
- "SD_MMC.begin();" を "SD_MMC.begin("/sdmmc", true);"に変更して1ビットモードを試したところ問題なく動きました。
- これでポートGPIO4,12,13の3ポートがフリーになります。
- SD_MMCは使用条件が厳しいのでSD_SPIでSDカードが使用出来ないか確認して見ました。
- これも動きました。これで
- SD_MMCの1ビットモードを使えばGPIO4,12,13が
- SPIを使えばGPIO4,12がフリーになります。
#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()
{
}
#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()
{
}
開発が終わればシリアル通信は必要無いので、GIPO1,3がフリーになるとすればヘッダーピンに出ているポートで ユーザが使えるポートはGPIO4,12,13,1,3あたりになる
フラッシュ
回路図からGPIO4につながっている事がわかりました。
下記でオンオフ出来ました。
pinMode(4, OUTPUT);
}
void loop() {
digitalWrite(4, HIGH);
delay(250);
digitalWrite(4, LOW);
delay(250);
}
フラッシュのポートがGPIOの4と分かったのでフリーポートは 1,3,12,13 の4つ
ボード上の小さいLED
これはGPIO33につながっていました。ヘッダーピンには出ていません。
ソフトの解析
ピン定義
"board_config.h" でボードの種類を指定し、ピン定義は "camera_pins.h" で行っています。 AI−THINKERは下記となります。
#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
初期化
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_QQVGA 160 × 120 4:3 FRAMESIZE_QCIF 176 × 144 11:9 FRAMESIZE_HQVGA 240 × 176 4:3 FRAMESIZE_240X240 240 × 240 1:1 FRAMESIZE_QVGA 320 × 240 4:3 FRAMESIZE_CIF 400 × 296 4:3 FRAMESIZE_HVGA 480 × 320 3:2 FRAMESIZE_VGA 640 × 480 4:3 FRAMESIZE_SVGA 800 × 600 4:3 FRAMESIZE_XGA 1024 × 768 4:3 FRAMESIZE_HD 1280 × 720 16:9 FRAMESIZE_SXGA 1280 × 1024 5:4 FRAMESIZE_UXGA 1600 × 1200 4:3
- 25行 最新の画像を取得:
- ESP32の処理の関係で最新の画像が取得出来ない事が有るようです。
- この指定で取得する画像は常に最新となります。
- 26〜28行 PSRAM有り前提
- AI-THINKER はPSRAMを持っているのでPSRAM有りを前提にしています。
- 26行:config.fb_location = CAMERA_FB_IN_PSRAM; <- PSRAM有り宣言
- 27行:画像用バッファー <- 有りの場合2。なしなら1。
- 28行:画像の粗さ。数字が大きくなると荒くなります。でも処理は軽くなる
- AI-THINKER はPSRAMを持っているのでPSRAM有りを前提にしています。
- 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 〜 2 | 0 | 明るさ調整 |
| set_contrast | -2 〜 2 | 0 | コントラスト |
| set_saturation | -2 〜 2 | 0 | 彩度 |
| set_sharpness | -2 〜 2 | 0 | シャープネス |
| set_denoise | 0 (オフ) / 1 (オン) | 1 | ノイズ除去 |
| set_ae_level | -2 〜 2 | 0 | 自動明るさ調整 |
| set_exposure_ctrl | 0 (オフ) / 1 (オン) | 1 | 自動露出 |
| set_gain_ctrl | 0 (オフ) / 1 (オン) | 1 | 自動ゲイン |
| set_whitebal | 0 (オフ) / 1 (オン) | 1 | 自動ホワイトバランス |
| set_wb_mode | 0〜4 | 0 | ホワイトバランスモード |
| set_special_effect | 0〜6 | 0 | 特殊効果 |
| set_vflip | 0 (オフ) / 1 (オン) | 0 | 上下反転 |
| set_hmirror | 0 (オフ) / 1 (オン) | 0 | 左右反転 |
| set_framesize | FRAMESIZE_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)
画像の取得
画像の取得は下記の様に行います。
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カードに保存するスケッチの例です
#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の概要が分かったので、次回から監視カメラの製作に入ります。