ESP32cam V3 OV2640 , ESP32cam v1.2 OV5640

#include <WiFi.h>
#include <esp_http_server.h>
#include <esp_mac.h>
#include "esp_camera.h"
#include "esp_wifi.h"
#include "soc/rtc.h"
#include <Preferences.h>
#include "mbedtls/base64.h"
#include <vector>

std::vector<int> ws_clients;
SemaphoreHandle_t wsMutex;
Preferences prefs;

bool LocalHost = false;

char WiFi_HostName[64] = "ESP32cam"; // const char*
char WiFi_SSID[64]     = "internet";
char WiFi_Password[64] = "internet";
char www_Login[64]     = "root";
char www_Password[64]  = "root";

String get_WiFi_HostName_MAC()
{
    uint8_t mac[6];
    esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP);
    char temp[64];
    snprintf(temp, sizeof(temp), "%s%02X%02X%02X", WiFi_HostName, mac[3], mac[4], mac[5] );
    return String(temp);
}

httpd_handle_t server = NULL;


// PIN ESP32cam v1.2 -> OV5640
#define CAM_PWDN    -1
#define CAM_RESET   5
#define CAM_XCLK    15
#define CAM_SIOD    22
#define CAM_SIOC    23
#define CAM_D0      2
#define CAM_D1      14
#define CAM_D2      35
#define CAM_D3      12
#define CAM_D4      27
#define CAM_D5      33
#define CAM_D6      34
#define CAM_D7      39
#define CAM_VSYNC   18
#define CAM_HREF    36
#define CAM_PCLK    26
#define CAM_LED     25

/*
// PIN ESP32cam V3 -> OV2640
#define CAM_PWDN    32
#define CAM_RESET   -1
#define CAM_XCLK    0
#define CAM_SIOD    26
#define CAM_SIOC    27
#define CAM_D0      5
#define CAM_D1      18
#define CAM_D2      19
#define CAM_D3      21
#define CAM_D4      36
#define CAM_D5      39
#define CAM_D6      34
#define CAM_D7      35
#define CAM_VSYNC   25
#define CAM_HREF    23
#define CAM_PCLK    22
*/

bool checkAuthenticated(httpd_req_t *req)
{
    if (strcmp(www_Login, "root") == 0 && strcmp(www_Password, "root") == 0) return true;
    char authHeader[128];
    esp_err_t err = httpd_req_get_hdr_value_str(req, "Authorization", authHeader, sizeof(authHeader));
    if (err != ESP_OK)
    {
        httpd_resp_set_status(req, "401 Unauthorized");
        httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"ESP32cam\"");
        httpd_resp_send(req, NULL, 0);
        return false;
    }
    char credentials[128];
    snprintf(credentials, sizeof(credentials), "%s:%s", www_Login, www_Password);
    unsigned char encoded[128];
    size_t olen = 0;
    mbedtls_base64_encode( encoded, sizeof(encoded), &olen, (const unsigned char*)credentials, strlen(credentials));
    char expected[150];
    snprintf(expected, sizeof(expected), "Basic %s", encoded);
    if (strcmp(authHeader, expected) != 0)
    {
        httpd_resp_set_status(req, "401 Unauthorized");
        httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"ESP32cam\"");
        httpd_resp_send(req, NULL, 0);
        return false;
    }
    return true;
}

bool initCamera() // Camera config
{
  camera_config_t config;

  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer   = LEDC_TIMER_0;

  config.pin_d0 = CAM_D0;
  config.pin_d1 = CAM_D1;
  config.pin_d2 = CAM_D2;
  config.pin_d3 = CAM_D3;
  config.pin_d4 = CAM_D4;
  config.pin_d5 = CAM_D5;
  config.pin_d6 = CAM_D6;
  config.pin_d7 = CAM_D7;

  config.pin_xclk = CAM_XCLK;
  config.pin_pclk = CAM_PCLK;
  config.pin_vsync = CAM_VSYNC;
  config.pin_href = CAM_HREF;

  config.pin_sscb_sda = CAM_SIOD;
  config.pin_sscb_scl = CAM_SIOC;

  config.pin_pwdn = CAM_PWDN;
  config.pin_reset = CAM_RESET;

  config.xclk_freq_hz = 20000000; // 8000000 -> Only OV5640
  config.pixel_format = PIXFORMAT_JPEG;

  if (psramFound())
  {
    config.frame_size = FRAMESIZE_HD;
    config.jpeg_quality = 15;
    config.fb_count = 2;
    config.fb_location = CAMERA_FB_IN_PSRAM;
  }
  else
  {
    config.frame_size = FRAMESIZE_QVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
    config.fb_location = CAMERA_FB_IN_DRAM;
  }

  config.grab_mode = CAMERA_GRAB_LATEST;

  esp_err_t err = esp_camera_init(&config);

  sensor_t *s = esp_camera_sensor_get();
  s->set_special_effect(s, 2);
  s->set_gain_ctrl(s, 1);
  s->set_exposure_ctrl(s, 1);
  s->set_aec2(s, 1);
  s->set_agc_gain(s, 30);
  s->set_brightness(s, 2);
  s->set_contrast(s, -1);

  if (err != ESP_OK)
  {
    Serial.printf("Camera init failed: 0x%x\n", err);
    return false;
  }

  return true;
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html style="height: 100%;">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>%hostname%</title>
  <style>
    body
    {
      margin: 0;
      height: 100%;
      background-color: rgb(14, 14, 14);
      display: flex;
      justify-content: center;
      align-items: center;
    }
    img
    {
      max-width: 100%;
      max-height: 100%;
      width: auto;
      height: auto;
      object-fit: contain;
      display: block;
      user-select: none;
      background-color: hsl(0, 0%, 10%);
    }
  </style>
</head>
<body>
  <img id="cam">
  <script>
    let ws = new WebSocket("ws://" + location.host + "/ws");
    ws.binaryType = "arraybuffer";

    ws.onmessage = function(event) {
        let blob = new Blob([event.data], {type:"image/jpeg"});
        document.getElementById("cam").src = URL.createObjectURL(blob);
    };
  </script>
  <!--
  <img id="img" src="/capture">
  <script>
    setInterval(() => {
      document.getElementById('img').src = '/capture?' + Date.now();
    }, 300);
  </script>
  -->
</body>
</html>
)rawliteral";

const char Settings_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>%hostname% Settings</title>
<style>
body
{
    margin:0;
    background:#555;
    color:#000;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
    display:flex;
    justify-content:center;
    align-items:center;
    min-height:100vh;
  line-height: 1.5;
}
form
{
  position: fixed;
    background:#fff;
    padding:25px;
    border-radius:16px;
}
h2
{
    text-align:center;
    margin-top:0;
}
table
{
    margin:auto;
    border-collapse:collapse;
}
td
{
    padding:8px;
}
input
{
    width:300px;
    padding:5px;
  border: 1px solid #000;
  border-radius: 8px;
}
button
{
  margin: 0px 5px;
    padding: 15px 40px;
  border: 0px solid #fff;
  border-radius: 999rem;
  background-color:#0B57D1;
  background-color:#000;
    color:#fff;
}
button:hover
{
  background-color:#777;
  cursor: pointer;
}

.password-wrapper {
  position: relative;
  display: inline-block;
  width: 100%;
}

.button-password {
  position: absolute;
  left: 290px;
  top: 45%;
  transform: translateY(-50%);
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: #000;
  font-size: 18px;
  transition: color 0.2s ease;
}

.button-password:hover
{
  background: none;
  color: #777;
}

.password
{
    display:flex;
    align-items:center;
    width:300px;
}
.password input
{
    width:300px;
}
.password button
{
    width:40px;
    margin-left:50px;
    padding:50px;
}
</style>
<script>
function togglePassword(id, btn)
{
    let input = document.getElementById(id);
    if(input.type==="password")
    {
        input.type="text";
        btn.innerHTML="👁";
    }
    else
    {
        input.type="password";
        btn.innerHTML="👁";
    }
}
</script>
</head>
<body>
<form action="/settings" method="GET">
<h2>Settings</h2>
<button type="button" style="position:absolute;top:10px;right:5px;padding:16px 20px;" onclick="location='/'">X</button>
<table>
<tr>
<td>WiFi Hostname</td>
<td><input name="wifi_hostname" value="%wifi_hostname%"></td>
</tr>
<tr>
<td>WiFi SSID</td>
<td><input name="wifi_ssid" value="%wifi_ssid%"></td>
</tr>
<tr>
<td>WiFi Password</td>
<td>
<div class="password-wrapper">
<input type="password" id="wifi_password" name="wifi_password" value="%wifi_password%">
<button type="button" class="button-password" onclick="togglePassword('wifi_password',this)">👁</button>
</div>
</td>
</tr>
<tr>
<td>WWW Login</td>
<td><input name="www_login" value="%www_login%"></td>
</tr>
<tr>
<td>WWW Password</td>
<td>
<div class="password-wrapper">
<input type="password" id="www_password" name="www_password" value="%www_password%">
<button type="button" class="button-password" onclick="togglePassword('www_password',this)">👁</button>
</div>
</td>
</tr>
<tr>
<td>Build</td>
<td><input value="%build%" readonly></td>
</tr>
</table>
<br>
<div style="display: flex; justify-content: center; align-items: center; height: 100%;">
<button type="button" onclick="location='/settings?reset'">Factory reset</button>
<button type="button" onclick="location='/settings?restart'">Restart</button>
<button type="submit">Save</button>
</div>
</form>
</body>
</html>
)rawliteral";

const char WebSocket_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html style="height: 100%;">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body
    {
      margin: 0;
      height: 100%;
      background-color: rgb(14, 14, 14);
      display: flex;
      justify-content: center;
      align-items: center;
    }
    img
    {
      max-width: 100%;
      max-height: 100%;
      width: auto;
      height: auto;
      object-fit: contain;
      display: block;
      user-select: none;
      background-color: hsl(0, 0%, 10%);
    }
  </style>
</head>
<body>
  <img id="cam">
  <script>
    let ws = new WebSocket("ws://" + location.host + "/ws");
    ws.binaryType = "arraybuffer";

    ws.onmessage = function(event) {
        let blob = new Blob([event.data], {type:"image/jpeg"});
        document.getElementById("cam").src = URL.createObjectURL(blob);
    };
  </script>
  <!--
  <img id="img" src="/capture">
  <script>
    setInterval(() => {
      document.getElementById('img').src = '/capture?' + Date.now();
    }, 300);
  </script>
  -->
</body>
</html>
)rawliteral";

String htmlReplace(String html,String key,String value)
{
    html.replace("%"+key+"%",value);
    return html;
}

esp_err_t handleIndex(httpd_req_t *req)
{
    if (!checkAuthenticated(req)) return ESP_OK;
      String page = index_html;
      page = htmlReplace(page, "hostname", get_WiFi_HostName_MAC());
    httpd_resp_set_type(req, "text/html");
  //httpd_resp_send(req, index_html, HTTPD_RESP_USE_STRLEN);
      httpd_resp_send(req, page.c_str(), page.length());
    return ESP_OK;
}
httpd_uri_t uri_index = { .uri = "/", .method = HTTP_GET, .handler = handleIndex, .user_ctx = NULL };

esp_err_t handleSettings(httpd_req_t *req)
{
    if (!checkAuthenticated(req)) return ESP_OK;
    char query[512] = "";
    bool hasQuery = false;
    if (httpd_req_get_url_query_len(req))
    {
        httpd_req_get_url_query_str(req, query, sizeof(query));
        hasQuery = true;
    }
    if (hasQuery)
    {
        prefs.begin("setting", false);
        char value[128];
        if (httpd_query_key_value(query, "wifi_hostname", value, sizeof(value)) == ESP_OK) prefs.putString("wifi_hostname", value);
        if (httpd_query_key_value(query, "wifi_ssid", value, sizeof(value)) == ESP_OK) prefs.putString("wifi_ssid", value);
        if (httpd_query_key_value(query, "wifi_password", value, sizeof(value)) == ESP_OK) prefs.putString("wifi_password", value);
        if (httpd_query_key_value(query, "www_login", value, sizeof(value)) == ESP_OK) prefs.putString("www_login", value);
        if (httpd_query_key_value(query, "www_password", value, sizeof(value)) == ESP_OK) prefs.putString("www_password", value);
        if (strstr(query, "reset")) prefs.clear();
        prefs.end();
        if (strstr(query, "restart") ||
            strstr(query, "reset") ||
            strstr(query, "wifi_hostname=") ||
            strstr(query, "wifi_ssid=") ||
            strstr(query, "wifi_password=") ||
            strstr(query, "www_login=") ||
            strstr(query, "www_password="))
        {
            httpd_resp_sendstr(req,
            "<html>"
            "<head>"
            "<meta http-equiv='refresh' content='1'>"
            "<style>"
            "body{background:#111;color:#fff;font-family:Arial;"
            "display:flex;justify-content:center;align-items:center;"
            "height:100vh;margin:0;}"
            "</style>"
            "</head>"
            "<body>"
            "<h2>Restart...</h2>"
            "<script>setTimeout(function(){window.location='/';},1000);</script>"
            "</body>"
            "</html>");
            delay(5000);
            ESP.restart();
            return ESP_OK;
        }
    }

    prefs.begin("setting", true);

    String page = Settings_html;

    page = htmlReplace(page, "hostname", get_WiFi_HostName_MAC());
    page = htmlReplace(page,"wifi_hostname",prefs.getString("wifi_hostname",""));
    page = htmlReplace(page,"wifi_ssid",prefs.getString("wifi_ssid",""));
    page = htmlReplace(page,"wifi_password",prefs.getString("wifi_password",""));
    page = htmlReplace(page,"www_login",prefs.getString("www_login",""));
    page = htmlReplace(page,"www_password",prefs.getString("www_password",""));
    page = htmlReplace(page,"build",prefs.getString("build",""));

    prefs.end();

    httpd_resp_set_type(req,"text/html");
    httpd_resp_send(req,page.c_str(),page.length());

    return ESP_OK;
}
httpd_uri_t uri_settings = { .uri = "/settings", .method = HTTP_GET, .handler = handleSettings, .user_ctx = NULL };
httpd_uri_t uri_setting = { .uri = "/setting", .method = HTTP_GET, .handler = handleSettings, .user_ctx = NULL };

esp_err_t handleOptions(httpd_req_t *req)
{
    httpd_resp_sendstr(req, "<html><body>Options...</body></html>");
    return ESP_OK;
}
httpd_uri_t uri_options= { .uri="/options", .method=HTTP_GET, .handler=handleOptions, .user_ctx=NULL };
httpd_uri_t uri_option= { .uri="/option", .method=HTTP_GET, .handler=handleOptions, .user_ctx=NULL };

esp_err_t handleCapture(httpd_req_t *req)
{
    if (!checkAuthenticated(req)) return ESP_OK;
    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb)
    {
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }
    httpd_resp_set_type(req, "image/jpeg");
    httpd_resp_set_hdr(req, "Cache-Control", "no-store");
    httpd_resp_send(req, (const char *)fb->buf, fb->len);
    esp_camera_fb_return(fb);
    return ESP_OK;
}
httpd_uri_t uri_capture = { .uri = "/capture", .method = HTTP_GET, .handler = handleCapture, .user_ctx = NULL };

esp_err_t handleStream(httpd_req_t *req)
{
    if (!checkAuthenticated(req)) return ESP_OK;
    httpd_resp_set_type(req, "multipart/x-mixed-replace;boundary=frame");
    while (true)
    {
        camera_fb_t *fb = esp_camera_fb_get();
        if (!fb) break;
        esp_err_t res;
        res = httpd_resp_send_chunk( req, "--frame\r\nContent-Type: image/jpeg\r\n\r\n", -1);
        if (res != ESP_OK)
        {
            esp_camera_fb_return(fb);
            break;
        }
        res = httpd_resp_send_chunk( req, (const char *)fb->buf, fb->len);
        esp_camera_fb_return(fb);
        if (res != ESP_OK) break;
        res = httpd_resp_send_chunk(req, "\r\n", 2);
        if (res != ESP_OK) break;
        delay(50);
    }
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}
httpd_uri_t uri_stream = { .uri = "/stream", .method = HTTP_GET, .handler = handleStream, .user_ctx = NULL };

esp_err_t handleWebSocket(httpd_req_t *req)
{
    if (!checkAuthenticated(req)) return ESP_OK;
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, WebSocket_html, HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}
httpd_uri_t uri_websocket = { .uri = "/websocket", .method = HTTP_GET, .handler = handleWebSocket, .user_ctx = NULL };

esp_err_t handleWS(httpd_req_t *req)
{
    if (!checkAuthenticated(req)) return ESP_OK;
    // Подключение клиента (handshake)
    if (req->method == HTTP_GET)
    {
        int fd = httpd_req_to_sockfd(req);
        xSemaphoreTake(wsMutex, portMAX_DELAY);
        if (std::find(ws_clients.begin(), ws_clients.end(), fd) == ws_clients.end())
        {
            ws_clients.push_back(fd);
        }
        xSemaphoreGive(wsMutex);
        return ESP_OK;
    }
    // Получение данных от клиента (если надо)
    httpd_ws_frame_t ws_pkt;
    memset(&ws_pkt, 0, sizeof(ws_pkt));
    ws_pkt.type = HTTPD_WS_TYPE_BINARY;
    esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, 0);
    if (ret != ESP_OK) return ret;
    if (ws_pkt.len)
    {
        uint8_t *buf = (uint8_t*)malloc(ws_pkt.len);
        if (!buf) return ESP_ERR_NO_MEM;
        ws_pkt.payload = buf;
        ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len);
        free(buf);
    }
    return ESP_OK;
}
httpd_uri_t uri_ws = { .uri = "/ws", .method = HTTP_GET, .handler = handleWS, .user_ctx = NULL, .is_websocket = true };

void wsSendFrame()
{
    if (ws_clients.empty()) return;
    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb) return;
    // копируем JPEG в отдельную память
    uint8_t *jpg = (uint8_t*)malloc(fb->len);
    if (!jpg)
    {
        esp_camera_fb_return(fb);
        return;
    }
    memcpy(jpg, fb->buf, fb->len);
    size_t jpgLen = fb->len;
    // камеру освобождаем сразу
    esp_camera_fb_return(fb);
    httpd_ws_frame_t frame = {};
    frame.type = HTTPD_WS_TYPE_BINARY;
    frame.payload = jpg;
    frame.len = jpgLen;
    xSemaphoreTake(wsMutex, portMAX_DELAY);
    for (auto it = ws_clients.begin(); it != ws_clients.end(); )
    {
        esp_err_t err = httpd_ws_send_frame_async(server, *it, &frame);

        if (err != ESP_OK)
        {
            it = ws_clients.erase(it);
        }
        else
        {
            ++it;
        }
    }
    xSemaphoreGive(wsMutex);
    free(jpg);
}

void printESP32Info()
{
  Serial.print("Chip type: ");
  Serial.print(ESP.getChipModel());
  Serial.print(" (revision ");
  Serial.print(ESP.getChipRevision());
  Serial.println(")");

  Serial.print("Cores: ");
  Serial.print(ESP.getChipCores());
  Serial.print(" CPU freq: ");
  Serial.print(ESP.getCpuFreqMHz());
  Serial.print(" MHz");
  Serial.print(" Crystal frequency: ");
  Serial.print(rtc_clk_xtal_freq_get());
  Serial.println(" MHz");

  Serial.print("Flash: ");
  Serial.print(ESP.getFlashChipSize()); // ESP.getFlashChipSize() / 1024 / 1024
  Serial.print(" Sketch: ");
  Serial.print(ESP.getSketchSize());
  Serial.print(" Free: ");
  Serial.print(ESP.getFreeSketchSpace());
  Serial.println(" bytes");

  Serial.print("Heap: ");
  Serial.print(ESP.getHeapSize());
  Serial.print(" Free: ");
  Serial.print(ESP.getFreeHeap());
  Serial.println(" bytes");

  Serial.print("PSRAM: ");
  Serial.print(ESP.getPsramSize());
  Serial.print(" Free: ");
  Serial.print(ESP.getFreePsram());
  Serial.println(" bytes");

  uint8_t mac[6];
  esp_read_mac(mac, ESP_MAC_WIFI_STA);
  Serial.printf(
    "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
    mac[0], mac[1], mac[2],
    mac[3], mac[4], mac[5]
  );
}

void printResetReason()
{
  esp_reset_reason_t reason = esp_reset_reason();
  Serial.print((int)reason);
  Serial.print(" - ");
  switch (reason)
  {
    case ESP_RST_POWERON:
      Serial.println("Power-on reset");
      break;
    case ESP_RST_EXT:
      Serial.println("External reset (reset pin)");
      break;
    case ESP_RST_SW:
      Serial.println("Software reset (ESP.restart)");
      break;
    case ESP_RST_PANIC:
      Serial.println("Exception / crash (panic)");
      break;
    case ESP_RST_INT_WDT:
      Serial.println("Interrupt watchdog");
      break;
    case ESP_RST_TASK_WDT:
      Serial.println("Task watchdog");
      break;
    case ESP_RST_WDT:
      Serial.println("Other watchdog reset");
      break;
    case ESP_RST_DEEPSLEEP:
      Serial.println("Wake from deep sleep");
      break;
    case ESP_RST_BROWNOUT:
      Serial.println("Brownout (low voltage)");
      break;
    case ESP_RST_SDIO:
      Serial.println("SDIO reset");
      break;
    default:
      Serial.println("Unknown reason");
      break;
  }
}

String getBuild()
{
  const char* months = "JanFebMarAprMayJunJulAugSepOctNovDec";
  String date = __DATE__;
  String time = __TIME__;
  int month = (strstr(months, date.substring(0, 3).c_str()) - months) / 3 + 1;
  int day = date.substring(4, 6).toInt();
  int year = date.substring(7).toInt();
  int hour = time.substring(0, 2).toInt();
  int min  = time.substring(3, 5).toInt();
  int sec  = time.substring(6, 8).toInt();
  char buf[16];
  sprintf(buf, "%04d%02d%02d%02d%02d%02d", year, month, day, hour, min, sec);
  return String(buf);
}

void initSetting()
{
  prefs.begin("setting", false); // false = можем читать и писать

  String savedBuild = prefs.getString("build", "");
  String currentBuild = getBuild();

  if (savedBuild != currentBuild)
  {
    Serial.println("");
    Serial.println("Firmware build: " + savedBuild + " -> " + currentBuild);
    prefs.clear();
    prefs.putString("wifi_hostname", WiFi_HostName);
    prefs.putString("wifi_ssid", WiFi_SSID);
    prefs.putString("wifi_password", WiFi_Password);
    prefs.putString("www_login", www_Login);
    prefs.putString("www_password", www_Password);
    prefs.putString("build", currentBuild);
  }
  Serial.println("");
  Serial.println("Firmware build: " + currentBuild);
  prefs.getString("wifi_hostname", WiFi_HostName, sizeof(WiFi_HostName));
  prefs.getString("wifi_ssid",     WiFi_SSID,     sizeof(WiFi_SSID));
  prefs.getString("wifi_password", WiFi_Password, sizeof(WiFi_Password));
  prefs.getString("www_login",    www_Login,     sizeof(www_Login));
  prefs.getString("www_password",  www_Password,  sizeof(www_Password));
  prefs.end();
}

void CommandLine(String cmd)
{
  prefs.begin("setting", false);

  if (cmd.startsWith("wifi_hostname="))
  {
    String value = cmd.substring(14);
    prefs.putString("wifi_hostname", value);
    Serial.println("wifi_hostname = " + value);
  }
  else if (cmd.startsWith("wifi_ssid="))
  {
    String value = cmd.substring(10);
    prefs.putString("wifi_ssid", value);
    Serial.println("wifi_ssid = " + value);
  }
  else if (cmd.startsWith("wifi_password="))
  {
    String value = cmd.substring(14);
    prefs.putString("wifi_password", value);
    Serial.println("wifi_password = " + value);
  }
  else if (cmd.startsWith("www_login="))
  {
    String value = cmd.substring(10);
    prefs.putString("www_login", value);
    Serial.println("www_login = " + value);
  }
  else if (cmd.startsWith("www_password="))
  {
    String value = cmd.substring(13);
    prefs.putString("www_password", value);
    Serial.println("www_password = " + value);
  }
  else if (cmd.startsWith("build="))
  {
    String value = cmd.substring(6);
    prefs.putString("build", value);
    Serial.println("build = " + value);
  }
  else if (cmd.startsWith("build="))
  {
    String value = cmd.substring(6);
    prefs.putString("build", value);
    Serial.println("build = " + value);
  }
  else if (cmd == "restart")
  {
    Serial.println("Restart ...");
    prefs.end();
    delay(1000);
    ESP.restart();
  }else if (cmd == "reset")
  {
    Serial.println("Factory reset ...");
    prefs.clear();
    prefs.end();
    delay(1000);
    ESP.restart();
  }
  else if (cmd == "?")
  {
    if (LocalHost)
    {
      Serial.println("HOST : " + String(WiFi.getHostname()));                 // имя хоста
      String WiFi_HostName_MAC = get_WiFi_HostName_MAC();
      Serial.println("SSID : " + String(WiFi_HostName_MAC));
      Serial.println("Password : " + String(WiFi_HostName_MAC));
      Serial.println(String("IP Hotspot: ") + WiFi.softAPIP().toString());    // IP точки доступа
      Serial.println("MAC : " + String(WiFi.softAPmacAddress()));
      Serial.println("Channel : " + String(WiFi.channel()));
      Serial.println("Clients : " + String(WiFi.softAPgetStationNum()));
    }
    else
    {
      Serial.println("HOST : " + String(WiFi.getHostname()));                 // имя хоста
      Serial.println("SSID : " + String(WiFi.SSID()));                        // имя подключенной сети
      Serial.println("Password : " + String(WiFi_Password) );                 // пароль подключенной сети
      Serial.println(String("IP : ") + WiFi.localIP().toString());            // IP
      Serial.println(String("GATEWAY : ") + WiFi.gatewayIP().toString());     // шлюз
      Serial.println(String("MASK : ") + WiFi.subnetMask().toString());       // маска
      Serial.println(String("DNS : ") + WiFi.dnsIP().toString());             // DNS
      Serial.println("MAC : " + String(WiFi.macAddress()));                   // MAC
      Serial.println("MAC Hotspot : " + String(WiFi.BSSIDstr()));             // MAC точки доступа
      Serial.println("Channel : " + String(WiFi.channel()));
      Serial.println("RSSI : " + String(WiFi.RSSI()));                        // уровень сигнала
    }
  Serial.println("------------------");
  Serial.println("wifi_hostname=" + prefs.getString("wifi_hostname", ""));
  Serial.println("wifi_ssid=" + prefs.getString("wifi_ssid", ""));
  Serial.println("wifi_password=" + prefs.getString("wifi_password", ""));
  Serial.println("www_login=" + prefs.getString("www_login", ""));
  Serial.println("www_password=" + prefs.getString("www_password", ""));
  Serial.println("build=" + prefs.getString("build", ""));
  Serial.println("------------------");
  Serial.println("restart -> Software reset");
  Serial.println("reset -> Factory Reset ");
  Serial.println("------------------");
  }
  else
  {
    Serial.println("Unknown command");
  }
  prefs.end();
}

void setup()
{
  Serial.begin(115200);
  delay(1000);

  initSetting();

  printResetReason();
  printESP32Info();

  if (!initCamera())
  {
    Serial.println("Camera init failed");
    while (true) delay(1000);
  }
  Serial.println("Camera OK");
  Serial.println();

  String WiFi_HostName_MAC = get_WiFi_HostName_MAC();

  WiFi.mode(WIFI_STA);
  WiFi.setHostname(WiFi_HostName);
  WiFi.begin(WiFi_SSID, WiFi_Password);

  Serial.print("Connecting WiFi ");

    unsigned long startTime = millis();
    while (WiFi.status() != WL_CONNECTED && millis() - startTime < 20000)
    {
        delay(500);
        Serial.print(".");
    }
    Serial.println();

    if (WiFi.status() == WL_CONNECTED)
    {
        Serial.println("WiFi connected");
    }
    else
    {
        Serial.println("WiFi failed");
        WiFi.disconnect(true);
        delay(500);

        //WiFi.mode(WIFI_AP);
        WiFi.mode(WIFI_AP_STA);
        IPAddress local_ip(10, 10, 10, 10);
        IPAddress gateway(10, 10, 10, 10);
        IPAddress subnet(255, 255, 255, 0);
        WiFi.softAPConfig(local_ip, gateway, subnet);

        bool ok = WiFi.softAP(WiFi_HostName_MAC, WiFi_HostName_MAC);
        if (ok)
        {
            LocalHost = true;
            Serial.println("Hotspot started");
        }
        else
        {
            Serial.println("Hotspot failed");
        }
    }

  Serial.print("IP : ");
  if (LocalHost) Serial.print(WiFi.softAPIP()); else Serial.print(WiFi.localIP());
  Serial.print(" /capture");
  Serial.print(" /websocket");
  Serial.println(" /stream");
  Serial.print("http://" + String(www_Login) + ":" + String(www_Password) + "@");
  if (LocalHost) Serial.print(WiFi.softAPIP()); else Serial.print(WiFi.localIP());
  Serial.println("/");

  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.max_uri_handlers = 16;
  wsMutex = xSemaphoreCreateMutex();
  httpd_start(&server, &config);
  httpd_register_uri_handler(server, &uri_index);
  httpd_register_uri_handler(server, &uri_settings);
  httpd_register_uri_handler(server, &uri_setting);
  httpd_register_uri_handler(server, &uri_options);
  httpd_register_uri_handler(server, &uri_option);
  httpd_register_uri_handler(server, &uri_capture);
  httpd_register_uri_handler(server, &uri_stream);
  httpd_register_uri_handler(server, &uri_websocket);
  httpd_register_uri_handler(server, &uri_ws);
}

void loop()
{
  static unsigned long last = 0;
  if (millis() - last > 33) // 200 = 5fps , 100 = 10 fps , 50 = 20 fps , 33 = 30 fps
  {
      last = millis();
      wsSendFrame();
  }
  if (Serial.available())
  {
    String cmd = Serial.readStringUntil('\n');
    cmd.trim();
    CommandLine(cmd);
  }
  if (LocalHost)
  {  
    static unsigned long RestartTime = 0;
    if (WiFi.softAPgetStationNum() == 0)
    {
        if (RestartTime == 0)
        {
            RestartTime = millis();   // клиент только что отключился
        }

        if (millis() - RestartTime >= 60000UL)
        {
            ESP.restart();
        }
    }
    else
    {
        RestartTime = 0;
    }
    //Serial.println("-> " + String(millis()) + " - " + String(RestartTime) + " = " + String(millis() - RestartTime) + " > 60000UL");
  }
  else if (!LocalHost)
  {
    if (WiFi.status() != WL_CONNECTED)
    {
        Serial.println("WiFi disconnect");
        delay(1000);
        ESP.restart();
    }
  }
}

Популярные сообщения из этого блога

C# Console Application FolderBrowserDialog and OpenFileDialog TopMost

C# CRC32

C# Windows Form - CefSharpBrowser - Authentication Required