工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

ESP32のRAM周りの理解のなさを反省、カメラを使うならRAM増強が必要

背景

ここ最近、ESP32とカメラモジュールを繋いでをいじっていたのだが、RAM周りについて理解が深まったというか、そもそも理解が足らなかったと痛感した。


わかったこと

1. ユーザープログラム上の変数データで使えるRAMは120数kB

私の使っているESP-WROOM-32 DevKitCには、ESP-WROOM-32が載っていて、SRAMが520kBとあり、この分がすべて使えそうである。しかし、実際にユーザープログラム上の変数データで使えるのは、マニュアルにある通り、SRAM2の領域200kBのうちの一部のようだ。(ただし、あらゆる方法を試したわけではない。下記の方法で調べた限りで。)

以下のような全く何もしないArduinoプログラムを書き込んだ場合、

void setup(){

}

void loop (){

}

Arduino IDEの書き込みログで、約307kB使えると表示される。

最大327680バイトのRAMのうち、グローバル変数が13228バイト(4%)を使っていて、ローカル変数で314452バイト使うことができます。

一方、こちらを使って残りメモリを求めると、setup関数スタート時点で、

127168

となる。結局使えるのは124kB程度。背景で動いているFree RTOSなのか、Arduinoのせいなのか細かいことはわからないが。

以下のように、各変数の配置先アドレスを調べてみる。

const int a = 1; // constはFlashメモリに配置される
int c[256 * 108 + 185] = {0}; // グローバル変数としてRAMが消費される
int b = 2; // グローバル変数としてRAMが消費される

void setup() {

  int i = 0;

  Serial.begin(115200);
  Serial.printf("a, address=%x\n", &a);
  Serial.printf("i, address=%x\n", &i);
  Serial.printf("b, address=%x\n", &b);
  Serial.printf("c, address=%x - %x\n", &c[0], &c[256 * 108 + 185 - 1]);

}

void loop (){

}

マニュアルと照らし合わせると、aは、constなのでFlashメモリに配置されている。i, b, cこれはSRAM2の領域だ。他にもいろいろやってみたが、SRAM1への配置はされなかった。どうやら、SRAM2の120kB強くらいしか使えないらしい。

a, address=3f400178
i, address=3ffb878c
b, address=3ffbdbb4
c, address=3ffbfdd0 - 3ffdb0b0


2. PSRAMを外付け(External)することでRAMが増強できる

マニュアルを読むと、External Memoryという項目が確認できる。 SPI接続のSRAMを外付けすることで、4MBまでRAMが増強できるらしい。

ESP-IDF Programming Guideには、より詳しくプログラムから利用する方法がかかれている。 やってはいないが、Arduino IDEのメニューバー>ツールの中にPSRAMという項目があってこれを有効化すれば、外付けSRAMが使えそうだ。


3. ESP32-CAMはPSRAMが増強されている

最近、ESP32にカメラを繋いでいじっていたときに、最高解像度設定(UXGA)で撮影したところ、一見ちゃんと取れたかに見えたのに、実は画像の一部が欠けて表示されていなかった。プログラムをいろいろいじってわかったのだが、上述の使用可能メモリ量のせいで、画像全体のデータが保持できていないことがわかった。

じゃあ、Amazonなどで格安で売っているESP32-CAMはどうしてちゃんと撮れるのか?というと、上述のPSRAMが増強されているから、ということがわかった。

ESP32-CAMの基板の写真をよく見ると、IPS6404という8本足のICが載っていて、調べたらこれがまさに外付けSRAMだった。まあ、カメラモジュール基板として作られているので必要なRAM容量が確保されているのは当たり前ではあるが、改めてちゃんと見ると納得。


3. ESP32-CAMが使えないとすると、PSRAMが一緒に載っているモジュールESP32-WROVER-Eを使って、カメラを繋げばいい

とはいえ、Amazonなどで売られているものの写真を見る限り、ESP32-CAMは技適をクリアしていない。(ように見える。)技適マークのあるものは見つからない。米国のFCCなどの認証はクリアしている。 総務省の説明によれば、FCC FCC認証OKのものは持ち込んでから90日は使えるとあるが、主に観光で来た外国人の持ち物を想定した話なので、ESP32-CAMをAmazonで買ったとしてそこから90日という風に適用されるのだろうか? また、技適未取得のものでも申請すれば一定期間使えるというルールの運用が始まった?のか、始まるのかわからないが、申請はしないといけない。

参考1 参考2

なので、結局ESP32-CAMが日本で大っぴらに使える状況なのかどうか、いまいち自信が持てないので、買うのを避けた。

じゃあ、ESP32-CAMが使えないとすると、手持ちのESP32に外付けSRAMをつければいいのだが、上述のIPS6404も、ESP-IDF Programming GuideにあるESP-PSRAM32も、ネットで探しても全く入手ルートがなさそうだった。ニーズがあまりないんだろうなあ。と、悩んでいたが、色々調べた結果、ESP32-WROVER-EというモジュールにはPSRAMが載っていて、技適マークも付いていて、すぐに使える開発ボードの形で秋月に売られてる

akizukidenshi.com

ことがわかった。 なので、これを買えばよい。もともと、ESP32-WROVER-Eというものがあることは知っていたのだが、手持ちのESP-WROOM-32で間に合ってしまったので全然気にしていなかった。やっと存在意義を知ることができた。


ArduinoでRAMの残りを求める関数を高速化する

Arudinoで、RAMの残りを求める関数として、以下のサイトにavailableMemoryというものが紹介されている。

https://playground.arduino.cc/Code/AvailableMemory/

これは、mallocの戻り値がNULLでなくなる=確保できるまで1バイトずつサイズを小さくして試すという仕組み。 原理が単純で分かりやすいのだが、ESP-WROOM-32などRAM容量の大きなものに対してそのまま使うと、非常に動作が遅い。また、残り容量次第で計算時間が大きく変化する。

そこで、高速化をしてみた。

mallocの戻り値がNULLでなくなるごとにstepという変数の値を半分に小さくしていく。つまり、バイナリサーチだ。size=1MB、step=128kBであれば、最悪でも40回程度で探索が終わってくれる。運が良ければ16回。

size_t availableMemory() {
  
  size_t size = 1024 * 1024; // 最大サイズ
  size_t step = 128 * 1024; // 探索ステップ
  
  while (true) {
    byte *buf;
    while ((buf = (byte *) malloc(size)) == NULL) {
      size -= step;
    }
    free(buf);
    //Serial.printf("step=%d, size=%d\n", step, size);
    step /= 2;
    if (step == 0) break;
    size += step * 2;
  }

  return size;
}

実際、使ってみると、非常に素早く終了する。デバッグ用に、プログラムの各所に入れても、それほど動作に影響を与えないだろう。

秋月で売っていたOV2640使用200万画素カメラをESP32につないで動かす

概要


秋月で売っているOV2640使用200万画素カメラをESP-WROOM-32につないで動作確認をした。

※2022/01/29追記
ESP-WROOM-32だけだと、RAM容量不足でUXGA解像度での撮影はできないことに注意。詳細は、後述UXGA解像度での撮影テストを参照。


背景と目的


以前、こちらの記事で、カメラで成長記録を自動的に撮りたいと書きながら全く手がつかず2年近くたってしまったが、最近ようやく実行に移せそうな状況になった。そこで、自動水やり器のオプションとして追加できそうなカメラがないか探したところ、秋月で売っているOV2640使用200万画素カメラが、価格も手ごろで解像度もまずまずだったので、試しにESP-WROOM-32につないで撮影してみることにした。

詳細


0. 環境


1. 配線

秋月のサイトに載っている仕様書を見ても、いまいちわからかなったのだが、

https://www.mgo-tec.com/blog-entry-esp32-ov2640-ssd1331-test1.htmlhttps://www.mgo-tec.com/blog-entry-esp32-ov2640-ssd1331-test1.html/2

を参考にさせていただき、というか、まるまる同じ配線を行った。ブレッドボードに挿せるように20本をピンヘッダ、ピンフレームにはんだ付けするのはとても面倒だったが、まあ仕方ない。


2. ソフトウェア

2.1 スケッチ

Arduino core for the ESP32のサンプルスケッチの中に、CameraWebServerというものがある。これは、ESP32上にWebServerを立て、ESP32と同じワイヤレスLANに接続されたスマホやPCでカメラの画像が見られるので、動作確認がしやすい。そこで、これを基に以下のような感じでコードを書いた。 Select camera modelという項目とcamera_pins.hのインクルードは不要なので削除し、その代わりにピン設定を定義。

#include "esp_camera.h"
#include <WiFi.h>

// ピン設定
#define Y2_GPIO_NUM 32
#define Y3_GPIO_NUM 35
#define Y4_GPIO_NUM 34
#define Y5_GPIO_NUM 5
#define Y6_GPIO_NUM 39
#define Y7_GPIO_NUM 18
#define Y8_GPIO_NUM 36
#define Y9_GPIO_NUM 19
#define XCLK_GPIO_NUM 27
#define PCLK_GPIO_NUM 23
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 26
#define SIOD_GPIO_NUM 21
#define SIOC_GPIO_NUM 22
#define PWDN_GPIO_NUM -1 // 2021/2/27 GNDに接続なので割り当てなし修正。
#define RESET_GPIO_NUM -1 // 2021/2/27 割り当てなしに修正。

// 接続先Wi-Fiアクセスポイント
const char* ssid = "*******";
const char* password = "*******";

:

// 以下、サンプルと同じため省略

2021/2/27 こちらの記事に従い修正。


2.2 書き込み

カメラとWi-Fiを同時に使うだけあってプログラムサイズが大きい。デフォルトの設定では194%となってしまい書き込めなかった。そこで、以下のようにPartition Schemeを変更したらちゃんと書き込めた。


3. 動作確認

手持ちのスマホWebブラウザから、シリアルに表示されたIPアドレスにアクセスしたところ、以下のように設定画面が表示された。

設定画面の一番下に、Start Streamボタンがあるので押したところリアルタイムに映像が表示された。ひとまず成功。しかし、初期設定では、VGA画質で余り画質が良くない。

解像度をUXGAに変更したところ、以下のようにかなりきれいになった。これなら、植物の画像もまあまあきれいに撮れるか。ただし、画角が狭いのは気になる。

2022/01/29追記
画角が狭いのは、ESP-WROOM-32のRAM容量不足でUXGA解像度の画像データが扱えず、画像の下端が切れてしまっているせい。結局、この後の検討で、RAM容量を増強したESP32-WROVERを使用することでちゃんと撮影できるようになった。詳細は、以下。

というわけで、ESP32のサンプルのおかげで動作確認はすんなり終わってしまった。ひとまず、これでカメラはちゃんと使えそうだということが分かった。ブレッドボードからの線出しでもちゃんと動いたので、ESPから少し離して稼働させるという使い方もできそうだ。


まとめと今後の課題


自動水やり器のオプションとして追加できそうな小型のカメラモジュールをESP32につないで動作させることができた。次は、撮影した画像をクラウド等に保存させてみたい。