Blame | Last modification | View Log | RSS feed
#include <FS.h>#include <SD_MMC.h>#include <AsyncFsWebServer.h> // https://github.com/cotestatnt/async-esp-fs-webserver/#include "esp_camera.h"#include "soc/soc.h" // Brownout error fix#include "soc/rtc_cntl_reg.h" // Brownout error fix#if ESP_ARDUINO_VERSION_MAJOR >= 3#include "soc/soc_caps.h"#endif#define FILESYSTEM SD_MMCAsyncFsWebServer server(FILESYSTEM, 80);// Local include files#include "camera_pins.h"uint16_t grabInterval = 0; // Grab a picture every x secondsuint32_t lastGrabTime = 0;// Timezone definition to get properly time from NTP server#define MYTZ "CET-1CEST,M3.5.0,M10.5.0/3"// Struct for saving time datas (needed for time-naming the image files)struct tm tInfo;// Functions prototypevoid listDir(const char *, uint8_t);void setLamp(int);// Grab a picture from CAM and store on SD or in flashvoid getPicture(AsyncWebServerRequest *);const char* getFolder = "/img";/////////////////////////////////// SETUP ///////////////////////////////////////void setup() {WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detect// Flash LED setuppinMode(LAMP_PIN, OUTPUT); // set the lamp pin as output#if ESP_ARDUINO_VERSION_MAJOR >= 3ledcAttach(LAMP_PIN, 1000, 8);#elseledcSetup(lampChannel, pwmfreq, pwmresolution); // configure LED PWM channelledcAttachPin(LAMP_PIN, lampChannel);#endifsetLamp(0); // set default valueSerial.begin(115200);Serial.println();// Try to connect to WiFi (will start AP if not connected after timeout)if (!server.startWiFi(10000)) {Serial.println("\nWiFi not connected! Starting AP mode...");server.startCaptivePortal("ESP32CAM_AP", "123456789", "/setup");}// Sync time with NTPconfigTzTime(MYTZ, "time.google.com", "time.windows.com", "pool.ntp.org");/*Init onboard SD filesystem (format if necessary)SD_MMC.begin(const char * mountpoint, bool mode1bit, bool format_if_mount_failed, int sdmmc_frequency, uint8_t maxOpenFiles)To avoid led glowing, set mode1bit = true (SD HS_DATA1 is tied to GPIO4, the same of on-board flash led)*/if (!SD_MMC.begin("/sdcard", true, true, SDMMC_FREQ_HIGHSPEED, 5)) {Serial.println("\nSD Mount Failed.\n");}if (!SD_MMC.exists(getFolder)) {if(SD_MMC.mkdir(getFolder))Serial.println("Dir created");elseSerial.println("mkdir failed");}listDir(getFolder, 1);// Enable ACE FS file web editor and add FS info callback functionserver.enableFsCodeEditor();// Add custom handlers to webserverserver.on("/getPicture", getPicture);server.on("/setInterval", setInterval);// Start server with built-in websocket event handlerserver.init();Serial.print(F("\nESP Web Server started on IP Address: "));Serial.println(server.getServerIP());Serial.println(F("This is \"remoteOTA.ino\" example.\n""Open /setup page to configure optional parameters.\n""Open /edit page to view, edit or upload example or your custom webserver source files."));// Init the camera module (according the camera_config_t defined)init_camera();}/////////////////////////////////// LOOP ///////////////////////////////////////void loop() {if (grabInterval) {if (millis() - lastGrabTime > grabInterval *1000) {lastGrabTime = millis();getPicture(nullptr);}}}////////////////////////////////// FUNCTIONS//////////////////////////////////////void setInterval(AsyncWebServerRequest *request) {if (request->hasArg("val")) {grabInterval = request->arg("val").toInt();Serial.printf("Set grab interval every %d seconds\n", grabInterval);}request->send(200, "text/plain", "OK");}// Lamp Controlvoid setLamp(int newVal) {if (newVal != -1) {// Apply a logarithmic function to the scale.int brightness = round((pow(2, (1 + (newVal * 0.02))) - 2) / 6 * pwmMax);ledcWrite(lampChannel, brightness);Serial.print("Lamp: ");Serial.print(newVal);Serial.print("%, pwm = ");Serial.println(brightness);}}// Send a picture taken from CAM to a Telegram chatvoid getPicture(AsyncWebServerRequest *request) {// Take Picture with Camera;Serial.println("Camera capture requested");// Take Picture with Camera and store in ram buffer fbsetLamp(100);delay(100);camera_fb_t *fb = esp_camera_fb_get();setLamp(0);if (!fb) {Serial.println("Camera capture failed");if (request != nullptr)request->send(500, "text/plain", "ERROR. Image grab failed");return;}// Keep files on SD memory, filename is time based (YYYYMMDD_HHMMSS.jpg)// Embedded filesystem is too small to keep all images, overwrite the same filechar filename[20];time_t now = time(nullptr);tInfo = *localtime(&now);strftime(filename, sizeof(filename), "%Y%m%d_%H%M%S.jpg", &tInfo);char filePath[30];strcpy(filePath, getFolder);strcat(filePath, "/");strcat(filePath, filename);File file = SD_MMC.open(filePath, "w");if (!file) {Serial.println("Failed to open file in writing mode");if(request != nullptr)request->send(500, "text/plain", "ERROR. Image grab failed");return;}// size_t _jpg_buf_len = 0;// uint8_t *_jpg_buf = NULL;// bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);file.write(fb->buf, fb->len);file.close();Serial.printf("Saved file to path: %s - %zu bytes\n", filePath, fb->len);// Clear bufferesp_camera_fb_return(fb);if (request != nullptr)request->send(200, "text/plain", filename);}// List all files saved in the selected filesystemvoid listDir(const char *dirname, uint8_t levels) {uint32_t freeBytes = SD_MMC.totalBytes() - SD_MMC.usedBytes();Serial.print("\nTotal space: ");Serial.println(SD_MMC.totalBytes());Serial.print("Free space: ");Serial.println(freeBytes);Serial.printf("Listing directory: %s\r\n", dirname);File root = SD_MMC.open(dirname);if (!root) {Serial.println("- failed to open directory\n");return;}if (!root.isDirectory()) {Serial.println(" - not a directory\n");return;}File file = root.openNextFile();while (file) {if (file.isDirectory()) {if (levels)listDir(file.name(), levels - 1);}else {Serial.printf("|__ FILE: %s (%d bytes)\n",file.name(), file.size());}file = root.openNextFile();}}