forked from external-repos/esp32cam-rtsp
- Added camera config
- Removed some not working framesizes - Restart handling
This commit is contained in:
28
include/camera_config.h
Normal file
28
include/camera_config.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <sensor.h>
|
||||
|
||||
typedef char camera_config_name_t[18];
|
||||
|
||||
typedef struct camera_config_entry
|
||||
{
|
||||
const camera_config_name_t name;
|
||||
const camera_config_t config;
|
||||
} camera_config_entry_t;
|
||||
|
||||
constexpr const camera_config_entry_t camera_configs[] = {
|
||||
|
||||
{"ESP32CAM", {.pin_pwdn = -1, .pin_reset = 15, .pin_xclk = 27, .pin_sscb_sda = 25, .pin_sscb_scl = 23, .pin_d7 = 19, .pin_d6 = 36, .pin_d5 = 18, .pin_d4 = 39, .pin_d3 = 5, .pin_d2 = 34, .pin_d1 = 35, .pin_d0 = 17, .pin_vsync = 22, .pin_href = 26, .pin_pclk = 21, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}},
|
||||
{"AI THINKER", {.pin_pwdn = 32, .pin_reset = -1, .pin_xclk = 0, .pin_sscb_sda = 26, .pin_sscb_scl = 27, .pin_d7 = 35, .pin_d6 = 34, .pin_d5 = 39, .pin_d4 = 36, .pin_d3 = 21, .pin_d2 = 19, .pin_d1 = 18, .pin_d0 = 5, .pin_vsync = 25, .pin_href = 23, .pin_pclk = 22, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_1, .ledc_channel = LEDC_CHANNEL_1, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}},
|
||||
{"TTGO T-CAM", {.pin_pwdn = 26, .pin_reset = -1, .pin_xclk = 32, .pin_sscb_sda = 13, .pin_sscb_scl = 12, .pin_d7 = 39, .pin_d6 = 36, .pin_d5 = 23, .pin_d4 = 18, .pin_d3 = 15, .pin_d2 = 4, .pin_d1 = 14, .pin_d0 = 5, .pin_vsync = 27, .pin_href = 25, .pin_pclk = 19, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}}};
|
||||
|
||||
const camera_config_t lookup_camera_config(const char *pin)
|
||||
{
|
||||
// Lookup table for the frame name to framesize_t
|
||||
for (const auto &entry : camera_configs)
|
||||
if (strncmp(entry.name, pin, sizeof(camera_config_name_t)) == 0)
|
||||
return entry.config;
|
||||
|
||||
return camera_config_t{};
|
||||
}
|
||||
@@ -7,13 +7,11 @@ typedef char frame_size_name_t[18];
|
||||
|
||||
typedef struct frame_size_entry
|
||||
{
|
||||
frame_size_name_t name;
|
||||
framesize_t frame_size;
|
||||
const frame_size_name_t name;
|
||||
const framesize_t frame_size;
|
||||
} frame_size_entry_t;
|
||||
|
||||
constexpr const frame_size_entry_t frame_sizes[] = {
|
||||
|
||||
{"96x96", FRAMESIZE_96X96},
|
||||
{"QQVGA (160x120)", FRAMESIZE_QQVGA},
|
||||
{"QCIF (176x144)", FRAMESIZE_QCIF},
|
||||
{"HQVGA (240x176)", FRAMESIZE_HQVGA},
|
||||
@@ -26,28 +24,14 @@ constexpr const frame_size_entry_t frame_sizes[] = {
|
||||
{"XGA (1024x768)", FRAMESIZE_XGA},
|
||||
{"HD (1280x720)", FRAMESIZE_HD},
|
||||
{"SXGA (1280x1024)", FRAMESIZE_SXGA},
|
||||
{"UXGA (1600x1200)", FRAMESIZE_UXGA},
|
||||
{"FHD (1920x1080)", FRAMESIZE_FHD},
|
||||
{"P HD (2560x1440)", FRAMESIZE_P_HD},
|
||||
{"P 3MP (2560x1600)", FRAMESIZE_P_3MP},
|
||||
{"QXGA (2560x1920)", FRAMESIZE_QXGA},
|
||||
{"QHD (2560x1440)", FRAMESIZE_QHD},
|
||||
{"WQXGA (2560x1600)", FRAMESIZE_WQXGA},
|
||||
{"P FHD (1080x1920)", FRAMESIZE_P_FHD},
|
||||
{"QSXGA (2560x1920)", FRAMESIZE_QSXGA},
|
||||
{"", FRAMESIZE_INVALID}};
|
||||
{"UXGA (1600x1200)", FRAMESIZE_UXGA}};
|
||||
|
||||
framesize_t lookup_frame_size(const char *pin)
|
||||
const framesize_t lookup_frame_size(const char *pin)
|
||||
{
|
||||
// Lookup table for the frame name to framesize_t
|
||||
auto entry = &frame_sizes[0];
|
||||
while (*entry->name)
|
||||
{
|
||||
if (strncmp(entry->name, pin, sizeof(frame_size_name_t)) == 0)
|
||||
return entry->frame_size;
|
||||
|
||||
entry++;
|
||||
}
|
||||
for (const auto &entry : frame_sizes)
|
||||
if (strncmp(entry.name, pin, sizeof(frame_size_name_t)) == 0)
|
||||
return entry.frame_size;
|
||||
|
||||
return FRAMESIZE_INVALID;
|
||||
}
|
||||
@@ -5,8 +5,10 @@
|
||||
|
||||
#define WIFI_SSID "ESP32CAM-RTSP"
|
||||
#define WIFI_PASSWORD nullptr
|
||||
#define CONFIG_VERSION "1.1"
|
||||
#define CONFIG_VERSION "1.0"
|
||||
|
||||
#define RTSP_PORT 554
|
||||
#define DEFAULT_CAMERA_CONFIG "AI THINKER"
|
||||
#define DEFAULT_FRAMERATE "20"
|
||||
#define DEFAULT_FRAMESIZE "SVGA (800x600)"
|
||||
#define DEFAULT_FRAMESIZE "SVGA (800x600)"
|
||||
#define DEFAULT_JPEG_QUALITY "12"
|
||||
105
src/main.cpp
105
src/main.cpp
@@ -6,35 +6,40 @@
|
||||
#include <ESPmDNS.h>
|
||||
#include <rtsp_server.h>
|
||||
#include <frame_size.h>
|
||||
#include <esp32cam.h>
|
||||
#include <camera_config.h>
|
||||
#include <settings.h>
|
||||
|
||||
char camera_config_val[sizeof(camera_config_entry)];
|
||||
char frame_rate_val[6];
|
||||
char frame_size_val[sizeof(frame_size_entry_t)];
|
||||
char jpeg_quality_val[4];
|
||||
|
||||
auto config_group_stream_settings = iotwebconf::ParameterGroup("settings", "Streaming settings");
|
||||
auto config_camera_config = iotwebconf::SelectParameter("Camera config", "config", camera_config_val, sizeof(camera_config_val), (const char *)camera_configs, (const char *)camera_configs, sizeof(camera_configs) / sizeof(camera_configs[0]), sizeof(camera_configs[0]), DEFAULT_CAMERA_CONFIG);
|
||||
auto config_frame_rate = iotwebconf::NumberParameter("Frame rate (ms)", "fr", frame_rate_val, sizeof(frame_rate_val), DEFAULT_FRAMERATE);
|
||||
auto config_frame_size = iotwebconf::SelectParameter("Frame size", "fs", frame_size_val, sizeof(frame_size_val), (const char *)frame_sizes, (const char *)frame_sizes, sizeof(frame_sizes) / sizeof(frame_sizes[0]), sizeof(frame_sizes[0]), DEFAULT_FRAMESIZE);
|
||||
auto config_jpg_quality = iotwebconf::NumberParameter("JPEG quality", "q", jpeg_quality_val, sizeof(jpeg_quality_val), DEFAULT_JPEG_QUALITY);
|
||||
|
||||
// Camera
|
||||
OV2640 cam;
|
||||
|
||||
// DNS Server
|
||||
DNSServer dnsServer;
|
||||
|
||||
// RTSP Server
|
||||
std::unique_ptr<rtsp_server> camera_server;
|
||||
|
||||
// Web server
|
||||
WebServer web_server(80);
|
||||
IotWebConf iotWebConf(WIFI_SSID, &dnsServer, &web_server, WIFI_PASSWORD, CONFIG_VERSION);
|
||||
bool config_changed = false;
|
||||
|
||||
void handleRoot()
|
||||
void handle_root()
|
||||
{
|
||||
log_v("Handle root");
|
||||
// Let IotWebConf test and handle captive portal requests.
|
||||
if (iotWebConf.handleCaptivePortal())
|
||||
return;
|
||||
|
||||
auto url = "rtsp://" + String(iotWebConf.getThingName()) + ".local:" + String(RTSP_PORT) + "/mjpeg/1";
|
||||
|
||||
String html;
|
||||
html += "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/>";
|
||||
html += "<title>" APP_TITLE " v" APP_VERSION "</title></head>";
|
||||
@@ -45,56 +50,75 @@ void handleRoot()
|
||||
html += "<li>CPU model: " + String(ESP.getChipModel()) + "</li>";
|
||||
html += "<li>CPU speed: " + String(ESP.getCpuFreqMHz()) + "Mhz</li>";
|
||||
html += "<li>Mac address: " + WiFi.macAddress() + "</li>";
|
||||
html += "<li>IPv4 address: " + WiFi.localIP().toString() + "</li>";
|
||||
html += "<li>IPv6 address: " + WiFi.localIPv6().toString() + "</li>";
|
||||
html += "</ul>";
|
||||
html += "<h3>Settings</h3>";
|
||||
html += "<ul>";
|
||||
html += "<li>Camera type: " + String(camera_config_val) + "</li>";
|
||||
html += "<li>Frame size: " + String(frame_size_val) + "</li>";
|
||||
html += "<li>Frame rate: " + String(frame_rate_val) + " ms</li>";
|
||||
html += "<li>JPEG quality: " + String(jpeg_quality_val) + " (0-100)</li>";
|
||||
html += "</ul>";
|
||||
html += "<br/>camera stream: <a href=\"" + url + "\">" + url + "</a>";
|
||||
html += "<br />";
|
||||
html += "<br/>Go to <a href=\"config\">configure page</a> to change settings.";
|
||||
if (config_changed)
|
||||
{
|
||||
html += "<br />";
|
||||
html += "<br/><h3 style=\"color:red\">Configuration has changed. Please <a href=\"restart\">restart</a> the device.</h3>";
|
||||
}
|
||||
|
||||
html += "</body></html>";
|
||||
web_server.send(200, "text/html", html);
|
||||
}
|
||||
|
||||
void handle_restart()
|
||||
{
|
||||
log_v("Handle restart");
|
||||
if (config_changed)
|
||||
{
|
||||
String html;
|
||||
html += "<h2>Restarting...</h2>";
|
||||
html += "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/>";
|
||||
html += "<title>" APP_TITLE " v" APP_VERSION "</title></head>";
|
||||
html += "<body>";
|
||||
web_server.send(200, "text/html", html);
|
||||
log_v("Restarting...");
|
||||
sleep(250);
|
||||
ESP.restart();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Redirect to root page.
|
||||
web_server.sendHeader("Location", "/", true );
|
||||
web_server.send(302, "text/plain", "");
|
||||
}
|
||||
}
|
||||
|
||||
void on_config_saved()
|
||||
{
|
||||
log_v("on_config_saved");
|
||||
ESP.restart();
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
void initialize_camera()
|
||||
{
|
||||
log_v("Initialize camera");
|
||||
log_i("Camera config: %s", camera_config_val);
|
||||
auto camera_config = lookup_camera_config(camera_config_val);
|
||||
log_i("Frame size: %s", frame_size_val);
|
||||
auto frame_size = lookup_frame_size(frame_size_val);
|
||||
// ESP32CAM
|
||||
log_d("Looking for ESP32CAM");
|
||||
esp32cam_config.frame_size = frame_size;
|
||||
if (cam.init(esp32cam_aithinker_config) == ESP_OK)
|
||||
{
|
||||
log_i("Found ESP32CAM");
|
||||
return;
|
||||
}
|
||||
log_i("JPEG quality: %s", jpeg_quality_val);
|
||||
auto jpeg_quality = atoi(jpeg_quality_val);
|
||||
log_i("Framerate: %s ms", frame_rate_val);
|
||||
|
||||
// AI Thinker
|
||||
log_d("Looking for AI Thinker");
|
||||
esp32cam_aithinker_config.frame_size = frame_size;
|
||||
if (cam.init(esp32cam_aithinker_config) == ESP_OK)
|
||||
{
|
||||
log_i("Found AI Thinker");
|
||||
return;
|
||||
}
|
||||
|
||||
// TTGO T-Cam
|
||||
log_d("Looking for TTGO T-Cam");
|
||||
esp32cam_ttgo_t_config.frame_size = frame_size;
|
||||
if (cam.init(esp32cam_ttgo_t_config) == ESP_OK)
|
||||
{
|
||||
log_i("Found TTGO T-Cam");
|
||||
return;
|
||||
}
|
||||
|
||||
log_e("No camera found");
|
||||
camera_config.frame_size = frame_size;
|
||||
camera_config.jpeg_quality = jpeg_quality;
|
||||
if (cam.init(camera_config) == ESP_OK)
|
||||
log_i("Camera found");
|
||||
else
|
||||
log_e("No camera found");
|
||||
}
|
||||
|
||||
void start_rtsp_server()
|
||||
@@ -103,6 +127,8 @@ void start_rtsp_server()
|
||||
initialize_camera();
|
||||
auto frame_rate = atol(frame_rate_val);
|
||||
camera_server = std::unique_ptr<rtsp_server>(new rtsp_server(cam, frame_rate, RTSP_PORT));
|
||||
// Add service to mDNS - rtsp
|
||||
MDNS.addService("rtsp", "tcp", 554);
|
||||
}
|
||||
|
||||
void on_connected()
|
||||
@@ -122,37 +148,34 @@ void setup()
|
||||
#ifdef CORE_DEBUG_LEVEL
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
|
||||
#endif
|
||||
|
||||
log_i("CPU Freq = %d Mhz", getCpuFrequencyMhz());
|
||||
log_i("Free heap: %d bytes", ESP.getFreeHeap());
|
||||
log_i("Starting " APP_TITLE "...");
|
||||
|
||||
config_group_stream_settings.addItem(&config_camera_config);
|
||||
config_group_stream_settings.addItem(&config_frame_rate);
|
||||
config_group_stream_settings.addItem(&config_frame_size);
|
||||
config_group_stream_settings.addItem(&config_jpg_quality);
|
||||
iotWebConf.addParameterGroup(&config_group_stream_settings);
|
||||
iotWebConf.getApTimeoutParameter()->visible = true;
|
||||
iotWebConf.setConfigSavedCallback(on_config_saved);
|
||||
iotWebConf.setWifiConnectionCallback(on_connected);
|
||||
|
||||
iotWebConf.init();
|
||||
|
||||
// Set up required URL handlers on the web server
|
||||
web_server.on("/", HTTP_GET, handleRoot);
|
||||
web_server.on("/", HTTP_GET, handle_root);
|
||||
web_server.on("/config", []
|
||||
{ iotWebConf.handleConfig(); });
|
||||
web_server.on("/restart", HTTP_GET, handle_restart);
|
||||
web_server.onNotFound([]()
|
||||
{ iotWebConf.handleNotFound(); });
|
||||
|
||||
// Set DNS to thingname
|
||||
if (!MDNS.begin(iotWebConf.getThingName()))
|
||||
log_e("Error setting up MDNS responder!");
|
||||
|
||||
MDNS.begin(iotWebConf.getThingName());
|
||||
// Add service to mDNS - http
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
// Add service to mDNS - rtsp
|
||||
MDNS.addService("rtsp", "tcp", 554);
|
||||
}
|
||||
|
||||
void loop()
|
||||
|
||||
Reference in New Issue
Block a user