#include #include #include #include #include #include #include "myconfig.h" #include "esp_camera.h" #include "camera_pins.h" #include #include #include #include "src/html/firmware.h" #include "src/html/index_ov2640.h" #include "src/html/index_ov3660.h" #include "src/html/index_simple.h" #include "src/html/css.h" #include "src/favicons.h" #include "src/logo.h" #include "storage.h" // Functions from the main .ino extern void flashLED(int flashtime); extern void setLamp(int newVal); extern void resetWiFiConfig(); // External variables declared in the main .ino extern char camera_name[]; extern char myVer[]; extern char baseVersion[]; extern IPAddress espIP; extern IPAddress espSubnet; extern IPAddress espGateway; extern char streamURL[]; extern char default_index[]; extern int myRotation; extern int lampVal; extern bool filesystem; extern bool debugData; extern int sketchSize; extern int sketchSpace; extern String sketchMD5; #include "fb_gfx.h" #include "fd_forward.h" #include "fr_forward.h" #if !defined(HTTP_PORT) #define HTTP_PORT 80 #endif int httpPort = HTTP_PORT; char httpURL[64] = {"Undefined"}; AsyncWebServer appServer(httpPort); bool updating; void appServerCB(void *pvParameters); size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_t len); void capture_handler(AsyncWebServerRequest *request); void cmd_handler(AsyncWebServerRequest *request); void status_handler(AsyncWebServerRequest *request); void favicon_16x16_handler(AsyncWebServerRequest *request); void favicon_32x32_handler(AsyncWebServerRequest *request); void favicon_ico_handler(AsyncWebServerRequest *request); void logo_svg_handler(AsyncWebServerRequest *request); void dump_handler(AsyncWebServerRequest *request); void style_handler(AsyncWebServerRequest *request); void handleIndex(AsyncWebServerRequest *request); void handleFirmware(AsyncWebServerRequest *request); void handleCameraClient(); void startCameraServer(); #define PART_BOUNDARY "123456789000000000000987654321"; void capture_handler(AsyncWebServerRequest *request) { camera_fb_t *fb = NULL; Serial.println("Capture Requested"); flashLED(75); // little flash of status LED int64_t fr_start = esp_timer_get_time(); fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); request->send(500, "text/plain", "Camera Capture Failed"); return; } size_t fb_len = 0; if (fb->format == PIXFORMAT_JPEG) { fb_len = fb->len; request->send(200, "image/jpeg", (const char *)fb->buf); } else { request->send(500, "text/plain", "Camera Capture Failed"); } esp_camera_fb_return(fb); int64_t fr_end = esp_timer_get_time(); Serial.printf("JPG: %uB %ums\r\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start) / 1000)); } void cmd_handler(AsyncWebServerRequest *request) { Serial.println("Command received!"); flashLED(75); if (!request->hasArg("var") || !request->hasArg("val")) { Serial.println("No var or val"); request->send(404, "text/plain", "Invalid parameters"); return; } String variable = request->arg("var").c_str(); String value = request->arg("val").c_str(); Serial.print("Var "); Serial.println(variable); int val = atoi(value.c_str()); sensor_t *s = esp_camera_sensor_get(); int res = 0; if (variable.compareTo("framesize") == 0) { if (s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val); } else if (variable.compareTo("quality") == 0) res = s->set_quality(s, val); else if (variable.compareTo("contrast") == 0) res = s->set_contrast(s, val); else if (variable.compareTo("brightness") == 0) res = s->set_brightness(s, val); else if (variable.compareTo("saturation") == 0) res = s->set_saturation(s, val); else if (variable.compareTo("gainceiling") == 0) res = s->set_gainceiling(s, (gainceiling_t)val); else if (variable.compareTo("colorbar") == 0) res = s->set_colorbar(s, val); else if (variable.compareTo("awb") == 0) res = s->set_whitebal(s, val); else if (variable.compareTo("agc") == 0) res = s->set_gain_ctrl(s, val); else if (variable.compareTo("aec") == 0) res = s->set_exposure_ctrl(s, val); else if (variable.compareTo("hmirror") == 0) res = s->set_hmirror(s, val); else if (variable.compareTo("vFlip") == 0) res = s->set_vflip(s, val); else if (variable.compareTo("awb_gain") == 0) res = s->set_awb_gain(s, val); else if (variable.compareTo("agc_gain") == 0) res = s->set_agc_gain(s, val); else if (variable.compareTo("aec_value") == 0) res = s->set_aec_value(s, val); else if (variable.compareTo("aec2") == 0) res = s->set_aec2(s, val); else if (variable.compareTo("dcw") == 0) res = s->set_dcw(s, val); else if (variable.compareTo("bpc") == 0) res = s->set_bpc(s, val); else if (variable.compareTo("wpc") == 0) res = s->set_wpc(s, val); else if (variable.compareTo("raw_gma") == 0) res = s->set_raw_gma(s, val); else if (variable.compareTo("lenc") == 0) res = s->set_lenc(s, val); else if (variable.compareTo("special_effect") == 0) res = s->set_special_effect(s, val); else if (variable.compareTo("wb_mode") == 0) res = s->set_wb_mode(s, val); else if (variable.compareTo("ae_level") == 0) res = s->set_ae_level(s, val); else if (variable.compareTo("rotate") == 0) myRotation = val; else if (variable.compareTo("lamp") == 0) { lampVal = constrain(val, 0, 100); setLamp(lampVal); } else if (variable.compareTo("save_prefs") == 0) { if (filesystem) savePrefs(SPIFFS); } else if (variable.compareTo("clear_prefs") == 0) { if (filesystem) removePrefs(SPIFFS); } else if (variable.compareTo("reboot") == 0) { request->send(200, "text/plain", "Rebooting..."); Serial.print("REBOOT requested"); for (int i = 0; i < 20; i++) { flashLED(50); delay(150); Serial.print('.'); } Serial.printf(" Thats all folks!\r\n\r\n"); ESP.restart(); } else if (variable.compareTo("clear_wifi") == 0) { request->send(200, "text/plain", "Reseting WiFi..."); Serial.println("Wifi reset requested"); resetWiFiConfig(); } else { res = -1; } if (res) { Serial.print("Unable to determine command: "); Serial.println(variable); request->send(404, "text/plain", "Invalid parameters"); return; } request->send(200); return; } void status_handler(AsyncWebServerRequest *request) { char json_response[1024]; sensor_t *s = esp_camera_sensor_get(); char *p = json_response; *p++ = '{'; p += sprintf(p, "\"lamp\":%d,", lampVal); p += sprintf(p, "\"framesize\":%u,", s->status.framesize); p += sprintf(p, "\"quality\":%u,", s->status.quality); p += sprintf(p, "\"brightness\":%d,", s->status.brightness); p += sprintf(p, "\"contrast\":%d,", s->status.contrast); p += sprintf(p, "\"saturation\":%d,", s->status.saturation); p += sprintf(p, "\"sharpness\":%d,", s->status.sharpness); p += sprintf(p, "\"special_effect\":%u,", s->status.special_effect); p += sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode); p += sprintf(p, "\"awb\":%u,", s->status.awb); p += sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain); p += sprintf(p, "\"aec\":%u,", s->status.aec); p += sprintf(p, "\"aec2\":%u,", s->status.aec2); p += sprintf(p, "\"ae_level\":%d,", s->status.ae_level); p += sprintf(p, "\"aec_value\":%u,", s->status.aec_value); p += sprintf(p, "\"agc\":%u,", s->status.agc); p += sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain); p += sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling); p += sprintf(p, "\"bpc\":%u,", s->status.bpc); p += sprintf(p, "\"wpc\":%u,", s->status.wpc); p += sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma); p += sprintf(p, "\"lenc\":%u,", s->status.lenc); p += sprintf(p, "\"vflip\":%u,", s->status.vflip); p += sprintf(p, "\"hmirror\":%u,", s->status.hmirror); p += sprintf(p, "\"dcw\":%u,", s->status.dcw); p += sprintf(p, "\"colorbar\":%u,", s->status.colorbar); p += sprintf(p, "\"cam_name\":\"%s\",", camera_name); p += sprintf(p, "\"code_ver\":\"%s\",", myVer); p += sprintf(p, "\"rotate\":\"%d\",", myRotation); p += sprintf(p, "\"stream_url\":\"%s\"", streamURL); *p++ = '}'; *p++ = 0; request->send(200, "application/json", json_response); } void favicon_16x16_handler(AsyncWebServerRequest *request) { request->send(200, "image/png", (const char *)favicon_16x16_png); } void favicon_32x32_handler(AsyncWebServerRequest *request) { request->send(200, "image/png", (const char *)favicon_32x32_png); } void favicon_ico_handler(AsyncWebServerRequest *request) { request->send(200, "image/png", (const char *)favicon_ico); } void logo_svg_handler(AsyncWebServerRequest *request) { request->send(200, "image/svg+xml", (const char *)logo_svg); } void dump_handler(AsyncWebServerRequest *request) { flashLED(75); Serial.println("\r\nDump Requested"); Serial.print("Preferences file: "); dumpPrefs(SPIFFS); char dumpOut[1200] = ""; char *d = dumpOut; // Header d += sprintf(d, "\r\n"); d += sprintf(d, "\r\n"); d += sprintf(d, "%s - Status\r\n", camera_name); d += sprintf(d, "\r\n"); d += sprintf(d, "\r\n"); d += sprintf(d, "\r\n"); d += sprintf(d, "\r\n\r\n"); d += sprintf(d, "\r\n"); d += sprintf(d, "

ESP32 Cam Webserver

\r\n"); // Module d += sprintf(d, "Name: %s
\r\n", camera_name); Serial.printf("Name: %s\r\n", camera_name); d += sprintf(d, "Firmware: %s (base: %s)
\r\n", myVer, baseVersion); Serial.printf("Firmware: %s (base: %s)\r\n", myVer, baseVersion); float sketchPct = 100 * sketchSize / sketchSpace; d += sprintf(d, "Sketch Size: %i (total: %i, %.1f%% used)
\r\n", sketchSize, sketchSpace, sketchPct); Serial.printf("Sketch Size: %i (total: %i, %.1f%% used)\r\n", sketchSize, sketchSpace, sketchPct); d += sprintf(d, "MD5: %s
\r\n", sketchMD5.c_str()); Serial.printf("MD5: %s\r\n", sketchMD5.c_str()); d += sprintf(d, "ESP sdk: %s
\r\n", ESP.getSdkVersion()); Serial.printf("ESP sdk: %s\r\n", ESP.getSdkVersion()); // Network d += sprintf(d, "

WiFi

\r\n"); d += sprintf(d, "Mode: Client
\r\n"); Serial.printf("Mode: Client\r\n"); String ssidName = WiFi.SSID(); d += sprintf(d, "SSID: %s
\r\n", ssidName.c_str()); Serial.printf("Ssid: %s\r\n", ssidName.c_str()); d += sprintf(d, "Rssi: %i
\r\n", WiFi.RSSI()); Serial.printf("Rssi: %i\r\n", WiFi.RSSI()); String bssid = WiFi.BSSIDstr(); d += sprintf(d, "BSSID: %s
\r\n", bssid.c_str()); Serial.printf("BSSID: %s\r\n", bssid.c_str()); d += sprintf(d, "IP address: %d.%d.%d.%d
\r\n", espIP[0], espIP[1], espIP[2], espIP[3]); Serial.printf("IP address: %d.%d.%d.%d\r\n", espIP[0], espIP[1], espIP[2], espIP[3]); d += sprintf(d, "Http port: %i
\r\n", httpPort); Serial.printf("Http port: %i\r\n", httpPort); byte mac[6]; WiFi.macAddress(mac); d += sprintf(d, "MAC: %02X:%02X:%02X:%02X:%02X:%02X
\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); Serial.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); // System d += sprintf(d, "

System

\r\n"); int64_t sec = esp_timer_get_time() / 1000000; int64_t upDays = int64_t(floor(sec / 86400)); int upHours = int64_t(floor(sec / 3600)) % 24; int upMin = int64_t(floor(sec / 60)) % 60; int upSec = sec % 60; d += sprintf(d, "Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)
\r\n", upDays, upHours, upMin, upSec); Serial.printf("Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)\r\n", upDays, upHours, upMin, upSec); d += sprintf(d, "Freq: %i MHz
\r\n", ESP.getCpuFreqMHz()); Serial.printf("Freq: %i MHz\r\n", ESP.getCpuFreqMHz()); d += sprintf(d, "Heap: %i, free: %i, min free: %i, max block: %i
\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); Serial.printf("Heap: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); d += sprintf(d, "Psram: %i, free: %i, min free: %i, max block: %i
\r\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); Serial.printf("Psram: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); if (filesystem) { d += sprintf(d, "Spiffs: %i, used: %i
\r\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); Serial.printf("Spiffs: %i, used: %i\r\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); } // Footer d += sprintf(d, "
\r\n"); d += sprintf(d, "\r\n"); d += sprintf(d, "\r\n"); d += sprintf(d, "
\r\n\r\n\r\n"); *d++ = 0; request->send(200, "image/html", dumpOut); } void style_handler(AsyncWebServerRequest *request) { request->send(200, "text/css", (const char *)style_css); } void handleIndex(AsyncWebServerRequest *request) { flashLED(150); // See if we have a specific target (full/simple/portal) and serve as appropriate String view = default_index; if (request->hasArg("view")) { view = request->arg("view").c_str(); } if (strncmp(view.c_str(), "simple", sizeof(view)) == 0) { Serial.println("Simple index page requested"); request->send_P(200, "text/html", (const char *)index_simple_html); return; } else if (strncmp(view.c_str(), "full", sizeof(view)) == 0) { sensor_t *s = esp_camera_sensor_get(); if (s->id.PID == OV3660_PID) { Serial.println("Full OV3660 index page requested"); request->send_P(200, "text/html", (const char *)index_ov3660_html); return; } Serial.println("Full OV2640 index page requested"); request->send_P(200, "text/html",(const char *)index_ov2640_html); return; } Serial.print("Unknown page requested: "); Serial.println(view); request->send(404, "text/plain", "Unknown page requested"); } void handleFirmware(AsyncWebServerRequest *request) { flashLED(75); // See if we have a specific target (full/simple/portal) and serve as appropriate Serial.println("Firmware page requested"); request->send(200, "text/html", firmware_html.c_str()); } String getHTMLHead() { String header = F(""); header += F(""); header += F(""); header += F(""); return header; } String getHTMLFoot() { return F(""); } void handleUpdate(AsyncWebServerRequest *request) { String response_message; response_message.reserve(1000); response_message = getHTMLHead(); response_message += ""; response_message += "Firmware = *.esp32.bin
SPIFFS = *.spiffs.bin
\
\
\
\
"; response_message += getHTMLFoot(); request->send(200, "text/html", response_message); }; void handleDoUpdate(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { updating = true; delay(500); if (!index) { // check file names for type int cmd = (filename.indexOf(F(".spiffs.bin")) > -1) ? U_SPIFFS : U_FLASH; if (cmd == U_FLASH && !(filename.indexOf(F("esp32.bin")) > -1)) return; // wrong image for ESP32 if (!Update.begin(UPDATE_SIZE_UNKNOWN, cmd)) { Update.printError(Serial); } } if (Update.write(data, len) != len) { Update.printError(Serial); } if (final) { if (!Update.end(true)) { Update.printError(Serial); } else { String response_message; response_message.reserve(1000); response_message = getHTMLHead(); response_message += "

Please wait while the device reboots

"; response_message += getHTMLFoot(); AsyncWebServerResponse *response = request->beginResponse(200, "text/html", response_message); response->addHeader("Refresh", "20"); response->addHeader("Location", "/"); request->send(response); delay(100); ESP.restart(); } } } void startCameraServer() { Serial.printf("Starting web server on port: '%d'\r\n", httpPort); appServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { handleIndex(request); }); appServer.on("/firmware", HTTP_GET, handleFirmware); appServer.on("/status", HTTP_GET, status_handler); appServer.on("/control", HTTP_GET, cmd_handler); appServer.on("/capture", HTTP_GET, capture_handler); appServer.on("/style.css", HTTP_GET, style_handler); appServer.on("/favicon-16x16.png", HTTP_GET, favicon_16x16_handler); appServer.on("/favicon-32x32.png", HTTP_GET, favicon_32x32_handler); appServer.on("/favicon.ico", HTTP_GET, favicon_ico_handler); appServer.on("/logo.svg", HTTP_GET, logo_svg_handler); appServer.on("/dump", HTTP_GET, dump_handler); /*handling uploading firmware file */ appServer.on("/update", HTTP_GET, [](AsyncWebServerRequest *request) { handleUpdate(request); }); appServer.on( "/doUpdate", HTTP_POST, [](AsyncWebServerRequest *request) {}, [](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { handleDoUpdate(request, filename, index, data, len, final); }); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); appServer.begin(); Serial.println("Web server started!"); sprintf(httpURL, "http://%d.%d.%d.%d:%d/", espIP[0], espIP[1], espIP[2], espIP[3], httpPort); }