diff --git a/app_httpd.cpp b/app_httpd.cpp index 59d9e24..05df2c5 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -17,9 +17,10 @@ #include "img_converters.h" #include "Arduino.h" -#include "camera_index_ov2640.h" -#include "camera_index_ov3660.h" +#include "index_ov2640.h" +#include "index_ov3660.h" #include "miniviewer.h" +#include "css.h" #include "favicon/favicons.h" //#define DEBUG_STREAM_DATA // Debug: dump info for each stream frame on serial port @@ -664,6 +665,15 @@ static esp_err_t favicon_ico_handler(httpd_req_t *req){ return httpd_resp_send(req, (const char *)favicon_ico, favicon_ico_len); } +static esp_err_t style_handler(httpd_req_t *req){ + flashLED(75); // a little feedback to user + delay(75); + flashLED(75); + httpd_resp_set_type(req, "text/css"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + return httpd_resp_send(req, (const char *)style_css, style_css_len); +} + static esp_err_t miniviewer_handler(httpd_req_t *req){ flashLED(75); // a little feedback to user delay(75); @@ -724,6 +734,13 @@ void startCameraServer(int hPort, int sPort){ .user_ctx = NULL }; + httpd_uri_t style_uri = { + .uri = "/style.css", + .method = HTTP_GET, + .handler = style_handler, + .user_ctx = NULL + }; + httpd_uri_t favicon_16x16_uri = { .uri = "/favicon-16x16.png", .method = HTTP_GET, @@ -780,6 +797,7 @@ void startCameraServer(int hPort, int sPort){ httpd_register_uri_handler(camera_httpd, &status_uri); httpd_register_uri_handler(camera_httpd, &capture_uri); httpd_register_uri_handler(camera_httpd, &miniviewer_uri); + httpd_register_uri_handler(camera_httpd, &style_uri); httpd_register_uri_handler(camera_httpd, &favicon_16x16_uri); httpd_register_uri_handler(camera_httpd, &favicon_32x32_uri); httpd_register_uri_handler(camera_httpd, &favicon_ico_uri); diff --git a/camera_index_ov2640.h b/camera_index_ov2640.h deleted file mode 100644 index 7a71e34..0000000 --- a/camera_index_ov2640.h +++ /dev/null @@ -1,893 +0,0 @@ -//File: index_ov2640.html -const uint8_t index_ov2640_html[] = R"=====( - - - - - - ESP32 OV2640 - - - - - -
- -
- -
- -
-
-
- - - - - -)====="; -size_t index_ov2640_html_len = sizeof(index_ov2640_html); diff --git a/camera_index_ov3660.h b/camera_index_ov3660.h deleted file mode 100644 index 73ea8a7..0000000 --- a/camera_index_ov3660.h +++ /dev/null @@ -1,901 +0,0 @@ -//File: index_ov3660.html -const uint8_t index_ov3660_html[] = R"=====( - - - - - - ESP32 OV3660 - - - - - -
- -
- -
- -
-
-
- - - - - -)====="; -size_t index_ov3660_html_len = sizeof(index_ov3660_html); diff --git a/css.h b/css.h new file mode 100644 index 0000000..657e2d5 --- /dev/null +++ b/css.h @@ -0,0 +1,372 @@ +/* + * Master CSS file included in the camer_index_* and miniviewer HTML + */ + +const uint8_t style_css[] = R"=====( +/* + * CSS for the esp32 cam webserver + */ + +body { + font-family: Arial,Helvetica,sans-serif; + background: #181818; + color: #EFEFEF; + font-size: 16px +} + +a { + color: #EFEFEF; + text-decoration: underline +} + +h2 { + font-size: 18px +} + +section.main { + display: flex +} + +#menu,section.main { + flex-direction: column +} + +#menu { + display: none; + flex-wrap: nowrap; + width: 340px; + background: #363636; + padding: 8px; + border-radius: 4px; + margin-top: -10px; + margin-right: 10px; +} + +/* #content { + display: flex; + flex-wrap: wrap; + align-items: stretch +} +*/ +figure { + padding: 0px; + margin: 0; + -webkit-margin-before: 0; + margin-block-start: 0; + -webkit-margin-after: 0; + margin-block-end: 0; + -webkit-margin-start: 0; + margin-inline-start: 0; + -webkit-margin-end: 0; + margin-inline-end: 0 +} + +figure img { + display: block; +/* + no max-width: + width: 100%; +*/ + max-width: 100%; + width: auto; + height: auto; + border-radius: 4px; + margin-top: 8px; +} + +section#buttons { + display: flex; + flex-wrap: nowrap; + justify-content: space-between +} + +#nav-toggle { + cursor: pointer; + display: block +} + +#nav-toggle-cb { + outline: 0; + opacity: 0; + width: 0; + height: 0 +} + +#nav-toggle-cb:checked+#menu { + display: flex +} + +.input-group { + display: flex; + flex-wrap: nowrap; + line-height: 22px; + margin: 5px 0 +} + +.input-group>label { + display: inline-block; + padding-right: 10px; + min-width: 47% +} + +.input-group input,.input-group select { + flex-grow: 1 +} + +.range-max,.range-min { + display: inline-block; + padding: 0 5px +} + +button { + display: block; + margin: 5px; + padding: 0 12px; + border: 0; + line-height: 28px; + cursor: pointer; + color: #fff; + background: #ff3034; + border-radius: 5px; + font-size: 16px; + outline: 0 +} + +button:hover { + background: #ff494d +} + +button:active { + background: #f21c21 +} + +button.disabled { + cursor: default; + background: #a0a0a0 +} + +input[type=range] { + -webkit-appearance: none; + width: 100%; + height: 22px; + background: #363636; + cursor: pointer; + margin: 0 +} + +input[type=range]:focus { + outline: 0 +} + +input[type=range]::-webkit-slider-runnable-track { + width: 100%; + height: 2px; + cursor: pointer; + background: #EFEFEF; + border-radius: 0; + border: 0 solid #EFEFEF +} + +input[type=range]::-webkit-slider-thumb { + border: 1px solid rgba(0,0,30,0); + height: 22px; + width: 22px; + border-radius: 50px; + background: #ff3034; + cursor: pointer; + -webkit-appearance: none; + margin-top: -11.5px +} + +input[type=range]:focus::-webkit-slider-runnable-track { + background: #EFEFEF +} + +input[type=range]::-moz-range-track { + width: 100%; + height: 2px; + cursor: pointer; + background: #EFEFEF; + border-radius: 0; + border: 0 solid #EFEFEF +} + +input[type=range]::-moz-range-thumb { + border: 1px solid rgba(0,0,30,0); + height: 22px; + width: 22px; + border-radius: 50px; + background: #ff3034; + cursor: pointer +} + +input[type=range]::-ms-track { + width: 100%; + height: 2px; + cursor: pointer; + background: 0 0; + border-color: transparent; + color: transparent +} + +input[type=range]::-ms-fill-lower { + background: #EFEFEF; + border: 0 solid #EFEFEF; + border-radius: 0 +} + +input[type=range]::-ms-fill-upper { + background: #EFEFEF; + border: 0 solid #EFEFEF; + border-radius: 0 +} + +input[type=range]::-ms-thumb { + border: 1px solid rgba(0,0,30,0); + height: 22px; + width: 22px; + border-radius: 50px; + background: #ff3034; + cursor: pointer; + height: 2px +} + +input[type=range]:focus::-ms-fill-lower { + background: #EFEFEF +} + +input[type=range]:focus::-ms-fill-upper { + background: #363636 +} + +input[type=text] { + border: 1px solid #363636; + font-size: 14px; + height: 20px; + margin: 1px; + outline: 0; + border-radius: 5px +} + +.switch { + display: block; + position: relative; + line-height: 22px; + font-size: 16px; + height: 22px +} + +.switch input { + outline: 0; + opacity: 0; + width: 0; + height: 0 +} + +.slider { + width: 50px; + height: 22px; + border-radius: 22px; + cursor: pointer; + background-color: grey +} + +.slider,.slider:before { + display: inline-block; + transition: .4s +} + +.slider:before { + position: relative; + content: ""; + border-radius: 50%; + height: 16px; + width: 16px; + left: 4px; + top: 3px; + background-color: #fff +} + +input:checked+.slider { + background-color: #ff3034 +} + +input:checked+.slider:before { + -webkit-transform: translateX(26px); + transform: translateX(26px) +} + +select { + border: 1px solid #363636; + font-size: 14px; + height: 22px; + outline: 0; + border-radius: 5px +} + +.image-container { + position: relative; + min-width: 160px; + transform-origin: top left +} + +.close { + position: absolute; + z-index: 99; + background: #ff3034; + width: 16px; + height: 16px; + border-radius: 100px; + color: #fff; + text-align: center; + line-height: 18px; + cursor: pointer +} + +.close-rot-none { + left: 5px; + top: 5px; +} + +.close-rot-left { + right: 5px; + top: 5px; +} + +.close-rot-right { + left: 5px; + bottom: 5px; +} + +.hidden { + display: none +} + +.inline-button { + line-height: 20px; + margin: 2px; + padding: 1px 4px 2px 4px; +} + +.loader { + border: 0.5em solid #f3f3f3; /* Light grey */ + border-top: 0.5em solid #000000; /* white */ + border-radius: 50%; + width: 1em; + height: 1em; + -webkit-animation: spin 2s linear infinite; /* Safari */ + animation: spin 2s linear infinite; +} + +@-webkit-keyframes spin { /* Safari */ + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} +)====="; + +size_t style_css_len = sizeof(index_ov2640_html); diff --git a/index_ov2640.h b/index_ov2640.h new file mode 100644 index 0000000..47038d2 --- /dev/null +++ b/index_ov2640.h @@ -0,0 +1,600 @@ +/* + * primary HTML for the OV2640 camera module + */ + +const uint8_t index_ov2640_html[] = R"=====( + + + + + + ESP32 OV3660 + + + + + + + +
+ +
+ +
+ +
+
+
+ + + + +)====="; + +size_t index_ov2640_html_len = sizeof(index_ov2640_html); diff --git a/index_ov3660.h b/index_ov3660.h new file mode 100644 index 0000000..5b3c043 --- /dev/null +++ b/index_ov3660.h @@ -0,0 +1,608 @@ +/* + * primary HTML for the OV3660 camera module + */ + +const uint8_t index_ov3660_html[] = R"=====( + + + + + + ESP32 OV3660 + + + + + + + +
+ +
+ +
+ +
+
+
+ + + + +)====="; + +size_t index_ov3660_html_len = sizeof(index_ov3660_html); diff --git a/miniviewer.h b/miniviewer.h index f62198a..71b0664 100644 --- a/miniviewer.h +++ b/miniviewer.h @@ -8,383 +8,9 @@ const uint8_t miniviewer_html[] = R"=====( ESP32-CAM MiniViewer + @@ -392,6 +18,7 @@ const uint8_t miniviewer_html[] = R"=====(