Arduino環境でESP32-CAMを使う

it

Arduino環境で手軽にカメラモジュールを使用することができるESP32-CAMを使ってみます。
シリアルモジュールを使用した書き込みとサンプルのWebServerを動かします。
小型LCD ST7735にカメラ撮影画像表示します。

ESP32-CAM

小型の基板に、CAM(OV-2640)を搭載したESP32基板。
撮影に使用できる高輝度LEDフラッシュや、保存するためのマイクロSDカードスロットを搭載。

多くは技適が対応していないものですが、AitendoのESP32-CAMキットなど技適対応品もあります。

主なスペック

接続無し
CPUESP32-WROOM-32
Xtensa 32bit LX6 240MHz
フラッシュMemory4MB
GPIO10
ADC0
UART1(スケッチ書き込み用)
I2C0
SPI1
LED1 撮影用高輝度(GP4)

ピン配置

外観

画像はAitendoで購入したESP32-CAMキットです。
ESP-WROOM32を半田実装が必要で大変ですが技適付きです。

使ってみた

画像はESP32-CAMのサンプルコードを実行した画面です。
ESP32-CAMがCAMERA Serverになり、Wifiで撮影画像を表示しています。
モーションセンサーなどを使い、自宅の防犯カメラなどがワイヤレスでできそうです。

使用感

Good

手軽にカメラモジュールで撮影できる。

Bad

書き込み用のUSBコネクタがないため、書き込みに手間がかかる。
使えるピンが少ないので、LCDを使用すると他にできることがなくなる。

そのほか
国内、AliExpressでも\1,000前後で入手性もよいですが、ほぼ技適対応していません。
一部偽技適マーク(番号のない技適マークだけ)などがあるので注意が必要です。

技適付きESP32カメラモジュールは、ESP32-WROVER, ESP32-S3なども対応してきました。
基板は少し大柄ですが使用できるピンが多いのでESP32-WROOMよりも使い勝手はよいです。

小型のマイコン基板で撮影できるので、モーションセンサやフラッシュLEDと組み合わせて防犯カメラを作ったり、野菜や花の成長をタイムラプス撮影などできそうです。

OV2640画角の違うモジュールがいくつかあり、ほとんどは標準で66°がセット売りされているようです。
他にも画角の広い120°、160°があり、これらの撮影した画像は準備ができたら追加していきます。

準備

ライブラリのインストール

ボードライブラリ

ESP32-CAMを使用するためにArduino IDEのボードマネージャからESP32用のライブラリのインストールとボードの選択をします。

ボードマネージャのURLhttps://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
検索ESP
ボードライブラリesp32 by Espressif Systems バージョン x.x.x※
選択するボードツール > ボード > esp32 > AI Thinker ESP32
※ x.x.x Sep/2022 動作確認は2.0.5を使用しています

モジュールライブラリ

サンプルスケッチでは、LCD ST7735モジュールを使用します。
モジュールを使用しない場合インストールの必要はありません。

機能/モジュールライブラリ名検索確認時のバージョン
ST7735Adafruit ST7735 and ST7789
Library by Adafruit
ST77351.9.3
ST7735Adafruit GFX Library by AdafruitGFX1.11.3

スケッチの書き込み

USB-シリアル変換アダプタ

ESP32-WROOMでの書き込みと同じで、GPIO0とGNDをショートした状態でUARTから書き込みを行います。
当方がAitendoで購入したESP32-CAMではなぜかアダプタが使用できないのでこちらの方法で書き込みを行っています。

シリアルUART変換アダプタにはFT232RLを使用し、VCCからは5Vを出力する設定にしています。
スケッチの書き込み前に、GPIO0とGNDをショートさせた状態で電源を投入します。
ArduinoIDEからスケッチの書き込みボタンを押すことで書き込みを開始します。
書き込み終了後はGPIO0とGNDの配線を外して電源を再投入します。

ESP32-CAM配線USB-シリアル
5VVCC
GNDGND
GPIO0GND
GPIO1RX
GPIO3TX

ESP32-CAM書き込みアダプタ

ESP32-CAMの専用アダプタでソケットにピンを挿すだけで書き込みができます。
GPIO0とGNDをショートさせなくてもスケッチの書き込みができるので便利です。

ESP32-CAMと合体するとすっきりとした状態で書き込みができます。
アダプタ側のMicroBとパソコンを接続し、ArduinoIDEの書き込みボタンからスケッチの書き込みをします。
Wifiを使用したカメラスケッチではこのまま使用することができますが、LCDなどを使用する場合はアダプタを外してLCD側の基板に載せ替える手間が発生するので少し面倒です。

トラブル

■起動しない

ESP32系ではWifiを使用すると突入で電力不足になり、起動ループになることがあります。
カメラモジュールを使用するだけでも何度か起動ループになったので、対策方法を同じく電源強化をします。

・USBハブ電源供給の場合
バスパワーのみだと不足することがあるので、AC電源付きのUSBハブを使用する。

使い方

カメラ webserver

説明

ESP32-CAMをwebserverにして映像配信します。

Wifiに接続するので、SSIDとパスワードを用意してスケッチに書き込みを行ってください。
スケッチを書き込みESP32-CAMを起動したら、シリアルモニタでIPを確認します。

ブラウザを起動し、IPを入力するとカメラの操作画面に切り替わります。
簡単な操作は結果にて紹介します。

スケッチ

サンプルスケッチは以下の操作で読み込みます。
ファイル(F) > スケッチ例 > ESP32 > Camera > CameraWebServer

スケッチ上部の、Select camera mode の#define定義個所を
#define CAMERA_MODEL_AI_THINKER // Has PSRAM のコメントを外し、
それ以外をすべてコメントにします。

36行目の const char* ssid = “*****” 箇所をWifiのSSIDに
37行目の const char* password = “*****” をWifiのパスワードに書き換えをします。

結果

ESP32-CAMを起動するとシリアルモニタに接続の様子と取得したIPアドレスが表示されます。

なぜか1行目は文字化けしやすいです。
… はWifiと接続中のようですが、電波が弱かったりすると接続できません。
表示されたIPアドレスをブラウザのURLに入力すると、カメラのWebサーバー画面が表示されます。

Get Still ボタンをおすことで撮影します。
画面左側の設定で画質やサイズを変更できます。
Start Streamボタンを押すことで動画配信ができます。

撮影画像をST7735に表示

説明

タクトスイッチとST7735を使い、ボタンが押されたら撮影してST7735に表示します。

そのほかのグラフィックのサンプルはArduinoサンプルスケッチを参照してください。
ファイル(F) > スケッチ例 > Adafruit ST7735 and ST7789 Library > graphicstest

配線

スケッチ書き込みと電源供給のためにFT232RLを使用していますが、配線図からは省略しています。

スケッチ

カメラを使用するための関連ファイルが必要なのでサンプルスケッチ単体で動作しません。

ArduinoIDEからCameraWebServerのサンプルスケッチを読み込み、CameraWebServer.inoの内容をサンプルスケッチに置き換えることで動作します。

#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <TJpg_Decoder.h>

#include "esp_camera.h"
#define CAMERA_MODEL_AI_THINKER // Has PSRAM

#include "camera_pins.h"

#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS   15  // Chip select control pin
#define TFT_DC    2  // Data Command control pin
#define TFT_RST   12

#define PIN_BTN 16

Adafruit_ST7735 tft = Adafruit_ST7735(&SPI, TFT_CS, TFT_DC, TFT_RST);

bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
   // Stop further decoding as image is running off bottom of screen
  if ( y >= tft.height() ) return 0;
   tft.drawRGBBitmap(x, y, bitmap, w, h);
   // Return 1 to decode next block
   return 1;
}


void init_camera() {
  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;

  if(psramFound()){
    config.frame_size = FRAMESIZE_QQVGA;
    config.jpeg_quality = 10;
    config.fb_count = 1;
    Serial.println("PSRAM");
  } else {
    config.frame_size = FRAMESIZE_QQVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  #if defined(CAMERA_MODEL_ESP_EYE)
    pinMode(13, INPUT_PULLUP);
    pinMode(14, INPUT_PULLUP);
  #endif

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);

    for(;;)
    {

    }

  }

  sensor_t * s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    Serial.println("PID");
    s->set_vflip(s, 1);
    s->set_brightness(s, 2);
    s->set_saturation(s, 0); 
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("ESP32-CAM Picture");
  SPI.begin(TFT_SCLK, -1, TFT_MOSI, TFT_CS);
  tft.initR(INITR_BLACKTAB); 
  
  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);
  init_camera();

  tft.setCursor(20, 50);
  tft.setTextColor(ST77XX_RED);
  tft.setTextSize(2);
  tft.println("DEADBEEF");

  pinMode(PIN_BTN, INPUT);

  TJpgDec.setJpgScale(1);

  TJpgDec.setCallback(tft_output);
}

void take_picture(){

  camera_fb_t * fb = NULL;

  fb = esp_camera_fb_get();
  if (!fb) {
      Serial.println("Camera capture failed");
      return;
  }

  uint16_t w = 0, h = 0;
  TJpgDec.getJpgSize(&w, &h, fb->buf, fb->len);
  TJpgDec.drawJpg(0, 0, fb->buf, fb->len);     
  esp_camera_fb_return(fb);
}   

void loop() {

  while (true) {
     int state = digitalRead(PIN_BTN);
     if (state == LOW) {
       take_picture();
     }
  }
}

結果

ボタンを押すことで撮影した画像がST7735に表示されました。
ボタンを押したままにすることで連続で撮影した画像が動画のように見えます。

コメント

タイトルとURLをコピーしました