M5Cameraのサンプルプログラムをビルドする(2020.02版 + 2021.05版追記)


はじめに

M5Stack社から発売されているM5Cameraは、ESP32 WROVERにカメラ(OVA2640)とセンサーを搭載した小型のWiFiカメラで、お値段も2000円以下でお手頃です。日本ではスイッチサイエンスさんなどから購入できます。技適も取得しています。(一部技適がないタイプもあるので注意。スイッチサイエンスさんのは技適あり)

長時間利用ではオーバーヒートするという課題もありますが、ちょっとした用途では便利なモジュールです。

M5Cameraは最初からWeb(HTTP)からカメラ画像を取得するプログラムが書き込まれているので、そのままビルドなしに使えます。

一方、自分でプログラムを書こうと思うと、開発環境がArduino IDEに対応しておらず(※)、ビルドが大変なのが課題です。メンテナンスやドキュメントも十分ではないようで、今回、いろいろハマった点をここで共有します。

<追記>
※ Arduino IDEで使う方法もあるとのこと。
- 「M5Cameraでスマホから操作できるWebカメラを作る(MacOS編)」 : https://qiita.com/nakazawaken1/items/57ea26d6981ff25912c9
- 「M5cameraでWebカメラを作る」 : https://ambidata.io/samples/m5stack/m5camera/

今回の対象は、

の、Masterの以下のコミットまでを対象にしています。

Latest commit 1c0504d on 12 Aug 2019

<2021/5/15 追記>
久しぶりにビルドしたところ以下の変更点があります。ご注意ください。

  • リポジトリ名がm5stack-cam-psramからm5stack-camになり、M5Camera Xにも対応
  • カメラのデフォルトがM5-Camera BからM5-Camera Xになっているので、変更する必要あり
  • 起動時にカメラが検出できない旨のエラーは発生しなくなっていますので、対応不要です。ただし、main.cのipアドレスの型定義修正はなおってないので修正必要です。また、make menuconfigの変更も依然必要です。

ESP-IDFの準備

ソースコード一式はこちらから取得します。

このREADME.mdにある手順に沿ってすすめます。

まずは、ESP-IDFをインストールする必要があります。Arudino IDEでESP32を使用できるようにしている場合、既にESP-IDFはインストールされていると思いますが、コンソールから各コマンドを使用することもあり、それとは別にインストールしました。私の環境はMacのため、以降の説明はMac環境のターミナルを使用するイメージとなっています。

make menuconfig

次に、make menuconfigを実行します。どこで実行するかがREADME.mdには書いていません。このリポジトリには複数のプロジェクトが含まれているのですが、購入時に入っているものはwifi_apなのでそのディレクトリ内に移動します。

$ cd m5stack-cam-psram/wifi/wifi_ap/

ESP-IDFの環境設定を有効にしておきます。

. $HOME/esp/esp-idf/export.sh

make menuconfigを実行します。

$ make menuconfig

ここで、README.mdでは、カメラの選択とpsramの有効化をするように書かれているのです、今のリポジトリは既にカメラがM5-Camera B(スイッチサイエンスで買えるもの)でpsramが有効になっており、特に変更する必要はありませんでした。

README.mdには記載ないですが、書き込みの際のデバイス名が、Macの場合は/dev/tty.SLAB_USBtoUARTなので、こちらの設定を変更しておきます。

ここで

$ make

でうまくいけばよいのですが、残念ながら現実は残酷でした。

CC build/esp32-camera/driver/twi.o
/Users/XXX/m5stack-cam-psram/wifi/wifi_ap/components/esp32-camera/driver/twi.c: In function 'pinMode':
/Users/XXX/m5stack-cam-psram/wifi/wifi_ap/components/esp32-camera/driver/twi.c:61:24: error: 'rtc_gpio_desc' undeclared (first use in this function); did you mean 'rtc_io_desc'?
     uint32_t rtc_reg = rtc_gpio_desc[pin].reg;
                        ^~~~~~~~~~~~~
                        rtc_io_desc
/Users/XXX/m5stack-cam-psram/wifi/wifi_ap/components/esp32-camera/driver/twi.c:61:24: note: each undeclared identifier is reported only once for each function it appears in
make[1]: *** [driver/twi.o] Error 1
make: *** [component-esp32-camera-build] Error 2

というエラーが発生します。

以下にあるように

make menuconfigで、rtc_gpio_desc配列のサポートを有効にしておく必要があります。

これでビルドできるかと思いきや、

/Users/XXX/m5stack-cam-psram/wifi/wifi_ap/main/main.c:232:17: error: incompatible types when assigning to type 'ip4_addr_t' {aka 'struct ip4_addr'} from type 'esp_ip4_addr_t' {aka 'struct esp_ip4_addr'}
       s_ip_addr = event->event_info.got_ip.ip_info.ip;
                 ^

というエラーが次に発生します。

ipアドレスの型定義が変わった模様。main.cのip4_addr_tesp_ip4_addr_tに変更します。

static ip4_addr_t s_ip_addr;
     ↓
static esp_ip4_addr_t s_ip_addr;
  s_ip_addr = *(ip4_addr_t*)&addr;
     ↓
  s_ip_addr = *(esp_ip4_addr_t*)&addr;

さて、これでコンパイルは通るようになるのですが、まだまだ問題が発生します。

次に、make flashでM5Cameraにイメージを書き込むのですが、

esptool write_flash: error: argument <address> <filename>: Detected overlap at address: 0x8000 for file: /Users/XXX/m5stack-cam-psram/wifi/wifi_ap/build/partitions_singleapp.bin

とエラーになり書き込みできません。

CONFIG_PARTITION_TABLE_OFFSETを8000から10000にしておく必要があります。

これで書き込みはできるようになります。これで動くかと思いきや、Webでアクセスしても画像が表示されません。

ここで、make monitorを使ってデバッグ情報を表示します。

I (1666) spiram: Reserving pool of 32K of internal memory for DMA/internal allocations
E (1716) ledc: requested frequency and duty resolution can not be achieved, try reducing freq_hz or duty_resolution. div_param=0
E (1716) camera_xclk: ledc_timer_config failed, rc=ffffffff
I (1726) sccb: pin_sda 22 pin_scl 23

I (1726) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (1756) gpio: GPIO[15]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
E (1806) camera: Detected camera not supported.
E (1806) camera: Camera probe failed with error 0x20004
E (1806) camera: Camera Init Failed

ということで、起動時にカメラが検出できない旨のエラーが発生しています。

これについての詳細は以下に記載があります。

対応としては、

wifi_ap/components/esp32-camera/driver/xclk.c

の以下の関数を修正する必要があります。

esp_err_t camera_enable_out_clock(camera_config_t* config)
{
    periph_module_enable(PERIPH_LEDC_MODULE);

    ledc_timer_config_t timer_conf;
    timer_conf.duty_resolution = 2;
    timer_conf.freq_hz = config->xclk_freq_hz;
    timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
    timer_conf.timer_num = config->ledc_timer;
    esp_err_t err = ledc_timer_config(&timer_conf);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
        return err;
    }

この関数について、まずは、以下の設定を追加します。

timer_conf.clk_cfg = LEDC_USE_APB_CLK;

また、以下の処理を呼び出す場所を、関数の最初から関数の最後に移動します。

periph_module_enable(PERIPH_LEDC_MODULE);

この2つの処理を対応した結果が、以下となります。

esp_err_t camera_enable_out_clock(camera_config_t* config)
{
    ledc_timer_config_t timer_conf;
    timer_conf.duty_resolution = 2;
    timer_conf.freq_hz = config->xclk_freq_hz;
    timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
    timer_conf.timer_num = config->ledc_timer;
    timer_conf.clk_cfg = LEDC_USE_APB_CLK;
    esp_err_t err = ledc_timer_config(&timer_conf);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
        return err;
    }

    ledc_channel_config_t ch_conf;
    ch_conf.gpio_num = config->pin_xclk;
    ch_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
    ch_conf.channel = config->ledc_channel;
    ch_conf.intr_type = LEDC_INTR_DISABLE;
    ch_conf.timer_sel = config->ledc_timer;
    ch_conf.duty = 2;
    ch_conf.hpoint = 0;
    err = ledc_channel_config(&ch_conf);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "ledc_channel_config failed, rc=%x", err);
        return err;
    }
    periph_module_enable(PERIPH_LEDC_MODULE);
    return ESP_OK;
}

これでようやく動作するイメージが作成できます。

これらの内容は後ほどプルリクしておく予定なので、最新では治ってるかもしれませんが、情報共有のためにここで残しておきます。