ESP32-Cam - Bewegungsmelder mit Fotoserie

Gibt es bei einer ESP32-Cam die Möglichkeit, eine festgelegte Fotoserie auf einer MicroSD Karte zu speichern?

Es geht um eine Wildkamera mit Bewegungsmelder (mein erstes eigenes IoT Projekt). Jedes mal, wenn eine Bewegung registriert wird will ich, dass die Cam aus dem DeepSleep erwacht, im Abstand von 2 Sekunden insgesamt Fünf Bilder macht, diese auf der MicroSD speichert und wieder in den DeepSleep-Modus geht. Wenn danach wieder Motion detected wird soll das ganze von vorne starten...also wieder 5 Bilder in einem Zeitraum von knapp 10 Sekunden und wieder schlafen gehen...

Habe bislang noch keine Möglichkeit gefunden mehr als ein Bild mit der ESP32-Cam nach dem "aufwachen" zu machen? Bin über jede Antwort dankbar :slight_smile:

Hi,
Ich habe eine Skizze, die von Interesse sein könnte, da sie zeigt, wie man Bilder aufnimmt und auf SD-Karte speichert.

Auch diese Skizze macht Bewegungserkennung über die Kamera

Hi, Danke für Antwort.
Interessante Skizze. Allerdings ist das Problem nicht das Speichern von Bildern auf der SD Karte, sondern tatsächlich das aufnehmen von einer voreingestellten Bilder-Serie. Es sollen exakt 5 Bilder in 10 Sekunden abgespeichert werden. Ich hab also nur das Problem, dass ich nicht weiß, wie ich der Kam sagen soll dass sie mehr als ein Bild machen soll....WIFI wird es an dem Ort, wo ich sie aufstellen werde nicht geben, daher das speichern auf der SD Karte.

Wenn du die CAM ein Bild machen und auf SD Karte speichern kannst, dann kannst du sie das auch 5 Mal hintereinander machen lassen. Ob ihr 2 Sekunden reichen um ein Bild zu machen und abzuspeichern musst du einfach ausprobieren, ich denke das sollte klappen.
Mach dir eine Schleife in der ein Bild aufgenommen wird und lass diese dann 5 Mal durchlaufen. Beispiele für solche Schleifen findest du zuhauf im Netz. (Zum Beispiel for-Schleife)

Gruß

Genau das habe ich bislang nicht hinbekommen, weswegen ich hier um Hilfe gebeten habe. Wenn ich den Code, der ein Bild macht kopiere und kopiere und erneut einfüge kommt bei mir immer eine Fehlermeldung, dass man diesen nicht zweimal verwenden kann. Ist wie gesagt mein ersten IoT Projekt und ich hab all mein Wissen über den Code von 4 Youtube Videos und 3 GitHub Tutorials...

Dann zeige doch mal den Code für 2 Bilder und die dazu gehörenden Fehlermeldungen.
Wie soll Dir sonst jemand helfen können?

Gruß Tommy

Das ist der Code zum Generieren der Fotos den ich derzeit versuche zum Laufen zu bekommen. Hier mal mit dem Beispiel von 3 anstelle von 5 Bildern in Folge.

  // Foto 1
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }
  
  // Foto 1 unter diesem Pfad speichern
  String path = "/Foto" + String(fotoNummer) + ".jpg";
  fs::FS &fs = SD_MMC;
  
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file) {
    Serial.println("Fehler beim File erstellen"); 
  } 
  else {
    file.write(fb->buf, fb->len); 
    EEPROM.write(COUNT_ADDRESS, fotoNummer);
    EEPROM.commit();
  }
  delay(2000);
  file.close();
  esp_camera_fb_return(fb);

  
    // Foto 2
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }

  // Foto 2 unter diesem Pfad speichern
  String path = "/Foto" + String(fotoNummer) + ".jpg";
  fs::FS &fs = SD_MMC;
  
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file) {
    Serial.println("Fehler beim File erstellen"); 
  } 
  else {
    file.write(fb->buf, fb->len); 
    EEPROM.write(COUNT_ADDRESS, fotoNummer);
    EEPROM.commit();
  }
  delay(2000);
  file.close();
  esp_camera_fb_return(fb);


    // Foto 3
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }

  // Foto 3 unter diesem Pfad speichern
  String path = "/Foto" + String(fotoNummer) + ".jpg";
  fs::FS &fs = SD_MMC;
  
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file) {
    Serial.println("Fehler beim File erstellen"); 
  } 
  else {
    file.write(fb->buf, fb->len); 
    EEPROM.write(COUNT_ADDRESS, fotoNummer);
    EEPROM.commit();
  }
  delay(1000);  
  file.close();
  esp_camera_fb_return(fb);
  
  Serial.printf("Fotos gespeichert: %s\n", path.c_str());
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);        
  rtc_gpio_hold_en(GPIO_NUM_4);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_13,0);
  delay(1000);
  Serial.println("Gehe in den deep-sleep mode");
  esp_deep_sleep_start();
}

So hatte ich gedacht könnte es funktionieren. Allerdings bringt er mir dann folgenden Fehlercode:

FEHLERCODE

/Users/jbs/Documents/The_Code-WildCam/The_Code-WildCam.ino: In function 'void setup()':
The_Code-WildCam:127:10: error: redeclaration of 'String path'
String path = "/Foto" + String(fotoNummer) + ".jpg";

/Users/jbs/Documents/The_Code-WildCam/The_Code-WildCam.ino:102:10: note: 'String path' previously declared here
String path = "/Foto" + String(fotoNummer) + ".jpg";

The_Code-WildCam:128:11: error: redeclaration of 'fs::FS& fs'
fs::FS &fs = SD_MMC;

/Users/jbs/Documents/The_Code-WildCam/The_Code-WildCam.ino:103:11: note: 'fs::FS& fs' previously declared here
fs::FS &fs = SD_MMC;

The_Code-WildCam:130:8: error: redeclaration of 'fs::File file'
File file = fs.open(path.c_str(), FILE_WRITE);

/Users/jbs/Documents/The_Code-WildCam/The_Code-WildCam.ino:105:8: note: 'fs::File file' previously declared here
File file = fs.open(path.c_str(), FILE_WRITE);

The_Code-WildCam:152:10: error: redeclaration of 'String path'
String path = "/Foto" + String(fotoNummer) + ".jpg";

/Users/jbs/Documents/The_Code-WildCam/The_Code-WildCam.ino:102:10: note: 'String path' previously declared here
String path = "/Foto" + String(fotoNummer) + ".jpg";

The_Code-WildCam:153:11: error: redeclaration of 'fs::FS& fs'
fs::FS &fs = SD_MMC;

/Users/jbs/Documents/The_Code-WildCam/The_Code-WildCam.ino:103:11: note: 'fs::FS& fs' previously declared here
fs::FS &fs = SD_MMC;

The_Code-WildCam:155:8: error: redeclaration of 'fs::File file'
File file = fs.open(path.c_str(), FILE_WRITE);

/Users/jbs/Documents/The_Code-WildCam/The_Code-WildCam.ino:105:8: note: 'fs::File file' previously declared here
File file = fs.open(path.c_str(), FILE_WRITE);

exit status 1
redeclaration of 'String path'

Bin wie gesagt kein Coder, daher weiß ich damit recht wenig anzufangen. Wahrscheinlich ein schlichter Anfängerfehler....

Wo änderst Du die Variable "fotoNummer"?

#include "esp_camera.h"
#include "SD_MMC.h"
#include "EEPROM.h"
#include "driver/rtc_io.h"
#include "soc/soc.h"           
#include "soc/rtc_cntl_reg.h"  

#define ID_ADDRESS            0x00
#define COUNT_ADDRESS         0x01
#define ID_BYTE               0xAA
#define EEPROM_SIZE           0x0F

#define sleepTime 2e6
uint16_t fotoNummer = 0;

...

  // Kamera initialisieren
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Initialisierung fehlgeschlagen Fehler NR: 0x%x", err);
    return;
  }

  // Mounten der SD-Karte
  if(!SD_MMC.begin()) {
    Serial.println("Konnte die SD-Karte nicht mounten");
    return;
  }
  
  uint8_t cardType = SD_MMC.cardType();
  if(cardType == CARD_NONE) {
    Serial.println("Keine SD-Karte vorhanden");
    return;
  }

  // EEPROM Initialisieren
  if (!EEPROM.begin(EEPROM_SIZE)) {
    Serial.println("Fehler beim EEPROM initialisieren"); 
    return;
  }
  
  if(EEPROM.read(ID_ADDRESS) != ID_BYTE) { 
    fotoNummer = 0;
    EEPROM.write(ID_ADDRESS, ID_BYTE);  
    EEPROM.commit(); 
  }
  else {
    EEPROM.get(COUNT_ADDRESS, fotoNummer);
    fotoNummer +=  1;    
  }

Tja...
jetzt wäre es schön, wenn das auch da wäre, was da angemeckert wird.

Ansonsten ist die Meldung eindeutig.

Keine Ahnung warum ich nicht schon vorher auf die Idee gekommen bin, den ganzen Code zu posten...aber hier:

#include "esp_camera.h"
#include "SD_MMC.h"
#include "EEPROM.h"
#include "driver/rtc_io.h"
#include "soc/soc.h"           
#include "soc/rtc_cntl_reg.h"  

#define ID_ADDRESS            0x00
#define COUNT_ADDRESS         0x01
#define ID_BYTE               0xAA
#define EEPROM_SIZE           0x0F

#define sleepTime 2e6
uint16_t fotoNummer = 0;

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);  
  Serial.begin(115200);
  pinMode(4, OUTPUT);
  rtc_gpio_hold_dis(GPIO_NUM_4); 
  
 // Pin Definition für CAMERA_MODEL_AI_THINKER aus dem Webserver Beispiel unter camera_pins.h
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0; 
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 5;
  config.pin_d1 = 18;
  config.pin_d2 = 19;
  config.pin_d3 = 21;
  config.pin_d4 = 36;
  config.pin_d5 = 39;
  config.pin_d6 = 34;
  config.pin_d7 = 35;
  config.pin_xclk = 0;
  config.pin_pclk = 22;
  config.pin_vsync = 25;
  config.pin_href = 23;
  config.pin_sscb_sda = 26;
  config.pin_sscb_scl = 27;
  config.pin_pwdn = 32;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA; 
    config.jpeg_quality = 10;           
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // Kamera initialisieren
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Kamera Initialisierung fehlgeschlagen Fehler NR: 0x%x", err);
    return;
  }

  // Mounten der SD-Karte
  if(!SD_MMC.begin()) {
    Serial.println("Kann die SD-Karte nicht mounten");
    return;
  }
  
  uint8_t cardType = SD_MMC.cardType();
  if(cardType == CARD_NONE) {
    Serial.println("Keine SD-Karte vorhanden");
    return;
  }

  // EEPROM Initialisieren
  if (!EEPROM.begin(EEPROM_SIZE)) {
    Serial.println("Fehler beim EEPROM initialisieren"); 
    return;
  }
  
  if(EEPROM.read(ID_ADDRESS) != ID_BYTE) { 
    fotoNummer = 0;
    EEPROM.write(ID_ADDRESS, ID_BYTE);  
    EEPROM.commit(); 
  }
  else {
    EEPROM.get(COUNT_ADDRESS, fotoNummer);
    fotoNummer +=  1;    
  }


  // Foto 1
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }
  
  // Foto 1 unter diesem Pfad speichern
  String path = "/Foto" + String(fotoNummer) + ".jpg";
  fs::FS &fs = SD_MMC;
  
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file) {
    Serial.println("Fehler beim File erstellen"); 
  } 
  else {
    file.write(fb->buf, fb->len); 
    EEPROM.write(COUNT_ADDRESS, fotoNummer);
    EEPROM.commit();
  }
  delay(2000);
  file.close();
  esp_camera_fb_return(fb);

  
    // Foto 2
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }

  // Foto 2 unter diesem Pfad speichern
  String path = "/Foto" + String(fotoNummer) + ".jpg";
  fs::FS &fs = SD_MMC;
  
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file) {
    Serial.println("Fehler beim File erstellen"); 
  } 
  else {
    file.write(fb->buf, fb->len); 
    EEPROM.write(COUNT_ADDRESS, fotoNummer);
    EEPROM.commit();
  }
  delay(2000);
  file.close();
  esp_camera_fb_return(fb);


    // Foto 3
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }

  // Foto 3 unter diesem Pfad speichern
  String path = "/Foto" + String(fotoNummer) + ".jpg";
  fs::FS &fs = SD_MMC;
  
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file) {
    Serial.println("Fehler beim File erstellen"); 
  } 
  else {
    file.write(fb->buf, fb->len); 
    EEPROM.write(COUNT_ADDRESS, fotoNummer);
    EEPROM.commit();
  }
  delay(1000);  
  file.close();
  esp_camera_fb_return(fb);
  
  Serial.printf("Fotos gespeichert: %s\n", path.c_str());
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);        
  rtc_gpio_hold_en(GPIO_NUM_4);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_13,0);
  delay(1000);
  Serial.println("Gehe in den deep-sleep mode");
  esp_deep_sleep_start();
}

void loop() {
}

Der Code hat in Zeile 100, 125 und 150 jeweils String path drin zu stehen.
In 100 ist das in Ordnung, in den anderen muss das "String" weg.

Und dann muss fotoNummer noch geeignet erhöht werden (sonst wird das erste Bild zweimal überschrieben oder das Schreiben klappt nicht).

Das Aufheben der Nummer im EEPROM oder seiner Simulation im Flash würde ich nur nach dem letzten Bild machen. Schont die Hardware :slightly_smiling_face:

// Bis jetzt FAIL!

#include "esp_camera.h"
#include "SD_MMC.h"
#include "EEPROM.h"
#include "driver/rtc_io.h"
#include "soc/soc.h"           
#include "soc/rtc_cntl_reg.h"  

#define ID_ADDRESS            0x00
#define COUNT_ADDRESS         0x01
#define ID_BYTE               0xAA
#define EEPROM_SIZE           0x0F

#define sleepTime 2e6
uint16_t fotoNummer = 3;

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);  
  Serial.begin(115200);
  pinMode(4, OUTPUT);
  rtc_gpio_hold_dis(GPIO_NUM_4); 
  
 // Pin Definition für CAMERA_MODEL_AI_THINKER aus dem Webserver Beispiel unter camera_pins.h
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0; 
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 5;
  config.pin_d1 = 18;
  config.pin_d2 = 19;
  config.pin_d3 = 21;
  config.pin_d4 = 36;
  config.pin_d5 = 39;
  config.pin_d6 = 34;
  config.pin_d7 = 35;
  config.pin_xclk = 0;
  config.pin_pclk = 22;
  config.pin_vsync = 25;
  config.pin_href = 23;
  config.pin_sscb_sda = 26;
  config.pin_sscb_scl = 27;
  config.pin_pwdn = 32;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA; 
    config.jpeg_quality = 10;           
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // Kamera initialisieren
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Kamera Initialisierung fehlgeschlagen Fehler NR: 0x%x", err);
    return;
  }

  // Mounten der SD-Karte
  if(!SD_MMC.begin()) {
    Serial.println("Kann die SD-Karte nicht mounten");
    return;
  }
  
  uint8_t cardType = SD_MMC.cardType();
  if(cardType == CARD_NONE) {
    Serial.println("Keine SD-Karte vorhanden");
    return;
  }

  // EEPROM Initialisieren
  if (!EEPROM.begin(EEPROM_SIZE)) {
    Serial.println("Fehler beim EEPROM initialisieren"); 
    return;
  }
  
  if(EEPROM.read(ID_ADDRESS) != ID_BYTE) { 
    fotoNummer = 0;
    EEPROM.write(ID_ADDRESS, ID_BYTE);  
    EEPROM.commit(); 
  }
  else {
    EEPROM.get(COUNT_ADDRESS, fotoNummer);
    fotoNummer +=  1;    
  }


  // Foto 1
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }
  
  // Foto 1 unter diesem Pfad speichern
  String path = "/Foto" + String(fotoNummer) + ".jpg";

  delay(1000);
  esp_camera_fb_return(fb);
  delay(1000);
  
    // Foto 2
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }
  
  delay(1000);
  esp_camera_fb_return(fb);
  delay(1000);

    // Foto 3
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Fotografieren fehlgeschlagen!");
    return;
  }
  fs::FS &fs = SD_MMC;
  File file = fs.open(path.c_str(), FILE_WRITE);
  if(!file) {
    Serial.println("Fehler beim File erstellen"); 
  } 
  else {
    file.write(fb->buf, fb->len); 
    EEPROM.write(COUNT_ADDRESS, fotoNummer);
    EEPROM.commit();
  }
  delay(1000);  
  file.close();
  esp_camera_fb_return(fb);
  delay(1000);
  
  Serial.printf("Fotos gespeichert: %s\n", path.c_str());
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);        
  rtc_gpio_hold_en(GPIO_NUM_4);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_13,0);
  delay(1000);
  Serial.println("Gehe in den deep-sleep mode");
  esp_deep_sleep_start();
}

void loop() {
}

Keine Ahnung ob ich es richtig gemacht habe, zumindest kommt jetzt keine Fehlermeldung mehr...Allerdings macht die Cam wieder nur ein Bild und leider keine drei. Was hab ich diesmal fabriziert? :slight_smile:

prüfe mal deine Verwendung von

fotoNummer +=  1;  

und analysiere, was passiert wenn du es nicht vor jedem Schreiben noch mal machst...

Was meinst du mit Verwendung prüfen? Ist das nicht dazu da, dass auf die fotoNummer immer +1 addiert wird um ein Überschreiben von Bildern zu verhindern?

Hab es mal versucht komplett wegzulassen oder die Zahl hochzusetzen....bleibt alles gleich, es wird nur ein Bild aufgenommen und auf der SD-Card gespeichert =(

du solltest die Verwendung überprüfen.
Ich habe nicht geschrieben dass du es löschen sollst.
schau noch mal rein.
Du erhöhst beim ersten Bild deinen Zähler.
Was machst beim zweiten Bild?
Wie hoch ist der Zähler beim zweiten Bild?
Was passiert dann mit dem ersten Bild?

Was machst beim dritten Bild mit dem Zähler?
Wie hoch ist der Zähler beim dritten Bild?
Was passiert dann mit dem zweiten Bild?

Diese Fragen sollst du dir stellen und beantworten.

Welche Programänderung ergibt sich daraus?

Besagt der Code nicht, dass der Zähler sich jedes mal um 1 erhöht wenn ein neues Bild gemacht wird? Dachte das wird dann bei der Bilderserie auch so sein...also das Bild 2 = Bild 1+1 und Bild 3 = Bild 2+1 usw ist und er dann jedes Bild einzeln auf der SD abspeichert. So war zumindest die Idee :smirk: Ich bin eigentlich Schreiner und hab von Computern und vor allem vom Coden absolut keine Ahnung... :grimacing: Ich weiß lediglich, was ich will, dass der Code kann...

Der Code besagt, daß der Zähler erhöht wird, wenn der Code aufgerufen wird.
Also exakt 1 mal.

Und d.h. ich muss was genau ändern?