From dceee2b179943351208dd41bcd40ea7c3f788a94 Mon Sep 17 00:00:00 2001 From: Rene Zeldenthuis Date: Sat, 10 Sep 2022 00:59:48 +0200 Subject: [PATCH] Work in progress --- include/camera_config.h | 33 ++++++++- include/settings.h | 2 +- lib/template_render/library.json | 5 ++ lib/template_render/template_render.h | 19 ++++++ src/main.cpp | 97 ++++++++++++++++----------- 5 files changed, 113 insertions(+), 43 deletions(-) create mode 100644 lib/template_render/library.json create mode 100644 lib/template_render/template_render.h diff --git a/include/camera_config.h b/include/camera_config.h index cf465b7..fce75a2 100644 --- a/include/camera_config.h +++ b/include/camera_config.h @@ -13,9 +13,36 @@ typedef struct camera_config_entry 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}}}; + {"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) { diff --git a/include/settings.h b/include/settings.h index 6f56f93..5dec82f 100644 --- a/include/settings.h +++ b/include/settings.h @@ -9,6 +9,6 @@ #define RTSP_PORT 554 #define DEFAULT_CAMERA_CONFIG "AI THINKER" -#define DEFAULT_FRAMERATE "20" +#define DEFAULT_FRAMEDURATION "20" #define DEFAULT_FRAMESIZE "SVGA (800x600)" #define DEFAULT_JPEG_QUALITY "12" \ No newline at end of file diff --git a/lib/template_render/library.json b/lib/template_render/library.json new file mode 100644 index 0000000..f8946d4 --- /dev/null +++ b/lib/template_render/library.json @@ -0,0 +1,5 @@ +{ + "name": "template_render", + "version": "1.0.0", + "description": "A mini template renderer" +} \ No newline at end of file diff --git a/lib/template_render/template_render.h b/lib/template_render/template_render.h new file mode 100644 index 0000000..0592d5a --- /dev/null +++ b/lib/template_render/template_render.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +typedef struct +{ + const char *key; + const String value; +} template_substitution_t; + +template +inline String template_render(const char *format, T (&values)[n]) +{ + auto s = String(format); + for (size_t i=0; i #include #include +#include #include char camera_config_val[sizeof(camera_config_entry)]; -char frame_rate_val[6]; +char frame_duration_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, nullptr, "min=\"10\""); +auto config_frame_rate = iotwebconf::NumberParameter("Frame duration (ms)", "fd", frame_duration_val, sizeof(frame_duration_val), DEFAULT_FRAMEDURATION, nullptr, "min=\"10\""); 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, nullptr, "min=\"1\" max=\"100\""); @@ -42,48 +43,66 @@ void handle_root() if (iotWebConf.handleCaptivePortal()) return; - auto url = "rtsp://" + String(iotWebConf.getThingName()) + ".local:" + String(RTSP_PORT) + "/mjpeg/1"; + const char *root_page_template = + "" + "" + "{{Title}}" APP_TITLE " v" APP_VERSION "" + "" + "

Status page for {{ThingName}}


" - String html; - html += "" - "" - "" APP_TITLE " v" APP_VERSION "" - ""; + "

ESP32

" + "
    " + "
  • CPU model: {{ChipModel}}
  • " + "
  • CPU speed: {{CpuFreqMHz}}Mhz
  • " + "
  • Mac address: {{MacAddress}}
  • " + "
  • IPv4 address: {{IpV4}}
  • " + "
  • IPv6 address: {{IpV6}}
  • " + "
" - html += "

Status page for " + String(iotWebConf.getThingName()) + "


"; + "

Settings

" + "
    " + "
  • Camera type: {{CameraType}}
  • " + "
  • Frame size: {{FrameSize}}
  • " + "
  • Frame rate: {{FrameDuration}} ms ({{FrameFrequency}} f/s)
  • " + "
  • JPEG quality: {{JpegQuality}} (0-100)
  • " + "
" - html += "

ESP32

"; - html += "
    "; - html += "
  • CPU model: " + String(ESP.getChipModel()) + "
  • "; - html += "
  • CPU speed: " + String(ESP.getCpuFreqMHz()) + "Mhz
  • "; - html += "
  • Mac address: " + WiFi.macAddress() + "
  • "; - html += "
  • IPv4 address: " + WiFi.localIP().toString() + "
  • "; - html += "
  • IPv6 address: " + WiFi.localIPv6().toString() + "
  • "; - html += "
"; + "

Diagnostics

" + "
    " + "
  • Uptime: {{Uptime}}
  • " + "
  • Free heap: {{FreeHeap}}b
  • " + "
  • Max free block: {{MaxAllocHeap}}b
  • " + "
" - html += "

Settings

"; - html += "
    "; - html += "
  • Camera type: " + String(camera_config_val) + "
  • "; - html += "
  • Frame size: " + String(frame_size_val) + "
  • "; - html += "
  • Frame rate: " + String(frame_rate_val) + " ms (" + String(1000.0 / atol(frame_rate_val), 1) + " f/s)
  • "; - html += "
  • JPEG quality: " + String(jpeg_quality_val) + " (0-100)
  • "; - html += "
"; + "
camera stream: rtsp://{{ThingName}}.local:{{RtspPort}}/mjpeg/1" + "
" + "
Go to configure page to change settings."; - html += "

Diagnostics

"; - html += "
    "; - html += "
  • Uptime: " + String(format_duration(millis() / 1000)) + "
  • "; - html += "
  • Free heap: " + format_si(ESP.getFreeHeap()) + "b
  • "; - html += "
  • Max free block: " + format_si(ESP.getMaxAllocHeap()) + "b
  • "; - html += "
"; + const template_substitution_t root_page_substitutions[] = { + {"Title", APP_TITLE}, + {"Version", APP_VERSION}, + {"ThingName", iotWebConf.getThingName()}, + {"ChipModel", ESP.getChipModel()}, + {"CpuFreqMHz", String(ESP.getCpuFreqMHz())}, + {"MacAddress", WiFi.macAddress()}, + {"IpV4", WiFi.localIP().toString()}, + {"IpV6", WiFi.localIPv6().toString()}, + {"CameraType", camera_config_val}, + {"FrameSize", frame_size_val}, + {"FrameDuration", frame_duration_val}, + {"FrameFrequency", String(1000.0 / atol(frame_duration_val), 1)}, + {"JpegQuality", jpeg_quality_val}, + {"Uptime", String(format_duration(millis() / 1000))}, + {"FreeHeap", format_si(ESP.getFreeHeap())}, + {"MaxAllocHeap", format_si(ESP.getMaxAllocHeap())}, + {"RtspPort", String(RTSP_PORT)}}; - html += "
camera stream: " + url + ""; - html += "
"; - html += "
Go to configure page to change settings."; + auto html = template_render(root_page_template, root_page_substitutions); if (config_changed) { - html += "
"; - html += "

Configuration has changed. Please restart the device.

"; + html += "
" + "

Configuration has changed. Please restart the device.

"; } html += ""; @@ -127,7 +146,7 @@ bool initialize_camera() auto frame_size = lookup_frame_size(frame_size_val); log_i("JPEG quality: %s", jpeg_quality_val); auto jpeg_quality = atoi(jpeg_quality_val); - log_i("Framerate: %s ms", frame_rate_val); + log_i("Frame rate: %s ms", frame_duration_val); camera_config.frame_size = frame_size; camera_config.jpeg_quality = jpeg_quality; @@ -139,11 +158,11 @@ void start_rtsp_server() log_v("start_rtsp_server"); if (!initialize_camera()) { - log_e("Failed to initialize camera. Type: %s, frame size: %s, frame rate: %s ms, jpeg quality: %s", camera_config_val, frame_size_val, frame_rate_val, jpeg_quality_val); + log_e("Failed to initialize camera. Type: %s, frame size: %s, frame rate: %s ms, jpeg quality: %s", camera_config_val, frame_size_val, frame_duration_val, jpeg_quality_val); return; } - auto frame_rate = atol(frame_rate_val); + auto frame_rate = atol(frame_duration_val); camera_server = std::unique_ptr(new rtsp_server(cam, frame_rate, RTSP_PORT)); // Add service to mDNS - rtsp MDNS.addService("rtsp", "tcp", 554); @@ -168,7 +187,7 @@ void setup() Serial.setDebugOutput(true); #endif - log_i("CPU Freq = %d Mhz", getCpuFrequencyMhz()); + log_i("CPU Freq: %d Mhz", getCpuFrequencyMhz()); log_i("Free heap: %d bytes", ESP.getFreeHeap()); log_i("Starting " APP_TITLE "...");