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();
}
}
}