M5Cameraのカメラ画像をM5StackのLCDに表示する

# はじめに

12/5 に [M5Camera](https://www.switch-science.com/catalog/5207/) が発売されたので、早速買ってみました。
M5Stackに接続して使うカメラモジュールかと思っていたら、M5Camera自体にESP32が搭載されており、単体で動作させることができるようです。

最初からネットワークカメラとして動作するプログラムが書き込まれており、 [ESP32Cam/M5Camera クイックスタート](https://m5stack.github.io/m5-docs/#/ja/quick_start/m5camera/m5camera_quick_start) を見ながらPCからSSID:M5CamのWifiアクセスポイントに接続し、ブラウザで http://192.168.4.1/ にアクセスするだけでカメラ画像がブラウザにリアルタイムに表示されました。

# M5StackのLCDにカメラ画像を表示する

PCのブラウザで表示するのもよいですが、せっかくM5シリーズなので、M5StackのLCDに表示したいと思います。
公式のサンプルプログラムがあるかな？と思ったのですが、2018/12/16現在はありませんでした。

## 先駆者を発見

Instructablesで、同じことをやってる人を発見。海外ではもっと早く販売されていたのでしょうか。
[M5Cam X M5Stack: 5 Steps](https://www.instructables.com/id/M5Cam-X-M5Stack/)
この中に、M5Stack-Cam-ViewerというM5StackでM5Cameraの画像を表示するソフトのリポジトリを発見。
[moononournation/M5Stack-Cam-Viewer: Arduino M5Cam viewer for M5Stack](https://github.com/moononournation/M5Stack-Cam-Viewer)
M5Camera側のプログラムもありますが、M5Camera側の開発環境がESP-IDFしか対応しておらず、開発環境の準備が大変そうだったのと、ざっと見たところM5Camera側は変更しなくても行けそうな気がしたのでM5Stack側だけ試してみることにしました。

## そのままでは動かない

早速クローンしてM5Stackに書き込んで実行してみたのですが、定期的に再起動するような挙動をしてしまい動きませんでした。
シリアルモニタには以下のような表示が繰り返されていました。

```text:シリアルモニタ
rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1100
load:0x40078000,len:9220
load:0x40080400,len:6300
entry 0x400806a4
M5Stack initializing...OK
[HTTP] begin...
[HTTP] GET...
[HTTP] GET... code: -11
[HTTP] GET... failed, error: read Timeout
[HTTP] begin...
[HTTP] GET...
[HTTP] GET... code: -11
[HTTP] GET... failed, error: read Timeout
[HTTP] begin...
[HTTP] GET...
[HTTP] GET... code: -11
[HTTP] GET... failed, error: read Timeout
[HTTP] begin...
[HTTP] GET...
[HTTP] GET... code: 200
[HTTP] size: 15200
***ERROR*** A stack overflow in task loopTask has been detected.
abort() was called at PC 0x4008d210 on core 1

Backtrace: 0x4008cfc8:0x3ffae240 0x4008d1f9:0x3ffae260 0x4008d210:0x3ffae280 0x400904e7:0x3ffae2a0 0x40091ff4:0x3ffae2c0 0x40091faa:0x00000000
```

何回かリトライして接続はでき、HTTPのGetは成功しているようなのですが、スタックオーバーフローが発生しています。M5Stack-Cam-Viewerのソースコードを読むと、下記のようなところがありました。

```c++:M5Stack_Cam_Viewer.ino
        // get lenght of document (is -1 when Server sends no Content-Length header)
        int len = http.getSize();
        Serial.printf("[HTTP] size: %d\n", len); // (1)

        if (len <= 0) {
          Serial.printf("[HTTP] Unknow content size: %d\n", len);
        } else {
          // create buffer for read
          uint8_t buff[len] = { 0 }; // (2)
```

シリアル出力に`[HTTP] size: 15200`とは表示されているので、(1)までは実行できているようです。スタックオーバーフローということなので、(2)の`uint8_t buff[len] = { 0 };`というところが怪しいです。というかここでしょう。

ひとまずバッファの定義を`loop()`の外に静的にしてみることにします。サイズがわからないのですが、`[HTTP] size: 15200`とあったのでひとまず20kbほど定義してみることにしました。

```cpp:M5Stack_Cam_Viewer.ino
uint8_t buff[20 * 1024] = { 0 }; // 追加
void loop() {
:
          // create buffer for read
//          uint8_t buff[len] = { 0 }; // コメントアウト
```

しかしうまくいきませんでした。再度シリアルモニタを見ると以下の表示がされていました。

```text:シリアルモニタ
[HTTP] GET... code: 200
[HTTP] size: 31200
[HTTP] read: 4308
[HTTP] read: 4308
[HTTP] read: 5744
[HTTP] read: 5744
Guru Meditation Error: Core  1 panic'ed (InstrFetchProhibited). Exception was unhandled.
Core 1 register dump:
```

スタックオーバーフローではないようですが、`[HTTP] size: 32100`とあるので、バッファのサイズが足りていなかったようです。
下記のようにバッファサイズを20kb→64kbに修正しました。

```cpp:M5Stack_Cam_Viewer.ino
uint8_t buff[64 * 1024] = { 0 }; // 20->64に変更
```

# 成功

表示できました。すばらしい。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/70022/991bd36f-c089-4772-18e3-027c6abf4459.png)

[この記事](https://www.instructables.com/id/M5Cam-X-M5Stack/) の動画では、1fpsくらい出ているようなのですが、私の手元だと0.2fps（1回の更新に5秒近くかかる）くらいしか出ていません。原因はわかりませんが、LCD画面が上から下まで更新されるのが目で見てわかるくらい遅いので、そのあたりなのだと思います。

現状M5Cameraがアクセスポイントなのですが、既存のWifiに接続するにはM5Camera側のコードを修正しなければいけません。M5Camera側のコードがいじれると、単体で画像認識のプログラムを走らせたりなどいろいろできそうなのですが、ArduinoIDEに対応してくれないかなあ…