From 6a27f0f6352c20f608f98c301ae2c3eb183e7b7e Mon Sep 17 00:00:00 2001 From: Rene Zeldenthuis Date: Tue, 5 Jul 2022 13:21:17 +0200 Subject: [PATCH] - Added camera config - Removed some not working framesizes - Restart handling --- include/camera_config.h | 28 ++++++++ include/frame_size.h | 30 ++------- include/{esp32cam.h => settings.h} | 6 +- src/main.cpp | 105 ++++++++++++++++++----------- 4 files changed, 103 insertions(+), 66 deletions(-) create mode 100644 include/camera_config.h rename include/{esp32cam.h => settings.h} (56%) diff --git a/include/camera_config.h b/include/camera_config.h new file mode 100644 index 0000000..cf465b7 --- /dev/null +++ b/include/camera_config.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +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{}; +} \ No newline at end of file diff --git a/include/frame_size.h b/include/frame_size.h index f87babe..b47d775 100644 --- a/include/frame_size.h +++ b/include/frame_size.h @@ -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; } \ No newline at end of file diff --git a/include/esp32cam.h b/include/settings.h similarity index 56% rename from include/esp32cam.h rename to include/settings.h index dcdece0..6f56f93 100644 --- a/include/esp32cam.h +++ b/include/settings.h @@ -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)" \ No newline at end of file +#define DEFAULT_FRAMESIZE "SVGA (800x600)" +#define DEFAULT_JPEG_QUALITY "12" \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 25ee429..adbb099 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,35 +6,40 @@ #include #include #include -#include +#include +#include +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 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 += ""; html += "" APP_TITLE " v" APP_VERSION ""; @@ -45,56 +50,75 @@ void handleRoot() 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 += ""; 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
    • "; + html += "
    • JPEG quality: " + String(jpeg_quality_val) + " (0-100)
    • "; html += "
    "; + html += "
    camera stream: " + url + ""; + html += "
    "; html += "
    Go to configure page to change settings."; + if (config_changed) + { + html += "
    "; + html += "

    Configuration has changed. Please restart the device.

    "; + } + html += ""; web_server.send(200, "text/html", html); } +void handle_restart() +{ + log_v("Handle restart"); + if (config_changed) + { + String html; + html += "

    Restarting...

    "; + html += ""; + html += "" APP_TITLE " v" APP_VERSION ""; + html += ""; + 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(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()