またマイコンに手を出してしまいました。沼ってますね。時間が足りないです。
前回と同じESP32ですが、ESP32-CAMというカメラモジュールの制御ができるものです。
↓前回はこちらです。
ESP32-CAMにArduinoのプログラムを書き込めば、ネットワークWebカメラが自作できます。
本記事では、ESP32-CAMのサンプルプログラム「CameraWebServer」のWebUIをシンプル化しましたので、その方法を記載します。
目次
ESP32-CAMとは
ESP32とカメラモジュールを搭載したマイコンボードです。
こんな感じでマイコンにカメラモジュールが付けられるようになっています。
技適のないものやUSBシリアルポートがないものが出回っていたりしますが、筆者は技適あり、USBシリアルポートありのものを購入しました。
AliExpressで2つで送料込み2千円程度でした。
一つは正常動作しましたが、マイコンの金属フレームが凹んでいました。
もう一つは、プログラムの書き込みができず、壊れていました。上の写真は壊れているほうです。
不良品を安く売りさばいている感じで、筆者のように外れを引く可能性もあり、あまりおすすめはできません。リンクも貼りません。
CameraWebServerとは
ESP32-CAMで使用可能なサンプルプログラムです。
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プログラムで、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からの接続ができなくなりますので、微妙ですね。
コメント