ESP32-CAMでもテストしましたがなんだか不安定なのでESP32 WROVERでのテストにしました。LAN内に複数カメラを設置してWIFIでそれぞれの画像を監視するものです。例えば家の中の部屋にいくつか設置したり、家の中から外に向けて設置したりして画像を確認するものです。各部屋のURLはHTMLのリンクでつなげます。

wifi,固定IPなどの設定
- wifiのIDなどを指定する
- thread処理したら不安定だったのでesp_http_server.hを使い単一接続とする
- DHCPでなく固定IPとなるように設定する
/*
* @file wrover_webserver-nothread.ino
* @brief ESP32-WROVER カメラを使いLAN内で監視カメラ構築
* @details HTTPのマルチパートストリームを用いて複数のWROVERをWEBリンクで繋ぐ。
* 10秒間だけWEBストリーミング画像を映す。(負荷低減の為)
* DHCPアドレスを固定IPに指定する。
*/
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_http_server.h"
// 変更箇所①
const char* ssid = "XXXXXXXXX"; // 接続するWiFiのSSID
const char* password = "XXXXXXXXX"; // 接続するWiFiのパスワード
// 変更箇所②
const IPAddress ip(192,168,0,13);
const IPAddress gateway(192,168,0,1);
const IPAddress subnet(255,255,255,0);
HTTPのマルチパートストリーム
//リアルタイムにストリーミング画像を表示するためには、HTTPのマルチパートストリームを利用。
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=frame";
static const char* _STREAM_BOUNDARY = "\r\n--frame\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
// 変更箇所③
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 Camera</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; }
img { width: 320px; height: 240px; }
.link { margin: 10px; display: block; }
</style>
</head>
<body>
<h1>部屋A</h1>
<img src="/stream" id="camera-stream">
<h2>他カメラ</h2>
<a href="http://192.168.0.12" class="link">リビング</a>
<a href="http://192.168.0.13" class="link">部屋A/a>
</body>
</html>
)rawliteral";
ストリームを処理するハンドラ
- 15秒間だけストリーミングするようにする(負荷軽減)
static esp_err_t stream_handler(httpd_req_t *req) {
camera_fb_t *fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t *_jpg_buf = NULL;
char part_buf[64];
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if (res != ESP_OK) {
return res;
}
unsigned long startTime = millis();
while (millis() - startTime < 15000) { // 15秒間ストリーミング時
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
if (fb->format != PIXFORMAT_JPEG) {
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if (!jpeg_converted) {
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
}
if (res == ESP_OK) {
size_t hlen = snprintf(part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, part_buf, hlen);
}
if (res == ESP_OK) {
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if (res == ESP_OK) {
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if (_jpg_buf) {
free(_jpg_buf);
_jpg_buf = NULL;
}
if (res != ESP_OK) {
break;
}
}
// ストリーミング終了後、残りのチャンクを送信して終了 15秒間ストリーミング時
httpd_resp_send_chunk(req, NULL, 0);
return res;
}
カメラサーバーを開始する関数
void start_camera_server() {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
if (httpd_start(&server, &config) == ESP_OK) {
httpd_register_uri_handler(server, &index_uri);
httpd_register_uri_handler(server, &stream_uri);
}
}
その他の設定
- カメラモデルに基づくピン配置の設定
if defined(CAMERA_MODEL_WROVER_KIT) - カメラの設定
camera_config_t config
上記はいつもと同じように設定します。 - 電源はPCからとらずAC電源からUSB接続します。特にESP32-CAMでするときはで電圧降下しました。
- その他安定させるために色々策を施しています。
テスト結果
- 画像下のリンクURLをクリックすると各カメラへ移動します。