【ESP32-CAM】CameraWebServerのWebUIをシンプル化

アプリ開発

またマイコンに手を出してしまいました。沼ってますね。時間が足りないです。

前回と同じESP32ですが、ESP32-CAMというカメラモジュールの制御ができるものです。

↓前回はこちらです。

ESP32-CAMにArduinoのプログラムを書き込めば、ネットワークWebカメラが自作できます。

本記事では、ESP32-CAMのサンプルプログラム「CameraWebServer」のWebUIをシンプル化しましたので、その方法を記載します。

ESP32-CAMとは

ESP32とカメラモジュールを搭載したマイコンボードです。

こんな感じでマイコンにカメラモジュールが付けられるようになっています。

技適のないものやUSBシリアルポートがないものが出回っていたりしますが、筆者は技適あり、USBシリアルポートありのものを購入しました。

AliExpressで2つで送料込み2千円程度でした。

一つは正常動作しましたが、マイコンの金属フレームが凹んでいました。

もう一つは、プログラムの書き込みができず、壊れていました。上の写真は壊れているほうです。

不良品を安く売りさばいている感じで、筆者のように外れを引く可能性もあり、あまりおすすめはできません。リンクも貼りません。

CameraWebServerとは

ESP32-CAMで使用可能なサンプルプログラムです。

githubに上がっています。

arduino-esp32/libraries/ESP32/examples/Camera/CameraWebServer at master · espressif/arduino-esp32
Arduino core for the ESP32. Contribute to espressif/arduino-esp32 development by creating an account on GitHub.

こちらを書き込めば、比較的簡単にネットワークWebカメラが出来上がります。

マイコンへの書き込み方法は、他サイトにいくつかありますので解説を省略します。

WebUIをシンプル化

シンプル化したい理由

サンプルプログラム「CameraWebServer」のWebUIは、高機能で、カメラモジュールの設定ができるようになっています。

しかし、カメラモジュールの設定は一度やれば、ほとんど変えませんので使わなくなります。

また、Webアクセスしたらすぐにライブカメラ表示したいですし、プレイヤーをつけて全画面表示もしたいです。

変更前

変更前のWebUIはこんな感じです。

色々設定できるようになっていて、最初はテンション上がりました。

変更後

変更後のWebUIです。

ライブ映像をプレイヤーの中に表示します。全画面ボタンをクリック or タップすれば、全画面になります。日常運用は、こちらで十分です。

WebUIの変更方法

WebUIの構成と変更方法

CameraWebServerでは、マイコン側の負荷を軽減するため、htmlファイルをgzip圧縮し、クライアントに配信しています。クライアント(ブラウザ)側でgzipを解凍させ表示させます。

HTTPヘッダーの「Content-Encoding」を「gzip」にすればこのようなことができるようです。知りませんでした。マイコンがサーバの場合は、クライアント(スマホやPC)のほうがスペックが高いので合理的ですね。

また、gzip化されたhtmlファイルは、「camera_index.h」にバイト配列として定義されています。

これを変更するには、新たなシンプル化したHTMLファイルを用意し、gzip化、バイト配列化して、「camera_index.h」の内容を置き換えればオッケーです。

設定値の固定

WebUIでカメラモジュールの設定ができなくなる代わりに、好みの設定をスケッチ(CameraWebServer.ino)内で設定しておきます。

筆者は下記2箇所を変更しました。

XCLK MHz設定

デフォルトでは、20MHzですが、25MHzのほうが調子が良いとどこかのサイトにあったため、変更しています。

【変更前】
config.xclk_freq_hz = 20000000;
【変更後】
config.xclk_freq_hz = 25000000;

フレームサイズ設定

解像度設定です。HDに固定します。

【変更前】
config.frame_size = FRAMESIZE_UXGA;
…
s->set_framesize(s, FRAMESIZE_QVGA);
【変更後】
config.frame_size = FRAMESIZE_HD;
…
s->set_framesize(s, FRAMESIZE_HD);

他にも、顔認識用のコードや不要なWebハンドラーを削除しましたが、必須ではないので省略します。

シンプル化したHTMLファイル

MJPG-streamerの映像を動画プレイヤー内に表示するには、まず、canvasに出力し、canvasの内容をvideoタグのソースに貼り付ける必要があり、JavaScriptで処理します。

canvasのサイズは、配信側に合わせてHD(1280px × 720px)に設定しています。

videoタグのサイズは、スマホでもプレイヤー全体が表示できるように小さいサイズ(320px × 180px)に設定しています。

index_ov2640.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
    <title>Web Cam</title>
</head>
<body>
    <canvas id="canvas" , width="1280px" , height="720px" hidden></canvas>
    <video id="player-canvas" controls autoplay loop muted poster="" playsinline width="320px" height="180px"></video>
    <script type="text/javascript">
        // MJPG-streamer -> CANVAS-TAG
        function drawCanvasFromMjpegStremer() {
            const canvas = document.getElementById("canvas");
            const ctx = canvas.getContext("2d");
            setInterval(() => {
                if (canvas && ctx) {
                    const cmr = new Image();
                    cmr.crossOrigin = 'anonymous';
                    cmr.src = document.location.origin + ":81/stream";
                    cmr.onload = () => {
                        ctx.drawImage(cmr, 0, 0);
                    };
                }
            }, 10000 / 60);
        }
        // CANVAS-TAG -> VIDEO-TAG
        function drawVideoFromCanvas() {
            const canvas = document.getElementById("canvas");
            const video = document.getElementById("player-canvas");
            const ctx = canvas.getContext('2d');
            canvasStream = canvas.captureStream(30);
            video.srcObject = canvasStream;
        }
        drawCanvasFromMjpegStremer();
        drawVideoFromCanvas();
    </script>
</body>
</html>

こちらの書き込みを参考にさせてもらいました。

Python の FastAPI で出力した MJPG-streamer を video タグに描画したい
# 前提・実現したいこと Javascript を使って、 MJPG-streamer のストリーミング画像をvideoタグに描画しようとしています。 しかし、 Canvas には画像が出力され、

変更手順

下記Pythonプログラムで、htmlファイルをgzip圧縮し、ヘッダーファイルの内容を出力します。

標準モジュールのみで実行可能で、追加するPythonモジュールはありません。

html2gzip.py

import gzip

f = open("index_ov2640.html", "r")
html_str = f.read()
f.close()

htmlgzip_bytes = gzip.compress(html_str.encode())
htmlgzip_hex = [htmlgzip_bytes[n:n+1].hex() for n in range(0, len(htmlgzip_bytes), 1)]

s = '#define index_ov2640_html_gz_len ' + str(len(htmlgzip_hex)) + '\n'
s += 'const uint8_t index_ov2640_html_gz[] = {\n  '
for n in range(len(htmlgzip_hex)):
    if n>0 and n%16==0: s += '\n  '
    s += '0x' + htmlgzip_hex[n].upper() + ', '
output_str = s[:-2] + '\n};'

print(output_str)

「index_ov2640.html」と「html2gzip.py」を同じディレクトリに置き、下記コマンドを実行すれば、ヘッダーファイルの内容が出力されます。

python html2gzip.py

「camera_index.h」の内容を全削除し、上記コマンド実行で出力された文字列に置き換えれば完了です。

コメント

録画サーバを立てて監視カメラを作ってみるのも面白いと思いますが、「CameraWebServer」では、クライアントは1台のみが接続可能なため、録画中はWebUIからの接続ができなくなりますので、微妙ですね。

コメント

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