Outils pour utilisateurs

Outils du site


projets:esp32-cam_timelaps_code

Ceci est une ancienne révision du document !


/*

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,
 "total_frames":180,
 "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[] = “v9cGepeto”; modif AccesPoint and http filesystem plage horaire #include <dummy.h> 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” 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() 1) #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #include “esp_log.h” #include “esp_camera.h”

#include <ESPmDNS.h>

#include <FS.h> #include “SPIFFS.h”

Now support ArduinoJson 6.0.0+ ( tested with v6.14.1 ) #include <ArduinoJson.h> https://github.com/bblanchon/ArduinoJson

#include <WiFi.h> #include <WiFiClient.h> #include <WebServer.h>

#include <ESP32WebServer.h> a changer dans src de ESP_WiFiManager #include <ESP_WiFiManager.h> https://github.com/khoih-prog/ESP_WiFiManager #include <WiFiManager.h> 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 <SD_MMC.h> 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 bool ntp_ok = false; 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

if (internet_connected >= 3 || ntp_ok == false) { // pas internet = pas horaire
  return;
}
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 ( timeinfo.tm_hour) ;
    //Serial.print(F("heures "));
    //Serial.println(heure);
    if ((int)timeinfo.tm_hour  == heure) {
      //Serial.print(F("  "));
      //Serial.println(strftime_buf);
      heure = 99;
      break;
    }
  }
  //strftime(strftime_buf, sizeof(strftime_buf), "%M", &timeinfo);
  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["total_frames"]       = total_frames;
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<char[]> 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["total_frames"])     total_frames =  json["total_frames"] | total_frames;
        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"));
    }
  }
}
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. ");
        }
        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("SD Card init failed with error 0x%x", card_err);
  major_fail();
  return;
}
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() {

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")); // juste pour ntp
    WiFi.begin("FreeWifi");
    delay(2000);
    if (WiFi.SSID() == (String)"FreeWifi") {
      Serial.println(F("oui"));
      internet_connected = 2;
    }
    delay(1000);
  } else if (connAttempts > 13) { // laisse tomber, passage AP
    internet_connected = 3;
    ntp_ok = false;
  }
  connAttempts++;
}
if (internet_connected == 0) internet_connected = 1;
if (internet_connected < 3) {
  Serial.println(WiFi.SSID());
  Serial.println(internet_connected);
  char pool[16] = "";
  if ( internet_connected == 2) {
    sprintf(pool, "%s", WiFi.gatewayIP().toString().c_str());
  } else {
    sprintf(pool, "pool.ntp.org");
  }
  Serial.print(F("Waiting for system time to be set "));
  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(1000);
  time(&now);
  localtime_r(&now, &timeinfo);
  Serial.println(ctime(&now));
  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);
    ntp_ok = true;
    Serial.println(ctime(&now));
    sprintf(localip, "%s", WiFi.localIP().toString().c_str());
  }
  ntp_ok = true; // on force
}
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 setting up MDNS responder!"));
  } else {
    Serial.printf("mDNS responder 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 card mount successfully!"));
}  else  {
  Serial.printf("Failed to mount SD card VFAT filesystem. 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("Failed to open directory"));
  return;
}
if (!root.isDirectory()) {
  Serial.println(F("Not a directory"));
  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("Done capture due to 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("Done 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 failed with 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("Failed to open /nofic.jsn for 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("Failed to open /nofic.jsn for reading"));
}
DynamicJsonDocument json(32);
DeserializationError error = deserializeJson(json, ficnofic);
if (error) {
  Serial.println(F("err lecture json /nofic.jon"));
  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("Starting an avi ");
config_camera();
if ( ntp_ok == true) {
  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("Could not open file");
  major_fail();
}
if (idxfile != NULL)  {
  //Serial.printf("File open: %s\n", "/sdcard/idx.tmp");
}  else  {
  Serial.println("Could not 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("Error on 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 - closing the 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 recorded and 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("Could not 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 ( ntp_ok == false) {
  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("Could not 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(<!doctype html>
<head>
  <title>ESP32 Photo</title>
  <style>
    body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
  </style>
</head>
<body>
  <h1>Capture</h1>
  <p>Date: %s</p>
  <img src="/capture.jpg" />
  <br>
  <h3><a href="http://%s/">http://%s/</a></h3>
</body>

</html>)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("Could not 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(<!doctype html>

<html> <head> <meta charset=“utf-8”> <meta name=“viewport” content=“width=device-width,initial-scale=1”> <title>%s ESP32-CAM start</title> </head> <body> <h1>%s<br>ESP32-CAM Video Recorder %s </h1><br> <h3>Message is <font color=“red”>%s</font></h3><br> Recording = %d (1 is active)<br> Capture Interval = %d ms<br> Length = %d seconds<br> Quality = %d (10 best to 50 worst)<br> Framesize = %d (10 UXGA, 7 SVGA, 6 VGA, 5 CIF)<br> Repeat = %d<br> Speed = %d<br> Gray = %d<br><br> <br> <br>

<h3><a href=“http://%s/”>http://%s/ retour au status</a></h3>

</body> </html>)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("starting 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(<!doctype html> <html> <head> <meta charset=“utf-8”> <meta name=“viewport” content=“width=device-width,initial-scale=1”> <title>%s ESP32-CAM stop</title> </head> <body> <h1>%s<br>ESP32-CAM configurations %s </h1><br> <h3>Message is <font color=“red”>%s</font></h3><br> <h3><a href=“http://%s/”>http://%s/ retour au status</a></h3> <h3><a href=“http://%s/start?framesize=VGA&length=1800&interval=100&quality=10&repeat=100&speed=1&gray=0”>VGA 2 fps for 30 minutes repeat, 1x playback</a></h3> <h3><a href=“http://%s/start?framesize=VGA&length=1800&interval=500&quality=10&repeat=300&speed=30&gray=0”>VGA 2 fps, for 30 minutes repeat, 30x playback</a></h3> <h3><a href=“http://%s/start?framesize=UXGA&length=1800&interval=1000&quality=10&repeat=100&speed=30&gray=0”>UXGA 1 sec per frame, for 30 minutes repeat, 30x playback</a></h3> <h3><a href=“http://%s/start?framesize=UXGA&length=1800&interval=500&quality=10&repeat=100&speed=30&gray=0”>UXGA 2 fps for 30 minutes repeat, 15x playback</a></h3> <h3><a href=“http://%s/start?framesize=CIF&length=1800&interval=50&quality=10&repeat=100&speed=1&gray=0”>CIF 20 fps second for 30 minutes repeat</a></h3> <h3><a href=“http://%s/start?framesize=UXGA&length=1800&interval=10000&quality=10&repeat=100&speed=1&gray=0”>UXGA 10 second for 30 minutes repeat</a></h3> <h3><a href=“http://%s/start?framesize=UXGA&length=1800&interval=600000&quality=10&repeat=100&speed=1&gray=0&sleep”>UXGA 10 min for 30 minutes repeati sleep</a></h3> <br> Capture Interval en ms<br> Length en seconds<br> Quality = 10 meilleurs a 50 pire<br> Framesize = UXGA SVGA VGA CIF<br> Repeat = nombre de fois<br> Speed = vitesse<br> Gray = 1 ou 0<br> Fichier sur la microSD /config.json <br> ex: {“devname”:“esp32-cam”,”framesize“:6,”repeat“:100,”xspeed“:1,”gray“:0,”quality“:10,”capture_interval“:10000,”total_frames“:180,”ssid“:“SNHACK”,”pass“:“1234567890”,”horaire“:“10 11,12,13,14,15,16,17,18,19,20”} <br>mis a jour dela config initiale par <h3><a href=“http://%s/saveconfig”> saveconfig</a></h3> </body> </html>)rawliteral”; sprintf(the_page, msg, devname, devname, vernum, the_message, localip, localip, localip, localip, localip, localip, localip, localip, localip, localip); Serial.println(the_page); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bool stop_handler() {

recording = 0;
Serial.println(F("stopping 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(<!doctype html> <html> <head> <meta charset=“utf-8”> <meta name=“viewport” content=“width=device-width,initial-scale=1”> <title>%s ESP32-CAM index</title> </head> <body> <h1>%s<br>ESP32-CAM Video Recorder %s <br><font color=“red”>%s</font></h1><br> <h3>Page de <font color=“red”>%s</font></h3><br> Total SD Space is %d MB, Used SD Space is %d MB<br> Recording = %d (1 is active)<br> Frame %d of %d, Skipped %d<br><br> Capture Interval = %d ms<br> Length = %d seconds<br> Quality = %d (10 best to 50 worst)<br> Framesize = %d (10 UXGA, 7 SVGA, 6 VGA, 5 CIF)<br> Repeat = %d<br> Playback Speed = %d<br> Gray = %d<br><br> Commandes possibles sur cette adresse ip:<br> <h3><a href=“http://%s/”>http://%s/</a></h3> <h3><a href=“http://%s/stop”>http://%s/stop<font color=“red”> … configuration et restart <br>Vous devez faire stop pour modifier la configuration</font></a></h3> <h3><a href=“http://%s/list”>http://%s/list> list </a></h3> <h3><a href=“http://%s/capture”>http://%s/capture> photo </a></h3> <h3><a href=“http://%s/saveconfig”> saveconfig</a></h3> Fichier sur la microSD /config.json ex: {“framesize”:6,”repeat“:100,”xspeed“:1,”gray“:0,”quality“:10,”capture_interval“:10000,”total_frames“:180}, cf list<br> LED flash sur une frame, SOS flash si pas de microSD<br> <br> </body> </html>)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 += "<html><head><meta charset=\"utf-8\">";
output += "<title> ESP32-CAM list</title>";
output += "</head><body>";
output += "<h3> Fichiers    Tailles</h3>";
File root = fs.open(dirname);
if (!root) {
  Serial.println(F("Failed to open directory"));
  return;
}
if (!root.isDirectory()) {
  Serial.println(F("Not a directory"));
  return;
}
File file = root.openNextFile();
while (file) {
  if (file.isDirectory()) {
    output += "<br><h2><a href=\"http://";
    output += (String)localip;
    output += (String)file.name();
    output += "\">http://";
    output += (String)localip;
    output += (String)file.name();
    output += "</a></h2>";
    output += "\n";
    Serial.println((String)file.name());
    if (levels) {
      listDir(fs, file.name(), levels - 1);
    }
  } else {
    output += "<br><a href=\"http://";
    output += (String)localip;
    output += (String)file.name();
    output += "\">http://";
    output += (String)localip;
    output += (String)file.name();
    output += "</a>";
    output += "  : ";
    output += file.size();
    Serial.println((String)file.name());
  }
  file = root.openNextFile();
}
output += "<br>";
output += "<br><a href=\"http://";
output += (String)localip;
output += "\">http://";
output += (String)localip;
output += " Retour aux status</a>";
output += "</body></html>";
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(“Saving 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[“total_frames”] = total_frames; 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 += "<html><head><meta charset=\"utf-8\">";
output += "<title> ESP32-CAM sauvegarde</title>";
output += "</head><body>";
output += "<h3>Sauvegarde du fichier json sur la microSD</h3><br>";
serializeJson(json, output);
output += " <br> OK <br>";
output += " Attendre 10s avant de retourner sur la page d'avant!<br>" ;
output += " RESET  en cours !</body></html>";
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 += “<html><head><meta charset=\”utf-8\“>”; output += “<title> ESP32-CAM reset</title>”; output += “</head><body>”; output += “<h3>Reset</h3><br>”; output += “ Attendre 10s avant de retourner sur la page d'avant!<br>” ; output += “ RESET en cours !</body></html>”; 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"));

}

1)
uint32_t)ESP.getEfuseMac(
projets/esp32-cam_timelaps_code.1591188598.txt.gz · Dernière modification : 2024/02/09 16:55 (modification externe)