/* ESP32-CAM l'oeil clignotemps TimeLapseAvi ESP32-CAM enregistreur Video pour CAMERA_MODEL_AI_THINKER by James Zahary July 20, 2019 TimeLapseAvi23x.ino jamzah.plc@gmail.com https://github.com/jameszah/ESP32-CAM-Video-Recorder This program records an AVI video on the SD Card of an ESP32-CAM. jameszah/ESP32-CAM-Video-Recorder is licensed under the GNU General Public License v3.0 modif gepeto@du-libre.org https://snhack.org/doku.php?id=projets:esp32-cam_ftp_avi_timelaps The is Arduino code, with standard setup for ESP32-CAM - Board ESP32 Wrover Module - Partition Scheme Huge APP (3MB No OTA) //server.on("/", index_handler); server.on("/capture", capture_handler); server.on("/start", start_handler); server.on("/stop", stop_handler); server.on("/saveconfig", saveSPIFFSConfigFile); server.on("/list", listDir_handler); server.on("/reset", reset_handler); Accet http://esp32-cam.local (devname dans le fichier config.jsn) /sdcard/config.jsn: {"devname":"esp32-cam", "framesize":10, "repeat":0, "xspeed":1, "gray":0, "quality":10, "capture_interval":10000, "length":1800, "ssid":"wifissid", "pass":"trucpass", "horaire":"6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21" } */ // // WARNING!!! Make sure that you have either selected ESP32 Wrover Module, // or another board which has PSRAM enabled // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // edit these parameters for your needs static const char vernum[] = "v10Gepeto"; // modif AccesPoint and http filesystem // plage horaire #include // 1 for blink red led with every sd card write, at your frame rate // 0 for blink only for skipping frames and SOS if camera or sd is broken #define BlinkWithWrite 0 static const char devname_conf[] = "epscam"; // nom de secour char devname[20] = "espcam"; // EDIT ssid and pass char ssid[40] = ""; char pass[20] = ""; const char ssid_ap[] = "ESPCAM"; const char pass_ap[] = "1234567890"; int invisible = 0; int canal_wifi = 6; int max_connexion = 4; int numero_fichier = 1; // si pas de serveur ntp retour a un index dans /sdcard/nofile.txt int connAttempts = 0; // si >10 mode AP bool bouton_init_ok = false; bool sleep_mode = false; long sleep_duree = 10 ; char horaire[80] = {0};// heures d'enregistrement char conf_horaire[] = "6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21";// heures d'enregistrement #define TIMEZONE "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" // de https://remotemonitoringsystems.ca/time-zone-abbreviations.php // startup defaults for first recordin // here are the recording options from the "retart web page" // framesize=UXGA&length=1800&interval=10000&quality=10&repeat=100&speed=1&gray=0 // VGA 10 fps for 30 min, repeat, realtime http://192.168.0.117/start?framesize=VGA&length=1800&interval=100&quality=10&repeat=100&speed=1&gray=0 // VGA 2 fps, for 30 minutes repeat, 30x playback http://192.168.0.117/start?framesize=VGA&length=1800&interval=500&quality=10&repeat=300&speed=30&gray=0 // UXGA 1 sec per frame, for 30 minutes repeat, 30x playback http://192.168.0.117/start?framesize=UXGA&length=1800&interval=1000&quality=10&repeat=100&speed=30&gray=0 // UXGA 2 fps for 30 minutes repeat, 15x playback http://192.168.0.117/start?framesize=UXGA&length=1800&interval=500&quality=10&repeat=100&speed=30&gray=0 // CIF 20 fps second for 30 minutes repeat http://192.168.0.117/start?framesize=CIF&length=1800&interval=50&quality=10&repeat=100&speed=1&gray=0 // reboot startup parameters here int record_on_reboot = 1; // set to 1 to record, or 0 to NOT record on reboot int framesize = 6; // vga (10 UXGA, 7 SVGA, 6 VGA, 5 CIF) int repeat = 100; // 100 files int xspeed = 1; // 1x playback speed (realtime is 1) int gray = 0; // not gray int quality = 10; // 10 on the 0..64 scale, or 10..50 subscale - 10 is good, 20 is grainy and smaller files int capture_interval = 100; // 100 ms or 10 frames per second int total_frames = 18000; // 18000 frames = 10 fps * 60 seconds * 30 minutes = half hour int PIRpin = 12; //int ResetPin = 13; // pin pour ouverture de la config //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int new_config = 5; // this system abandoned ! int xlength = total_frames * capture_interval / 1000; int recording = 0; int PIRstatus = 0; int PIRrecording = 0; int ready = 0; #define ESP_getChipId() ((uint32_t)ESP.getEfuseMac()) //#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #include "esp_log.h" #include "esp_camera.h" #include #include #include "SPIFFS.h" // Now support ArduinoJson 6.0.0+ ( tested with v6.14.1 ) #include //https://github.com/bblanchon/ArduinoJson #include #include #include //#include // a changer dans src de ESP_WiFiManager //#include //https://github.com/khoih-prog/ESP_WiFiManager #include // https://github.com/tzapu/WiFiManager //default custom static IP char static_ip[16] = "10.0.1.56"; char static_gw[16] = "10.0.1.1"; char static_sn[16] = "255.255.255.0"; //FtpServer ftpSrv; //set #define FTP_DEBUG in ESP32FtpServer.h to see ftp verbose on serial //#define FTP_DEBUG in ESP32FtpServer.h to see ftp verbose on serial File fsUploadFile; File list_file; File f_log; // Time #include "time.h" // MicroSD #include "driver/sdmmc_host.h" #include "driver/sdmmc_defs.h" #include "sdmmc_cmd.h" #include "esp_vfs_fat.h" #include long current_millis; long last_capture_millis = 0; static esp_err_t cam_err; static esp_err_t card_err; char strftime_buf[64]; int file_number = 0; int internet_connected = 0; // 0 non connecté , 1 mode sta , 2 freewifi, 3 mode AP struct tm timeinfo; time_t now; char localip[20] = {}; char buf_config[513] = {0}; size_t buftaille = 0; char *filename ; char *stream ; int newfile = 0; int frames_so_far = 0; FILE *myfile; long bp; long ap; long bw; long aw; long totalp; long totalw; float avgp; float avgw; int overtime_count = 0; // CAMERA_MODEL_AI_THINKER #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 #define SIOD_GPIO_NUM 26 #define SIOC_GPIO_NUM 27 #define Y9_GPIO_NUM 35 #define Y8_GPIO_NUM 34 #define Y7_GPIO_NUM 39 #define Y6_GPIO_NUM 36 #define Y5_GPIO_NUM 21 #define Y4_GPIO_NUM 19 #define Y3_GPIO_NUM 18 #define Y2_GPIO_NUM 5 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 // GLOBALS #define BUFFSIZE 512 // global variable used by these pieces char str[20]; uint16_t n; uint8_t buf[BUFFSIZE]; static int i = 0; uint8_t temp = 0, temp_last = 0; unsigned long fileposition = 0; uint16_t frame_cnt = 0; uint16_t remnant = 0; uint32_t length = 0; uint32_t startms; uint32_t elapsedms; uint32_t uVideoLen = 0; bool is_header = false; long bigdelta = 0; int other_cpu_active = 0; int skipping = 0; int skipped = 0; int fb_max = 12; camera_fb_t * fb_q[30]; int fb_in = 0; int fb_out = 0; camera_fb_t * fb = NULL; FILE *avifile = NULL; FILE *idxfile = NULL; FILE *nofile = NULL; FILE *configSD = NULL; FILE *ficnofic = NULL; #define AVIOFFSET 240 // AVI main header length unsigned long movi_size = 0; unsigned long jpeg_size = 0; unsigned long idx_offset = 0; uint8_t zero_buf[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t dc_buf[4] = {0x30, 0x30, 0x64, 0x63}; // "00dc" uint8_t avi1_buf[4] = {0x41, 0x56, 0x49, 0x31}; // "AVI1" uint8_t idx1_buf[4] = {0x69, 0x64, 0x78, 0x31}; // "idx1" uint8_t vga_w[2] = {0x80, 0x02}; // 640 uint8_t vga_h[2] = {0xE0, 0x01}; // 480 uint8_t cif_w[2] = {0x90, 0x01}; // 400 uint8_t cif_h[2] = {0x28, 0x01}; // 296 uint8_t svga_w[2] = {0x20, 0x03}; // 800 uint8_t svga_h[2] = {0x58, 0x02}; // 600 uint8_t uxga_w[2] = {0x40, 0x06}; // 1600 uint8_t uxga_h[2] = {0xB0, 0x04}; // 1200 const int avi_header[AVIOFFSET] PROGMEM = { 0x52, 0x49, 0x46, 0x46, 0xD8, 0x01, 0x0E, 0x00, 0x41, 0x56, 0x49, 0x20, 0x4C, 0x49, 0x53, 0x54, 0xD0, 0x00, 0x00, 0x00, 0x68, 0x64, 0x72, 0x6C, 0x61, 0x76, 0x69, 0x68, 0x38, 0x00, 0x00, 0x00, 0xA0, 0x86, 0x01, 0x00, 0x80, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x49, 0x53, 0x54, 0x84, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x6C, 0x73, 0x74, 0x72, 0x68, 0x30, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x73, 0x4D, 0x4A, 0x50, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x66, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x4D, 0x4A, 0x50, 0x47, 0x00, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x46, 0x4F, 0x10, 0x00, 0x00, 0x00, 0x6A, 0x61, 0x6D, 0x65, 0x73, 0x7A, 0x61, 0x68, 0x61, 0x72, 0x79, 0x20, 0x76, 0x36, 0x30, 0x20, 0x4C, 0x49, 0x53, 0x54, 0x00, 0x01, 0x0E, 0x00, 0x6D, 0x6F, 0x76, 0x69, }; #define LED_BUILTIN 2 #define LED_ON HIGH #define LED_OFF LOW // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, or GPIO2/TXD1 of NodeMCU control on-board LED #define PIN_LED LED_BUILTIN ////////////// test horaire //////////////////// void Test_horaires() { // cherche si heure maintenant contenue dans horaires Serial.print ( timeinfo.tm_year) ; if ( timeinfo.tm_year < 110) { // date year deouis 1900 return; } else { Serial.println(F(" test horaire")); int heure = 0 ; int taille_plages = strlen(horaire); if (taille_plages > 3 ) { char buf[3] = "00"; for (int i = 0; i < taille_plages; i++) { buf[0] = horaire[i++]; if (horaire[i] != ',' && horaire[i] != ';' && horaire[i] != ' ') buf[1] = horaire[i++]; else buf[1] = '\0'; buf[2] = '\0'; heure = atoi(buf); time(&now); localtime_r(&now, &timeinfo); //Serial.print(F("heures ")); //Serial.println(heure); if ((int)timeinfo.tm_hour == heure) { //Serial.print(F(" ")); //Serial.println(strftime_buf); heure = 99; break; } } int reste = 58 - (int)timeinfo.tm_min; if (reste > 3 && heure < 90) { //end_avi(); Serial.print(F("Sleep ")); Serial.println(reste); esp_sleep_enable_timer_wakeup(1000000 * 60 * (uint64_t)reste); esp_deep_sleep_start(); delay(1000); } } else Serial.println(F("ok")); } } /// /// SPIFFS/SD config /// bool readFileconfig() { Serial.println(F("/sdcard/config.jsn")); configSD = fopen("/sdcard/config.jsn", "r"); if (configSD == NULL) { Serial.println(F("pas sur la SD")); return false; } //Serial.write(configSD.read()); fseek(configSD, 0, SEEK_END); int bufftaille = ftell(configSD); fseek(configSD, 0, SEEK_SET); while (!feof(configSD)) { fread(buf_config, bufftaille + 1, 1, configSD); } fclose(configSD); Serial.println(buf_config); return true; } void saveFileconfigSD() { configSD = fopen("/sdcard/config.jsn", "w"); if (configSD == NULL) { Serial.println(F("Failed to open /sdcard/config.jsn for writing")); } DynamicJsonDocument json(512); json["devname"] = devname; json["quality"] = quality; json["framesize"] = framesize; json["capture_interval"] = capture_interval; json["repeat"] = repeat; json["xspeed"] = xspeed; json["length"] = xlength; json["gray"] = gray; json["ssid"] = ssid; json["pass"] = pass; json["horaire"] = horaire; serializeJson(json, buf_config); fwrite(buf_config, sizeof(buf_config), 1, configSD); fclose(configSD); } bool loadSPIFFSConfigFile(void) { //clean FS, for testing //SPIFFS.format(); if (SPIFFS.begin()) { //read configuration from FS json Serial.println(F("Mounting FS...")); if (readFileconfig() == true) { //Serial.print(F("flash config ")); //Serial.println(buf_config); File configFile = SPIFFS.open("/config.jsn", "w"); configFile.print(buf_config); configFile.flush(); configFile.close(); delay(100); } if (SPIFFS.exists("/config.jsn")) { Serial.println(F("Reading config file")); File configFile = SPIFFS.open("/config.jsn", "r"); if (configFile) { Serial.print(F("Opened config file, size = ")); size_t configFileSize = configFile.size(); Serial.println(configFileSize); // Allocate a buffer to store contents of the file. std::unique_ptr buf(new char[configFileSize + 1]); configFile.readBytes(buf.get(), configFileSize); Serial.print(F("\nJSON parseObject() result : ")); DynamicJsonDocument json(512); auto deserializeError = deserializeJson(json, buf.get(), configFileSize); if ( deserializeError ) { Serial.println(F("failed")); return false; } else { if (json["framesize"]) framesize = json["framesize"] | framesize; if (json["blynk_server"]) repeat = json["repeat"] | repeat; if (json["xspeed"]) xspeed = json["blynk_port"] | xspeed; if (json["gray"]) gray = json["gray"] | gray; if (json["quality"]) quality = json["quality"] | quality ; if (json["capture_interval"]) capture_interval = json["capture_interval"] | capture_interval; if (json["length"]) xlength = json["length"] | xlength; if (json["ssid"]) strlcpy (ssid , json["ssid"], sizeof(ssid)); else strlcpy (ssid , ssid_ap, sizeof(ssid)); if (json["pass"]) strlcpy (pass , json["pass"], sizeof(pass)); else strlcpy (pass , pass_ap, sizeof(pass)); if (json["devname"]) strlcpy (devname , json["devname"], sizeof(devname)); else strlcpy (devname , devname_conf, sizeof(devname)); if (json["horaire"]) strlcpy (horaire , json["horaire"], sizeof(horaire)); else strlcpy (horaire , conf_horaire, sizeof(horaire)); } //serializeJson(json, Serial); Serial.println(F("json :")); serializeJsonPretty(json, Serial); configFile.close(); Serial.println(F("OK")); total_frames = xlength * 1000 / capture_interval; } } } else { Serial.println(F("failed to mount FS")); return false; } return true; } //// //// //// void heartBeatPrint(void) { static int num = 1; if (WiFi.status() == WL_CONNECTED) Serial.print(F("H")); // H means connected to WiFi else Serial.print(F("F")); // F means not connected to WiFi if (num == 80) { Serial.println(); num = 1; } else if (num++ % 10 == 0) { Serial.print(F(" ")); } } void toggleLED() { //toggle state digitalWrite(PIN_LED, !digitalRead(PIN_LED)); } void check_status() { static ulong checkstatus_timeout = 0; static ulong LEDstatus_timeout = 0; static ulong currentMillis; #define HEARTBEAT_INTERVAL 10000L #define LED_INTERVAL 2000L currentMillis = millis(); if ((currentMillis > LEDstatus_timeout) || (LEDstatus_timeout == 0)) { // Toggle LED at LED_INTERVAL = 2s toggleLED(); LEDstatus_timeout = currentMillis + LED_INTERVAL; } // Print hearbeat every HEARTBEAT_INTERVAL (10) seconds. if ((currentMillis > checkstatus_timeout) || (checkstatus_timeout == 0)) { heartBeatPrint(); checkstatus_timeout = currentMillis + HEARTBEAT_INTERVAL; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // AviWriterTask runs on cpu 1 to write the avi file // TaskHandle_t CameraTask, AviWriterTask; SemaphoreHandle_t baton; int counter = 0; void codeForAviWriterTask( void * parameter ) { for (;;) { if (ready) { make_avi(); } delay(1); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // CameraTask runs on cpu 0 to take pictures and drop them in a queue // void codeForCameraTask( void * parameter ) { for (;;) { if (other_cpu_active == 1 ) { current_millis = millis(); if (current_millis - last_capture_millis > capture_interval) { last_capture_millis = millis(); xSemaphoreTake( baton, portMAX_DELAY ); if ( ( (fb_in + fb_max - fb_out) % fb_max) + 1 == fb_max ) { xSemaphoreGive( baton ); Serial.print(F(" Queue Full, Skipping ..")); // the queue is full skipped++; skipping = 1; } if (skipping > 0 ) { if (!BlinkWithWrite) { digitalWrite(33, LOW); } if (skipping % 2 == 0) { // skip every other frame until queue is cleared frames_so_far = frames_so_far + 1; frame_cnt++; fb_in = (fb_in + 1) % fb_max; bp = millis(); fb_q[fb_in] = esp_camera_fb_get(); totalp = totalp - bp + millis(); } else { Serial.print(((fb_in + fb_max - fb_out) % fb_max)); // skip an extra frame to empty the queue skipped++; } skipping = skipping + 1; if (((fb_in + fb_max - fb_out) % fb_max) == 0 ) { skipping = 0; Serial.println(" Queue cleared. "); delay(100); } xSemaphoreGive( baton ); } else { skipping = 0; frames_so_far = frames_so_far + 1; frame_cnt++; fb_in = (fb_in + 1) % fb_max; bp = millis(); fb_q[fb_in] = esp_camera_fb_get(); totalp = totalp - bp + millis(); xSemaphoreGive( baton ); } } } delay(1); } } // // Writes an uint32_t in Big Endian at current file position // static void inline print_quartet(unsigned long i, FILE * fd) { uint8_t x[1]; x[0] = i % 0x100; size_t i1_err = fwrite(x , 1, 1, fd); i = i >> 8; x[0] = i % 0x100; size_t i2_err = fwrite(x , 1, 1, fd); i = i >> 8; x[0] = i % 0x100; size_t i3_err = fwrite(x , 1, 1, fd); i = i >> 8; x[0] = i % 0x100; size_t i4_err = fwrite(x , 1, 1, fd); } WebServer server(80); void startCameraServer(); //httpd_handle_t camera_httpd = NULL; char the_page[3000]; WiFiEventId_t eventID; #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // setup() runs on cpu 1 // void setup() { //WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector // creates other problems Serial.begin(115200); Serial.setDebugOutput(true); Serial.println(" "); Serial.printf("ESP-CAM Video Recorder %s\n", vernum); // SD camera init card_err = init_sdcard(); if (card_err != true) { Serial.printf("init sd error 0x%x", card_err); major_fail(); } loadSPIFFSConfigFile(); //Serial.printf(" http://%s.local - to access the camera\n", devname); pinMode(33, OUTPUT); // little red led on back of chip digitalWrite(33, LOW); // turn on the red LED on the back of chip if (!psramFound()) { Serial.println("paraFound wrong - major fail"); //major_fail(); } init_wifi(); Test_horaires(); startCameraServer(); Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024)); Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024)); digitalWrite(33, HIGH); // red light turns off when setup is complete baton = xSemaphoreCreateMutex(); xTaskCreatePinnedToCore( codeForCameraTask, "CameraTask", 10000, NULL, 1, &CameraTask, 0); delay(50); xTaskCreatePinnedToCore( codeForAviWriterTask, "AviWriterTask", 10000, NULL, 2, &AviWriterTask, 1); delay(50); recording = 0; // we are NOT recording config_camera(); pinMode(4, OUTPUT); // using 1 bit mode, shut off the Blinding Disk-Active Light digitalWrite(4, LOW); pinMode(PIRpin, INPUT_PULLDOWN); // or PULLDOWN for active high newfile = 0; // no file is open // don't fiddle with this! recording = record_on_reboot; ready = 1; Serial.print(F("Camera Ready! Use http://")); Serial.print(WiFi.localIP()); Serial.print(F(": ")); Serial.print(localip); Serial.print(F(": ")); Serial.println(WiFi.softAPIP()); Serial.println(F(" to connect")); } bool init_wifi() { if (internet_connected < 3) { // que si pas deja en AP WiFi.disconnect(true); WiFi.mode(WIFI_STA); WiFi.setHostname(devname); WiFi.begin(ssid, pass); delay(1000); while (WiFi.status() != WL_CONNECTED ) { delay(1000); Serial.print(F(".")); if (connAttempts == 10) { Serial.println(F("tentative FreeWifi ntp")); // juste pour ntp WiFi.begin("FreeWifi"); delay(2000); if (WiFi.SSID() == (String)"FreeWifi") { Serial.println(F("Temps FreeWifi")); internet_connected = 2; } delay(1000); } if(connAttempts > 13) { // laisse tomber, passage AP internet_connected = 3; break; } connAttempts++; } } if (internet_connected == 0) internet_connected = 1; if (internet_connected < 3) { Serial.println(WiFi.SSID()); char pool[16] = ""; if ( internet_connected == 2) { sprintf(pool, "%s", WiFi.gatewayIP().toString().c_str()); } else { sprintf(pool, "pool.ntp.org"); } Serial.print(F("Attente ntp de ")); Serial.print(pool); configTime(0, 0, pool); setenv("TZ", TIMEZONE, 1); // Paris time zone from #define at top tzset(); time_t now ; timeinfo = { 0 }; int retry = 0; const int retry_count = 10; delay(2000); time(&now); localtime_r(&now, &timeinfo); Serial.print(F(" : ")); Serial.println(ctime(&now)); Serial.print(F(" - ")); while (!time(nullptr) && ++retry < retry_count) { Serial.printf("... (%d/%d) -- %d\n", retry, retry_count, timeinfo.tm_year); delay(1000); time(&now); localtime_r(&now, &timeinfo); Serial.println(ctime(&now)); Serial.print(F(" ok ntp ")); } sprintf(localip, "%s", WiFi.localIP().toString().c_str()); } if (internet_connected >= 2) { Serial.print(F("\nPassage en Point d acces...")); WiFi.disconnect(); delay(500); WiFi.mode(WIFI_AP); delay(1000); WiFi.softAP(ssid_ap, pass_ap, canal_wifi, invisible, max_connexion); delay(1000); Serial.print(F("ouverture paswd de ")); Serial.print(ssid_ap); Serial.print(F(" ")); Serial.println(pass_ap); internet_connected = 3; delay(1000); sprintf(localip, "%s", WiFi.softAPIP().toString().c_str()); Serial.println(WiFi.softAPIP()); Serial.println(WiFi.localIP()); if (!MDNS.begin(devname)) { Serial.println(F("Error MDNS !")); } else { Serial.printf("mDNS started '%s'\n", devname); } } Serial.println(localip); return true; } bool init_sdcard() { esp_err_t ret = ESP_FAIL; sdmmc_host_t host = SDMMC_HOST_DEFAULT(); host.flags = SDMMC_HOST_FLAG_1BIT; // using 1 bit mode host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); slot_config.width = 1; // using 1 bit mode //Serial.print("Slot config width should be 4 width: "); Serial.println(slot_config.width); esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed = false, .max_files = 5, }; //pinMode(4, OUTPUT); // using 1 bit mode, shut off the Blinding Disk-Active Light //digitalWrite(4, LOW); sdmmc_card_t *card; //Serial.println(F("Mounting SD card...")); ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); if (ret == ESP_OK) { Serial.println(F("SD mount ok!")); } else { Serial.printf("err mount SD card VFAT Error: %s", esp_err_to_name(ret)); major_fail(); return false; } sdmmc_card_print_info(stdout, card); Serial.print("SD_MMC Begin: "); Serial.println(SD_MMC.begin()); // required by ftp system ?? listDirSD(SD_MMC, "/", 0); return true; } void listDirSD(fs::FS & fs, const char * dirname, uint8_t levels) { Serial.printf("Listing directory: %s\n", dirname); File root = fs.open(dirname); if (!root) { Serial.println(F("err open directory")); return; } if (!root.isDirectory()) { Serial.println(F("Not a dir")); return; } File file = root.openNextFile(); while (file) { if (file.isDirectory()) { Serial.print(F(" DIR : ")); Serial.println(file.name()); if (levels) { listDirSD(fs, file.name(), levels - 1); } } else { Serial.print(F(" FILE: ")); Serial.print(file.name()); Serial.print(F(" SIZE: ")); Serial.println(file.size()); } file = root.openNextFile(); } return; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Make the avi move in 4 pieces // // make_avi() called in every loop, which calls below, depending on conditions // start_avi() - open the file and write headers // another_pic_avi() - write one more frame of movie // end_avi() - write the final parameters and close the file void make_avi( ) { PIRstatus = digitalRead(PIRpin); if (PIRstatus == 1) { if (PIRrecording == 1) { // keep recording for 15 more seconds if ( (millis() - startms) > (total_frames * capture_interval - 5000) ) { total_frames = total_frames + 10000 / capture_interval ; Serial.println(F("Add 10 s")); } } else { if ( recording == 0 && newfile == 0) { //start a pir recording with current parameters, except no repeat and 15 seconds Serial.println(F("Start a PIR")); PIRrecording = 1; repeat = 0; total_frames = 15000 / capture_interval; xlength = total_frames * capture_interval / 1000; recording = 1; } } } // we are recording, but no file is open if (newfile == 0 && recording == 1) { // open the file digitalWrite(33, HIGH); newfile = 1; start_avi(); } else { // we have a file open, but not recording if (newfile == 1 && recording == 0) { // got command to close file digitalWrite(33, LOW); end_avi(); Serial.println(F("ok capture command")); frames_so_far = total_frames; newfile = 0; // file is closed recording = 0; // DO NOT start another recording PIRrecording = 0; } else { if (newfile == 1 && recording == 1) { // regular recording if (frames_so_far >= total_frames) { // we are done the recording Serial.println(F("ok capture for total frames!")); digitalWrite(33, LOW); // close the file end_avi(); frames_so_far = 0; newfile = 0; // file is closed if (repeat > 0) { recording = 1; // start another recording repeat = repeat - 1; } else { recording = 0; PIRrecording = 0; } } else if ((millis() - startms) > (total_frames * capture_interval)) { // time is up, even though we have not done all the frames Serial.println (F(" ")); Serial.println(F("Done capture for time")); Serial.print(F("Time Elapsed: ")); Serial.print(millis() - startms); Serial.print(F(" Frames: ")); Serial.println(frame_cnt); Serial.print(F("Config: ")); Serial.print(total_frames * capture_interval ) ; Serial.print(F(" (")); Serial.print(total_frames); Serial.print(F(" x ")); Serial.print(capture_interval); Serial.println(F(")")); digitalWrite(33, LOW); // close the file end_avi(); frames_so_far = 0; newfile = 0; // file is closed if (repeat > 0) { recording = 1; // start another recording repeat = repeat - 1; } else { recording = 0; PIRrecording = 0; } //// Sleep si necessaire si personne n'est connecté entre 2 fichiers if (sleep_mode == true && WiFi.softAPgetStationNum() < 1) { Serial.print(F("Sleep ")); Serial.println(sleep_duree); esp_sleep_enable_timer_wakeup(1000000 * 60 * (uint64_t)sleep_duree); esp_deep_sleep_start(); delay(1000); } } else { // regular another_save_avi(); } } } } } static esp_err_t config_camera() { camera_config_t config; //Serial.println("config camera"); if (new_config == 5) { config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sscb_sda = SIOD_GPIO_NUM; config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; config.frame_size = FRAMESIZE_UXGA; fb_max = 7; // for vga and uxga config.jpeg_quality = 8; config.fb_count = fb_max + 1; // camera init cam_err = esp_camera_init(&config); if (cam_err != ESP_OK) { Serial.printf("Camera init error 0x%x", cam_err); major_fail(); } new_config = 2; } delay(100); sensor_t * ss = esp_camera_sensor_get(); ss->set_quality(ss, quality); ss->set_framesize(ss, (framesize_t)framesize); if (gray == 1) { ss->set_special_effect(ss, 2); // 0 regular, 2 grayscale } else { ss->set_special_effect(ss, 0); // 0 regular, 2 grayscale } for (int j = 0; j < 3; j++) { do_fb(); // start the camera ... warm it up delay(1); } } void nofichiersave(fs::FS & fs, const char * nomfic) { File ficnofic = fs.open(nomfic, FILE_WRITE); if (!ficnofic) { Serial.println(F("err open /nofic.jsn writing")); } DynamicJsonDocument json(32); json["nofichier"] = numero_fichier; serializeJson(json, ficnofic); } int nofichierread(fs::FS & fs, const char * nomfic) { File ficnofic = fs.open(nomfic, FILE_READ); if (!ficnofic) { Serial.println(F("err open /nofic.jsn reading")); } DynamicJsonDocument json(32); DeserializationError error = deserializeJson(json, ficnofic); if (error) { Serial.println(F("err json /nofic.jsn")); ficnofic.println(); return (numero_fichier); } numero_fichier = json["nofichier"]; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // start_avi - open the files and write in headers // static esp_err_t start_avi() { Serial.println(F("Starting an avi ")); config_camera(); if ( (int)timeinfo.tm_year > 110 ) { time(&now); localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%F_%H.%M.%S", &timeinfo); } else { nofichierread(SD_MMC, "/nofic.jsn"); sprintf(strftime_buf, "%d", numero_fichier); } char fname[100]; if (framesize == 6) { sprintf(fname, "/sdcard/%s_%s_vga_Q%d_I%d_L%d_S%d.avi", devname, strftime_buf, quality, capture_interval, xlength, xspeed); } else if (framesize == 7) { sprintf(fname, "/sdcard/%s_%s_svga_Q%d_I%d_L%d_S%d.avi", devname, strftime_buf, quality, capture_interval, xlength, xspeed); } else if (framesize == 10) { sprintf(fname, "/sdcard/%s_%s_uxga_Q%d_I%d_L%d_S%d.avi", devname, strftime_buf, quality, capture_interval, xlength, xspeed); } else if (framesize == 5) { sprintf(fname, "/sdcard/%s_%s_cif_Q%d_I%d_L%d_S%d.avi", devname, strftime_buf, quality, capture_interval, xlength, xspeed); } else { Serial.println("Wrong framesize"); sprintf(fname, "/sdcard/%s_%s_xxx_Q%d_I%d_L%d_S%d.avi", devname, strftime_buf, quality, capture_interval, xlength, xspeed); } Serial.print(F("\nFile name will be >")); Serial.print(fname); Serial.println("<"); avifile = fopen(fname, "w"); idxfile = fopen("/sdcard/idx.tmp", "w"); if (avifile != NULL) { //Serial.printf("File open: %s\n", fname); } else { Serial.println("err open file"); major_fail(); } if (idxfile != NULL) { //Serial.printf("File open: %s\n", "/sdcard/idx.tmp"); } else { Serial.println(F("err open file")); major_fail(); } for ( i = 0; i < AVIOFFSET; i++) { char ch = pgm_read_byte(&avi_header[i]); buf[i] = ch; } size_t err = fwrite(buf, 1, AVIOFFSET, avifile); if (framesize == 6) { fseek(avifile, 0x40, SEEK_SET); err = fwrite(vga_w, 1, 2, avifile); fseek(avifile, 0xA8, SEEK_SET); err = fwrite(vga_w, 1, 2, avifile); fseek(avifile, 0x44, SEEK_SET); err = fwrite(vga_h, 1, 2, avifile); fseek(avifile, 0xAC, SEEK_SET); err = fwrite(vga_h, 1, 2, avifile); } else if (framesize == 10) { fseek(avifile, 0x40, SEEK_SET); err = fwrite(uxga_w, 1, 2, avifile); fseek(avifile, 0xA8, SEEK_SET); err = fwrite(uxga_w, 1, 2, avifile); fseek(avifile, 0x44, SEEK_SET); err = fwrite(uxga_h, 1, 2, avifile); fseek(avifile, 0xAC, SEEK_SET); err = fwrite(uxga_h, 1, 2, avifile); } else if (framesize == 7) { fseek(avifile, 0x40, SEEK_SET); err = fwrite(svga_w, 1, 2, avifile); fseek(avifile, 0xA8, SEEK_SET); err = fwrite(svga_w, 1, 2, avifile); fseek(avifile, 0x44, SEEK_SET); err = fwrite(svga_h, 1, 2, avifile); fseek(avifile, 0xAC, SEEK_SET); err = fwrite(svga_h, 1, 2, avifile); } else if (framesize == 5) { fseek(avifile, 0x40, SEEK_SET); err = fwrite(cif_w, 1, 2, avifile); fseek(avifile, 0xA8, SEEK_SET); err = fwrite(cif_w, 1, 2, avifile); fseek(avifile, 0x44, SEEK_SET); err = fwrite(cif_h, 1, 2, avifile); fseek(avifile, 0xAC, SEEK_SET); err = fwrite(cif_h, 1, 2, avifile); } fseek(avifile, AVIOFFSET, SEEK_SET); Serial.print(F("\nRecording ")); Serial.print(total_frames); Serial.println(F(" video frames ...\n")); startms = millis(); bigdelta = millis(); totalp = 0; totalw = 0; overtime_count = 0; jpeg_size = 0; movi_size = 0; uVideoLen = 0; idx_offset = 4; frame_cnt = 0; frames_so_far = 0; skipping = 0; skipped = 0; newfile = 1; other_cpu_active = 1; } // end of start avi //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // another_save_avi runs on cpu 1, saves another frame to the avi file // // the "baton" semaphore makes sure that only one cpu is using the camera subsystem at a time // static esp_err_t another_save_avi() { xSemaphoreTake( baton, portMAX_DELAY ); if (fb_in == fb_out) { // nothing to do xSemaphoreGive( baton ); } else { fb_out = (fb_out + 1) % fb_max; int fblen; fblen = fb_q[fb_out]->len; //xSemaphoreGive( baton ); if (BlinkWithWrite) { digitalWrite(33, LOW); } jpeg_size = fblen; movi_size += jpeg_size; uVideoLen += jpeg_size; bw = millis(); size_t dc_err = fwrite(dc_buf, 1, 4, avifile); size_t ze_err = fwrite(zero_buf, 1, 4, avifile); bw = millis(); size_t err = fwrite(fb_q[fb_out]->buf, 1, fb_q[fb_out]->len, avifile); if (err == 0 ) { Serial.println(F("Err avi write")); major_fail(); } totalw = totalw + millis() - bw; //xSemaphoreTake( baton, portMAX_DELAY ); esp_camera_fb_return(fb_q[fb_out]); // release that buffer back to the camera system xSemaphoreGive( baton ); remnant = (4 - (jpeg_size & 0x00000003)) & 0x00000003; print_quartet(idx_offset, idxfile); print_quartet(jpeg_size, idxfile); idx_offset = idx_offset + jpeg_size + remnant + 8; jpeg_size = jpeg_size + remnant; movi_size = movi_size + remnant; if (remnant > 0) { size_t rem_err = fwrite(zero_buf, 1, remnant, avifile); } fileposition = ftell (avifile); // Here, we are at end of chunk (after padding) fseek(avifile, fileposition - jpeg_size - 4, SEEK_SET); // Here we are the the 4-bytes blank placeholder print_quartet(jpeg_size, avifile); // Overwrite placeholder with actual frame size (without padding) fileposition = ftell (avifile); fseek(avifile, fileposition + 6, SEEK_SET); // Here is the FOURCC "JFIF" (JPEG header) // Overwrite "JFIF" (still images) with more appropriate "AVI1" size_t av_err = fwrite(avi1_buf, 1, 4, avifile); fileposition = ftell (avifile); fseek(avifile, fileposition + jpeg_size - 10 , SEEK_SET); //Serial.println("Write done"); //41 totalw = totalw + millis() - bw; //if (((fb_in + fb_max - fb_out) % fb_max) > 0 ) { // Serial.print(((fb_in + fb_max - fb_out) % fb_max)); Serial.print(F(" "); //} digitalWrite(33, HIGH); } } // end of another_pic_avi //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // end_avi runs on cpu 1, empties the queue of frames, writes the index, and closes the files // static esp_err_t end_avi() { unsigned long current_end = 0; other_cpu_active = 0 ; // shuts down the picture taking program //Serial.print(" Write Q: "); Serial.print((fb_in + fb_max - fb_out) % fb_max); Serial.print(" in/out "); Serial.print(fb_in); Serial.print(" / "); Serial.println(fb_out); for (int i = 0; i < fb_max; i++) { // clear the queue another_save_avi(); } //Serial.print(" Write Q: "); Serial.print((fb_in + fb_max - fb_out) % fb_max); Serial.print(" in/out "); Serial.print(fb_in); Serial.print(" / "); Serial.println(fb_out); current_end = ftell (avifile); Serial.println(F("End of avi - close files")); elapsedms = millis() - startms; float fRealFPS = (1000.0f * (float)frame_cnt) / ((float)elapsedms) * xspeed; float fmicroseconds_per_frame = 1000000.0f / fRealFPS; uint8_t iAttainedFPS = round(fRealFPS); uint32_t us_per_frame = round(fmicroseconds_per_frame); //Modify the MJPEG header from the beginning of the file, overwriting various placeholders fseek(avifile, 4 , SEEK_SET); print_quartet(movi_size + 240 + 16 * frame_cnt + 8 * frame_cnt, avifile); fseek(avifile, 0x20 , SEEK_SET); print_quartet(us_per_frame, avifile); unsigned long max_bytes_per_sec = movi_size * iAttainedFPS / frame_cnt; fseek(avifile, 0x24 , SEEK_SET); print_quartet(max_bytes_per_sec, avifile); fseek(avifile, 0x30 , SEEK_SET); print_quartet(frame_cnt, avifile); fseek(avifile, 0x8c , SEEK_SET); print_quartet(frame_cnt, avifile); fseek(avifile, 0x84 , SEEK_SET); print_quartet((int)iAttainedFPS, avifile); fseek(avifile, 0xe8 , SEEK_SET); print_quartet(movi_size + frame_cnt * 8 + 4, avifile); Serial.println(F("\n*** Video ok,saved ***\n")); Serial.print(F("Recorded ")); Serial.print(elapsedms / 1000); Serial.print(F("s in ")); Serial.print(frame_cnt); Serial.print(F(" frames\nFile size is ")); Serial.print(movi_size + 12 * frame_cnt + 4); Serial.print(F(" bytes\nActual FPS is ")); Serial.print(fRealFPS, 2); Serial.print(F("\nMax data rate is ")); Serial.print(max_bytes_per_sec); Serial.print(F(" byte/s\nFrame duration is ")); Serial.print(us_per_frame); Serial.println(F(" us")); Serial.print(F("Average frame length is ")); Serial.print(uVideoLen / frame_cnt); Serial.println(F(" bytes")); Serial.print(F("Average picture time (ms) ")); Serial.println( totalp / frame_cnt ); Serial.print(F("Average write time (ms) ")); Serial.println( totalw / frame_cnt ); Serial.print(F("Frames Skipped % ")); Serial.println( 100.0 * skipped / total_frames, 1 ); Serial.println(F("Writing the index")); fseek(avifile, current_end, SEEK_SET); fclose(idxfile); size_t i1_err = fwrite(idx1_buf, 1, 4, avifile); print_quartet(frame_cnt * 16, avifile); idxfile = fopen("/sdcard/idx.tmp", "r"); if (idxfile != NULL) { //Serial.printf("File open: %s\n", "/sdcard/idx.tmp"); } else { Serial.println(F("err open file")); //major_fail(); } char * AteBytes; AteBytes = (char*) malloc (8); for (int i = 0; i < frame_cnt; i++) { size_t res = fread ( AteBytes, 1, 8, idxfile); size_t i1_err = fwrite(dc_buf, 1, 4, avifile); size_t i2_err = fwrite(zero_buf, 1, 4, avifile); size_t i3_err = fwrite(AteBytes, 1, 8, avifile); } free(AteBytes); fclose(idxfile); fclose(avifile); int xx = remove("/sdcard/idx.tmp"); Serial.println(F("---")); // incremente le numero_fichier if ( (int)timeinfo.tm_year < 110) { numero_fichier++; nofichiersave(SD_MMC, "/nofic.jsn"); sprintf(strftime_buf, "%d", numero_fichier); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // do_fb - just takes a picture and discards it // static esp_err_t do_fb() { xSemaphoreTake( baton, portMAX_DELAY ); camera_fb_t * fb = esp_camera_fb_get(); Serial.print(F("Pic, len=")); Serial.println(fb->len); esp_camera_fb_return(fb); xSemaphoreGive( baton ); } // // if we have no camera, or sd card, then flash rear led on and off to warn the human SOS - SOS // void major_fail() { for (int i = 0; i < 10; i++) { // 10 loops or about 100 seconds then reboot digitalWrite(33, LOW); delay(150); digitalWrite(33, HIGH); delay(150); digitalWrite(33, LOW); delay(150); digitalWrite(33, HIGH); delay(150); digitalWrite(33, LOW); delay(150); digitalWrite(33, HIGH); delay(150); delay(1000); digitalWrite(33, LOW); delay(500); digitalWrite(33, HIGH); delay(500); digitalWrite(33, LOW); delay(500); digitalWrite(33, HIGH); delay(500); digitalWrite(33, LOW); delay(500); digitalWrite(33, HIGH); delay(500); delay(1000); Serial.print(F("Major Fail ")); Serial.print(i); Serial.print(F(" / ")); Serial.println(10); } ESP.restart(); } void do_time() { if (WiFi.status() != WL_CONNECTED) { Serial.println(F("** WiFi reconnect **")); WiFi.reconnect(); delay(5000); if (WiFi.status() != WL_CONNECTED) { Serial.println(F("** WiFi rerestart **")); init_wifi(); } } Test_horaires(); } //////////////////////////////////////////////////////////////////////////////////// // // some globals for the loop() // long wakeup; long last_wakeup = 0; void loop() { wakeup = millis(); if (wakeup - last_wakeup > (14 * 60 * 1000) ) { // 14 minutes last_wakeup = millis(); do_time(); } //ftpSrv.handleFTP(); server.handleClient(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // // save photo stuff not currently used static esp_err_t save_photo() { Serial.print(F("Taking picture: /capture.jpg")); camera_fb_t *fb = esp_camera_fb_get(); char *filename = (char*)malloc(21 + sizeof(int)); sprintf(filename, "/sdcard/capture.jpg"); Serial.println(filename); FILE *file = fopen(filename, "w"); if (file != NULL) { size_t err = fwrite(fb->buf, 1, fb->len, file); Serial.printf("File saved: %s\n", filename); } else { Serial.println(F("err open file")); } fclose(file); esp_camera_fb_return(fb); free(filename); } void capture_handler() { save_photo(); time(&now); const char *strdate = ctime(&now); const char msg[] PROGMEM = R"rawliteral( ESP32 Photo

Capture

Date: %s


http://%s/

)rawliteral"; sprintf(the_page, msg, strdate, localip, localip); server.send(200, "text/html", the_page); delay(1000); // pour eviter les prise en rafale } static esp_err_t save_photo_numbered() { file_number++; Serial.print(F("Taking picture: ")); Serial.print(file_number); camera_fb_t *fb = esp_camera_fb_get(); char *filename = (char*)malloc(21 + sizeof(int)); sprintf(filename, "/sdcard/capture_%d.jpg", file_number); Serial.println(filename); FILE *file = fopen(filename, "w"); if (file != NULL) { size_t err = fwrite(fb->buf, 1, fb->len, file); Serial.printf("File saved: %s\n", filename); } else { Serial.println(F("err open file")); } fclose(file); esp_camera_fb_return(fb); free(filename); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // // void do_start(char *the_message) { Serial.print(F("do_start ")); Serial.println(the_message); const char msg[] PROGMEM = R"rawliteral( %s ESP32-CAM start

%s
ESP32-CAM Video Recorder %s


Message is %s


Recording = %d (1 is active)
Capture Interval = %d ms
Length = %d seconds
Quality = %d (10 best to 50 worst)
Framesize = %d (10 UXGA, 7 SVGA, 6 VGA, 5 CIF)
Repeat = %d
Speed = %d
Gray = %d



http://%s/ retour au status

)rawliteral"; sprintf(the_page, msg, devname, devname, vernum, the_message, recording, capture_interval, capture_interval * total_frames / 1000, quality, framesize, repeat, xspeed, gray, localip, localip); //Serial.println(strlen(msg)); } bool start_handler() { char buf[80]; size_t buf_len; char new_res[20]; if (recording == 1) { server.send(200, "text/html", "You must Stop recording, before starting a new one. Start over ..."); return false; } else { //recording = 1; Serial.println(F("start recording")); sensor_t * s = esp_camera_sensor_get(); int new_interval = capture_interval; int new_length = capture_interval * total_frames; int new_framesize = s->status.framesize; int new_quality = s->status.quality; int new_repeat = 0; int new_xspeed = 1; int new_xlength = 3; int new_gray = 0; /* Serial.println(""); Serial.println("Current Parameters :"); Serial.print(" Capture Interval = "); Serial.print(capture_interval); Serial.println(" ms"); Serial.print(" Length = "); Serial.print(capture_interval * total_frames / 1000); Serial.println(" s"); Serial.print(" Quality = "); Serial.println(new_quality); Serial.print(" Framesize = "); Serial.println(new_framesize); Serial.print(" Repeat = "); Serial.println(repeat); Serial.print(" Speed = "); Serial.println(xspeed); */ for (int i = 0; i < server.args(); i++) { Serial.print(server.argName(i)); Serial.print(F(" ")); Serial.println(server.arg(i)); int len = server.arg(i).length() + 1; char retour[20] = {0}; server.arg(i).toCharArray(retour, len); if (server.argName(i) == "length") { int x = atoi(retour); if (x >= 1 && x <= 3600 * 24 ) { // 1 sec to 24 hours new_length = x; } } else if (server.argName(i) == "repeat") { int x = atoi(retour); if (x >= 0 ) { new_repeat = x; } } else if (server.argName(i) == "framesize") { if (strcmp(retour, "UXGA") == 0) { new_framesize = 10; } else if (strcmp(retour, "SVGA") == 0) { new_framesize = 7; } else if (strcmp(retour, "VGA") == 0) { new_framesize = 6; } else if (strcmp(retour, "CIF") == 0) { new_framesize = 5; } else { Serial.println(F("Only UXGA, SVGA, VGA, and CIF are valid!")); } } else if (server.argName(i) == "quality") { int x = atoi(retour); if (x >= 10 && x <= 50) { // MINIMUM QUALITY 10 to save memory new_quality = x; } } else if (server.argName(i) == "speed") { int x = atoi(retour); if (x >= 1 && x <= 100) { new_xspeed = x; } } else if (server.argName(i) == "interval") { int x = atoi(retour); if (x <= 1 ) { new_interval = capture_interval; } else { new_interval = x; } } else if (server.argName(i) == "sleep") { // en minute sleep_mode = true; if (len > 1) sleep_duree = atoi(retour); } else if (server.argName(i) == "gray") { int x = atoi(retour); if (x == 1 ) { new_gray = x; } else { new_gray = 0; } } else if (server.argName(i) == "ssid") { strncpy(ssid, retour, len); } else if (server.argName(i) == "pass") { strncpy(pass, retour, len); } else if (server.argName(i) == "horaire") { strncpy(horaire, retour, len); } } framesize = new_framesize; capture_interval = new_interval; xlength = new_length; total_frames = new_length * 1000 / capture_interval; repeat = new_repeat; quality = new_quality; xspeed = new_xspeed; gray = new_gray; do_start("Starting a new AVI"); server.send(200, "text/html", the_page); recording = 1; return true; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // void do_stop(char *the_message) { Serial.print(F("do_stop ")); Serial.println(the_message); const char msg[] PROGMEM = R"rawliteral( %s ESP32-CAM stop

%s
ESP32-CAM configurations %s


Message is %s


http://%s/ retour au status

VGA 2 fps for 30 minutes repeat, 1x playback

VGA 2 fps, for 30 minutes repeat, 30x playback

UXGA 1 sec per frame, for 30 minutes repeat, 30x playback

UXGA 2 fps for 30 minutes repeat, 15x playback

CIF 20 fps second for 30 minutes repeat

UXGA 10 second for 30 minutes repeat

UXGA 10 min for 30 minutes repeati sleep


Capture Interval en ms
Length en seconds
Quality = 10 meilleurs a 50 pire
Framesize = UXGA SVGA VGA CIF
Repeat = nombre de fois
Speed = vitesse
Gray = 1 ou 0

mis a jour dela config initiale par

saveconfig

Fichier sur la microSD /config.jsn
%s )rawliteral"; //ex: {"devname":"esp32-cam","framesize":6,"repeat":100,"xspeed":1,"gray":0,"quality":10,"capture_interval":10000,"length":180,"ssid":"SNHACK","pass":"1234567890","horaire":"10 11,12,13,14,15,16,17,18,19,20"} sprintf(the_page, msg, devname, devname, vernum, the_message, localip, localip, localip, localip, localip, localip, localip, localip, localip, localip, buf_config); Serial.println(the_page); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // bool stop_handler() { recording = 0; Serial.println(F("stop recording")); do_stop("Stopping previous recording"); server.send(200, "text/html", the_page); delay(1000); return true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // void do_status(char *the_message) { Serial.print(F("do_status ")); Serial.println(the_message); const char msg[] PROGMEM = R"rawliteral( %s ESP32-CAM index

%s
ESP32-CAM Video Recorder %s
%s


Page de %s


Total SD Space is %d MB, Used SD Space is %d MB
Recording = %d (1 is active)
Frame %d of %d, Skipped %d

Capture Interval = %d ms
Length = %d seconds
Quality = %d (10 best to 50 worst)
Framesize = %d (10 UXGA, 7 SVGA, 6 VGA, 5 CIF)
Repeat = %d
Playback Speed = %d
Gray = %d

Commandes possibles sur cette adresse ip:

http://%s/

http://%s/stop ... configuration et restart
Vous devez faire stop pour modifier la configuration

http://%s/list> list

http://%s/capture> photo

saveconfig

Fichier sur la microSD /config.json ex: {"framesize":6,"repeat":100,"xspeed":1,"gray":0,"quality":10,"capture_interval":10000,"length":180}, cf list
LED flash sur une frame, SOS flash si pas de microSD

)rawliteral"; time(&now); const char *strdate = ctime(&now); //Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024)); //Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024)); int tot = SD_MMC.totalBytes() / (1024 * 1024); int use = SD_MMC.usedBytes() / (1024 * 1024); //Serial.print(strlen(msg)); Serial.print(" "); sprintf(the_page, msg, devname, devname, vernum, strdate, the_message, tot, use, recording, frames_so_far, total_frames, skipped, capture_interval, capture_interval * total_frames / 1000, quality, framesize, repeat, xspeed, gray, localip, localip, localip, localip, localip, localip, localip, localip, localip); //Serial.println(strlen(the_page)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // LIST /// void listDir(fs::FS & fs, const char * dirname, uint8_t levels) { Serial.printf("Listing directory: %s\n", dirname); String output = ""; output += ""; output += " ESP32-CAM list"; output += ""; output += "

Fichiers Tailles

"; File root = fs.open(dirname); if (!root) { Serial.println(F("err open dir")); return; } if (!root.isDirectory()) { Serial.println(F("Not a dir")); return; } File file = root.openNextFile(); while (file) { delay(100); if (file.isDirectory()) { output += "

http://"; output += (String)localip; output += (String)file.name(); output += "

"; output += "\n"; //Serial.println((String)file.name()); if (levels) { listDir(fs, file.name(), levels - 1); } } else { output += "
http://"; output += (String)localip; output += (String)file.name(); output += ""; output += " : "; output += file.size(); //Serial.println((String)file.name()); } file = root.openNextFile(); } output += "
"; output += "
http://"; output += (String)localip; output += " Retour aux status"; output += ""; server.send(200, "text/html", output); //Serial.println(F("fin")); return; } bool listDir_handler() { listDir(SD_MMC, "/", 0); //Serial.println(F("listDir fin")); return true; } /// /// SDCARD /// bool loadFromSdCard(fs::FS & fs, String path) { String dataType = "text/plain"; if (path.endsWith("/")) { do_status("Refresh Status"); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Pragma", "no-cache"); server.sendHeader("Expires", "-1"); server.send(200, "text/html", the_page); return true; } if (path.endsWith(".src")) { path = path.substring(0, path.lastIndexOf(".")); } else if (path.endsWith(".htm")) { dataType = "text/html"; } else if (path.endsWith(".css")) { dataType = "text/css"; } else if (path.endsWith(".js")) { dataType = "application/javascript"; } else if (path.endsWith(".png")) { dataType = "image/png"; } else if (path.endsWith(".gif")) { dataType = "image/gif"; } else if (path.endsWith(".jpg")) { dataType = "image/jpeg"; } else if (path.endsWith(".ico")) { dataType = "image/x-icon"; } else if (path.endsWith(".xml")) { dataType = "text/xml"; } else if (path.endsWith(".pdf")) { dataType = "application/pdf"; } else if (path.endsWith(".zip")) { dataType = "application/zip"; } File dataFile = fs.open(path.c_str()); if (dataFile.isDirectory()) { path += "/index.htm"; dataType = "text/html"; dataFile = fs.open(path.c_str()); } if (!dataFile) { return false; } if (server.hasArg("download")) { dataType = "application/octet-stream"; } if (server.streamFile(dataFile, dataType) != dataFile.size()) { Serial.println(F("Sent less data than expected!")); } dataFile.close(); return true; } /// /// DEFAULT LECTURE SD /// void handleNotFound() { if (loadFromSdCard(SD_MMC, server.uri())) { return; } String message = ""; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i = 0; i < server.args(); i++) { message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; } server.send(404, "text/plain", message); Serial.println(message); } /// /// SPIFFS SAVE config /// bool saveSPIFFSConfigFile(void) { Serial.println(F("Save config")); recording = 0; DynamicJsonDocument json(512); json["devname"] = devname; json["quality"] = quality; json["framesize"] = framesize; json["capture_interval"] = capture_interval; json["repeat"] = repeat; json["xspeed"] = xspeed; json["length"] = xlength; json["gray"] = gray; json["ssid"] = ssid; json["pass"] = pass; json["horaire"] = horaire; File configFile = SPIFFS.open("/config.jsn", "w"); if (!configFile) { Serial.println(F("Failed to open config file for writing")); } //serializeJson(json, Serial); serializeJsonPretty(json, Serial); String output = ""; output += ""; output += " ESP32-CAM sauvegarde"; output += ""; output += "

Sauvegarde du fichier json sur la microSD


"; serializeJson(json, output); output += "
OK
"; output += " Attendre 10s avant de retourner sur la page d'avant!
" ; output += " RESET en cours !"; server.send(200, "text/html", output); // sauv flash serializeJson(json, configFile); configFile.close(); saveFileconfigSD(); //end save delay(1000); ESP.restart(); } /// /// RESET /// void reset_handler() { String output = ""; output += ""; output += " ESP32-CAM reset"; output += ""; output += "

Reset


"; output += " Attendre 10s avant de retourner sur la page d'avant!
" ; output += " RESET en cours !"; server.send(200, "text/html", output); delay(1000); ESP.restart(); } //// //// START CAMERA //// void startCameraServer() { //server.on("/", index_handler); server.on("/capture", capture_handler); server.on("/start", start_handler); server.on("/stop", stop_handler); server.on("/saveconfig", saveSPIFFSConfigFile); server.on("/list", listDir_handler); server.on("/reset", reset_handler); server.onNotFound(handleNotFound); server.begin(); Serial.println(F("Camera http started")); }