Skip to Content

Fotos mit ESP32-CAM aufnehmen

Fotos mit ESP32-CAM aufnehmen

In diesem Tutorial lernst du, wie du mit einer ESP32-CAM Fotos aufnimmst, wenn ein Knopf gedrückt wird, und diese auf der SD-Karte speicherst. Wir bauen im Grunde eine einfache Digitalkamera.

Benötigte Teile

Du brauchst einen ESP32-CAM, um die Codebeispiele auszuprobieren. Du kannst einen ESP32-CAM mit einem USB-TTL Shield zum Programmieren oder einen FTDI USB-TTL Adapter verwenden. Der FTDI USB-TTL Adapter ist etwas umständlicher zu benutzen, lässt aber die GPIO-Pins gut zugänglich. Das USB-TTL Shield ist einfacher zu verwenden, aber es ist schwieriger, die GPIO-Pins anzuschließen.

Wenn du deinen PC nutzen möchtest, um die vom ESP32-CAM aufgenommenen und auf der SD-Karte gespeicherten Bilder anzusehen, brauchst du eventuell auch einen SD-Kartenleser.

ESP32-CAM mit USB-TTL Shield

FTDI USB-TTL Adapter

Dupont wire set

Dupont-Kabel-Set

Half_breadboard56a

Breadboard

SD-Kartenleser

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Schaltplan

Wir wollen mit dem ESP32-CAM ein Foto machen, sobald ein Knopf gedrückt wird. Im folgenden Schaltplan verbinden wir daher einen Taster zwischen den GND-Pin und den GPIO13-Pin:

Button wired to ESP32-CAM
Taster an ESP32-CAM angeschlossen

Immer wenn der Knopf gedrückt (geschlossen) wird, wird GPIO13 auf Masse gezogen. Das funktioniert, weil GPIO13 (und andere GPIO-Pins) interne Pull-ups hat, die GPIO13 hoch halten, wenn nichts angeschlossen ist. Das bedeutet, die Tasterfunktion ist invertiert (wenn geschlossen GPIO13 == LOW) und das wirst du im Codeabschnitt sehen.

Das folgende Bild zeigt, wie du diese Schaltung mit einem Breadboard und dem ESP32-CAM board aufbauen kannst:

Circuit on breadboard with button and ESP32-CAM
Schaltung auf Breadboard mit Taster und ESP32-CAM

Wie du siehst, habe ich das USB-TTL Shield verwendet und konnte eine kleine Lücke zwischen ESP32-CAM und Shield lassen, um die Kabel für den Taster an GND und GPIO13 anzuschließen:

Connecting wires at gap between ESP32-CAM and USB-TTL Shield
Kabelverbindung in der Lücke zwischen ESP32-CAM und USB-TTL Shield

Alternativ könntest du den FTDI USB-TTL Adapter verwenden oder den ESP32-CAM über ein Dupont-Kabelset mit dem USB-TTL Shield verbinden.

Code zum Fotografieren und Speichern

Das folgende Arduino-Sketch macht ein Foto mit der ESP32-CAM-Kamera und speichert es auf der SD-Karte, wenn der an GPIO13 angeschlossene Knopf gedrückt (geschlossen) wird.

Für diesen Code benötigst du die esp32cam Bibliothek. Du kannst sie über den LIBRARY MANAGER in der Arduino IDE installieren. Suche einfach nach „esp32cam“ und drücke INSTALL. Das Bild unten zeigt die abgeschlossene Installation:

esp32cam library installed via Library Manager
esp32cam Bibliothek installiert über LIBRARY MANAGER

Unten findest du den kompletten Code für unsere kleine Schnappschuss-Anwendung. Schau ihn dir zuerst an, dann besprechen wir die Details:

#include "FS.h"
#include "SD_MMC.h"
#include "esp32cam.h"

const byte btnPin = GPIO_NUM_13;
const auto RES = esp32cam::Resolution::find(1600, 1200);

void takePicAndSave() {
  static int cnt = 0;

  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    Serial.println("Capture failed!");
    return;
  }
  
  String path = "/img" + String(cnt++) + ".jpg";
  File file = SD_MMC.open(path.c_str(), FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file!");
  }
  frame->writeTo(file);
  Serial.printf("Wrote: %s\n", path.c_str());
  file.close();
}

void enableFlash(bool enable) {
  digitalWrite(GPIO_NUM_4, enable ? HIGH : LOW);
}

void initCamera() {
    using namespace esp32cam;
    Config cfg;
    cfg.setPins(pins::AiThinker);
    cfg.setResolution(RES);
    cfg.setJpeg(80);

    bool ok = Camera.begin(cfg);
    Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}

void initSDCard() {
  if (!SD_MMC.begin("/sdcard", true)) {
    Serial.println("SD Card Mount Failed!");
  } else if (SD_MMC.cardType() == CARD_NONE) {
    Serial.println("No SD card inserted!");
  }
  Serial.println("SD card ready.");
}

void setup() {
  Serial.begin(115200);
  initCamera();
  initSDCard();
  pinMode(btnPin, INPUT);
  pinMode(GPIO_NUM_4, OUTPUT);
  enableFlash(false);  
  delay(1000);
}

void loop() {
  if (!digitalRead(btnPin)) {
    enableFlash(true);
    takePicAndSave();
    enableFlash(false);
    delay(500);
  }
}

Bibliotheken

Am Anfang des Codes werden drei Bibliotheken eingebunden:

#include "FS.h"
#include "SD_MMC.h"
#include "esp32cam.h"

Diese Bibliotheken sind essenziell für das ESP32-CAM-Projekt. Die FS.h Bibliothek stellt Dateisystemoperationen bereit, SD_MMC.h ermöglicht dem ESP32 die Anbindung an SD-Karten über den MMC (MultiMediaCard) Bus, und esp32cam.h ist eine höherstufige Bibliothek, die die Kamerakonfiguration und Bildaufnahme auf dem ESP32-CAM Modul vereinfacht.

Konstanten

Dann werden eine Konstante für den Taster-Pin und eine Kameraauflösung definiert:

const byte btnPin = GPIO_NUM_13;
const auto RES = esp32cam::Resolution::find(1600, 1200);

Hier wird btnPin auf GPIO 13 gesetzt, wo der Taster angeschlossen ist. Du könntest auch GPIO 12 verwenden, aber in beiden Fällen muss die SD-Karten-Schnittstelle im 1-Bit-Modus laufen! Für mehr Details siehe das More GPIO pins for ESP32-CAM Tutorial.

Das RES Objekt definiert die Bildauflösung, in diesem Fall 1600×1200 Pixel (UXGA), die später zur Kamerakonfiguration verwendet wird. Hier ist eine Liste möglicher Kameraauflösungen, wobei je nach Kamera nicht alle funktionieren:

  • 96×96
  • 160×120
  • 128×128
  • 176×144
  • 240×176
  • 240×240
  • 320×240
  • 320×320
  • 400×296
  • 480×320
  • 640×480
  • 800×600
  • 1024×768
  • 1280×720
  • 1280×1024
  • 1600×1200

takePicAndSave

Die takePicAndSave() Funktion ist die Kernfunktion, die ein Bild aufnimmt und auf der SD-Karte speichert:

void takePicAndSave() {
  static int cnt = 0;

Die cnt Variable wird static deklariert, was bedeutet, dass sie ihren Wert zwischen Funktionsaufrufen behält. Wir nutzen sie, um für jedes Foto eindeutige Dateinamen zu erzeugen.

  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    Serial.println("Capture failed!");
    return;
  }

Die Funktion ruft esp32cam::capture() auf, um einen Frame von der Kamera zu erfassen. Wenn das Erfassen fehlschlägt (d.h. frame ist nullptr), wird eine Fehlermeldung ausgegeben und die Funktion beendet sich frühzeitig.

  String path = "/img" + String(cnt++) + ".jpg";
  File file = SD_MMC.open(path.c_str(), FILE_WRITE);

Der Code baut einen Dateinamen zusammen, indem er „img“ mit dem inkrementierenden cnt Wert verbindet und „.jpg“ anhängt. Die Datei wird dann auf der SD-Karte zum Schreiben geöffnet.

  if (!file) {
    Serial.println("Failed to open file!");
  }

Wenn die Datei aus irgendeinem Grund nicht geöffnet werden kann (z.B. SD-Karte nicht vorhanden, Dateisystemfehler), wird eine Fehlermeldung ausgegeben.

  frame->writeTo(file);
  Serial.printf("Wrote: %s\n", path.c_str());
  file.close();
}

Wenn die Datei erfolgreich geöffnet wurde, wird der aufgenommene Frame in die Datei geschrieben, eine Bestätigung mit dem gespeicherten Dateinamen im Serial Monitor ausgegeben und schließlich die Datei geschlossen, um die Datenintegrität zu gewährleisten.

enableFlash

Die enableFlash(bool enable) Funktion steuert den eingebauten LED-Blitz des ESP32-CAM:

void enableFlash(bool enable) {
  digitalWrite(GPIO_NUM_4, enable ? HIGH : LOW);
}

Diese Funktion setzt den Spannungspegel an GPIO 4. Wenn enable true ist, wird GPIO 4 auf HIGH gesetzt, wodurch die Blitz-LED eingeschaltet wird; andernfalls wird sie auf LOW gesetzt und der Blitz ausgeschaltet.

Mehr Informationen zur Steuerung der Blitz-LED findest du im Control ESP32-CAM Flash LED Tutorial.

initCamera

Die initCamera() Funktion initialisiert das Kameramodul:

void initCamera() {
    using namespace esp32cam;
    Config cfg;
    cfg.setPins(pins::AiThinker);
    cfg.setResolution(RES);
    cfg.setJpeg(80);

Innerhalb von initCamera() wird zuerst der esp32cam Namespace lokal eingebunden. Dann wird ein Config Objekt, cfg, erstellt und so konfiguriert, dass es die vordefinierten Pins für ein Ai-Thinker Board Layout verwendet, was der typische Boardtyp für ESP32-CAM Module ist. Die Kameraauflösung wird auf RES (1600×1200) und die JPEG-Kompressionsqualität auf 80 gesetzt (wobei 100 beste Qualität bedeutet und niedrigere Werte mehr Kompression und schlechtere Qualität).

    bool ok = Camera.begin(cfg);
    Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}

Die Camera.begin(cfg) Funktion versucht, die Kamera mit den angegebenen Einstellungen zu starten. Sie gibt „CAMERA OK“ aus, wenn erfolgreich, oder „CAMERA FAIL“, wenn nicht.

initSDCard

Die initSDCard() Funktion mountet die SD-Karte:

void initSDCard() {
  if (!SD_MMC.begin("/sdcard", true)) {
    Serial.println("SD Card Mount Failed!");
  } else if (SD_MMC.cardType() == CARD_NONE) {
    Serial.println("No SD card inserted!");
  }
  Serial.println("SD card ready.");
}

Hier initialisiert SD_MMC.begin() den SD_MMC Bus und versucht, das Dateisystem unter dem Pfad /sdcard zu mounten. Das zweite Argument true aktiviert den 1-Leiter-Modus, der die Verkabelung vereinfacht, aber die Geschwindigkeit reduziert. Wenn das Mounten fehlschlägt, wird „SD Card Mount Failed!“ ausgegeben. Wenn die Karte gemountet ist, aber keine Karte physisch eingelegt ist, erscheint „No SD card inserted!“. Ansonsten wird angenommen, dass alles in Ordnung ist und „SD card ready.“ ausgegeben.

setup

Die setup() Funktion bereitet alles einmalig beim Start vor:

void setup() {
  Serial.begin(115200);
  initCamera();
  initSDCard();
  pinMode(btnPin, INPUT);
  pinMode(GPIO_NUM_4, OUTPUT);
  enableFlash(false);  
  delay(1000);
}

In setup() wird die serielle Schnittstelle mit 115200 Baud für Debugging gestartet. Die Kamera und SD-Karte werden durch Aufruf der Funktionen initCamera() und initSDCard() initialisiert. Dann wird der Taster-Pin (btnPin) als Eingang gesetzt und der Pin zur Blitzsteuerung (GPIO 4) als Ausgang. enableFlash(false) sorgt dafür, dass die Blitz-LED ausgeschaltet startet. Am Ende wird eine Verzögerung von 1 Sekunde (1000 Millisekunden) eingefügt, damit sich das System stabilisieren kann, bevor die Hauptschleife startet.

loop

Schließlich überprüft die loop() Funktion ständig, ob der Knopf gedrückt ist:

void loop() {
  if (!digitalRead(btnPin)) {
    enableFlash(true);
    takePicAndSave();
    enableFlash(false);
    delay(500);
  }
}

In loop() wird der Zustand von btnPin gelesen. Wenn der Knopf gedrückt ist, liefert digitalRead(btnPin) LOW, weil der Pin beim Drücken auf Masse gezogen wird. Wenn der Knopf als gedrückt erkannt wird, wird der Blitz durch Aufruf von enableFlash(true) eingeschaltet, dann wird ein Foto aufgenommen und gespeichert durch Aufruf von takePicAndSave(), und schließlich wird der Blitz wieder mit enableFlash(false) ausgeschaltet. Danach folgt eine 500 Millisekunden Verzögerung, um zu verhindern, dass bei einem einzigen Tastendruck mehrere Bilder gemacht werden.

Fazit

In diesem Tutorial hast du gelernt, wie man mit dem ESP32-CAM eine kleine Digitalkamera baut. Das Codebeispiel richtet den ESP32-CAM so ein, dass er einen Knopf überwacht. Wenn der Knopf gedrückt wird, schaltet er den Blitz ein, nimmt ein Foto mit 1600×1200 Auflösung auf, speichert es auf der SD-Karte unter einem fortlaufenden Dateinamen wie /img0.jpg, /img1.jpg usw., schaltet den Blitz aus und wartet kurz, bevor er wieder lauscht.

Wenn du den ESP32 zwischen den Aufnahmen in den Deep-Sleep-Modus versetzen möchtest, schau dir das Motion Activated ESP32-CAM Tutorial an, und für Video-Streaming lies den Stream Video with ESP32-CAM Artikel.

Außerdem, wenn du benachrichtigt werden möchtest, sobald Bewegung erkannt wird, schau dir unser ESP32 send Telegram Message Tutorial an, das zeigt, wie du Nachrichten an die Telegram-App auf deinem Handy sendest.

Und wenn du Fragen hast, hinterlasse sie gerne im Kommentarbereich.

Viel Spaß beim Tüfteln 😉