Diesmal fang ich direkt mit dem Ergebnis des Projekts an, das aktuellste Bild aus dem Nistkasten:

Hier gibt es alle Bilder (evtl. lange Ladezeit): Galerie

Die Grundidee zum Nistkasten mit Kamera kam beim Lesen eines make:-Artikels. Da bei uns eh ziemlich viele Vögel in den Gärten und Hecken sind, besteht vielleicht die Chance, dass man die Tiere in einem Nistkasten mit Kamera beobachten kann. Der Nistkasten aus der make: ist mit einem Raspberry Zero, einer USB-Kamera und einer festen Spannungsversorgung ausgestattet. Ich hatte aber noch einige ESP32Cam-Module im Schieber liegen.

Meine Idee für den Nistkasten mit Kamera sah folgendermaßen aus:
Es soll ein Nistkasten gebaut werden, der mit Hilfe einer Kamera den Nist- und eventuellen Brutfortschritt im Innern dokumentiert. Der Kasten soll am Gartenhaus angebracht werden und ohne externe Stromversorgung auskommen. Die Bilder sollen per WLAN auf einem FTP-Server landen und dann auch zur Ansicht veröffentlicht werden. Das war mal der grobe Rahmen.

Motiviert durch die große Photovoltaikanlage auf dem Hausdach, wollte ich auch eine unabhängige Lösung für das Vogelhäuschen, komplett mit Akku (als Backup sollte von extern auch geladen werden können.
Beim Bau des Nistkastens hab ich mich grob an den Größenvorgaben vom NABU orientiert. Als Material habe ich Siebdruckplatte gewählt, da die noch als Reststück verfügbar war.
Im Nistkasten ist es recht dunkel, was natürlich schlecht ist, wenn man Bilder aus dem Innern machen will. Um die Vögel nicht zu stören hab ich Infrarot-LED verbaut. Infrarot-Licht ist für Vögel genau so unsichtbar wie für Menschen. Einige Vogelarten können aber Licht aus dem Ultraviolettbereich wahrnehmen.

Vier LEDs sollten genug sein um den Kasten ordentlich auszuleuchten. Das Loch in der Mitte ist für die Kamera. Die Spannungsversorgung und die Steuerung der LEDs muss auch im „Dachstuhl“ des Nistkasten untergebracht werden. Ein erster Versuchsaufbau (Akku, Laderegler und StepUp-Regler um aus den 3,7V des Akkus wieder 5V zu machen) sah sehr übersichtlich aus.

Der Akku wurde geladen und es kamen auch die gewünschten 5V hinten raus. Allerdings reichte ein einzelner Akku nicht um das Kameramodul dauerhaft mit Strom zu versorgen.

Trotzdem habe ich alles mal in den Nistkasten gebaut und ihn probeweise am Gartenhaus montiert. Das Einflugloch hab ich dabei noch zugeklebt, nicht, dass beim Testen schon ein Vogel in die neue Villa einziehen will.

Das Teil sieht schon ganz schick aus, ich wusste nun auch ungefähr was an der Hardware und Software noch geändert werden muss.
Nun mal etwas zur Software: Für den ESP gibt es eine fertige Webserverlösung mit der man das Modul sehr einfach als Netzwerkkamera einsetzen kann, es bietet auch viele Einstellmöglichkeiten zur Auflösung, Bildqualität, Weißabgleich usw., sogar eine rudimentäre Gesichtserkennung ist in der Software untergebracht. Das war meine Ausgangsbasis.

In dem Beispielprogramm änderte ich erstmal einiges um vor jeder Bildaufnahme die LEDs einzuschalten und die Bilder auf einer Micro-SD-Karte zu speichern. Problem an dem Programm war, dass der ESP dauerhaft lief und das nicht gerade stromsparend ist. Aus der Kombination aus Hardware, Standort und Software ergaben sich folgende neue Aufgaben:
– WLAN-Reichweite des ESPs reicht nicht aus, da die Platinenantenne nicht viel taugt
– Stromverbrauch reduzieren
– Software schreiben und einen „Tiefschlafmodus“ integrieren
– Solarausbeute erhöhen und größeren Akku einbauen

WLAN verbessern:
Um eine externe Antenne am ESP anzuschließen hat das Modul schon einen Antennenstecker verbaut, allerdings ist der noch ohne Funktion, da standardmäßig die interne Antenne (ganz unten auf der Platine) verwendet wird. Um den Anschluss für die externe Antenne zu aktivieren muss man das grün markierte Bauteil einmal auslöten und 90° verdreht wieder einlöten, so dass es an der blauen Linie orientiert den Kontakt zum Anschluss herstellt.

Stromverbrauch und Software:
Um den Stromverbauch hardwareseitig zu reduzieren, besorgte ich einen besseren Laderegler (mit einem CN3065), der auf kleine Solarpanels zugeschnitten ist und ich entschied mich später auf die SD-Karte zu verzichten. Der nächste große Brocken war dann die Software. Da kamen recht viele unerwartete Probleme auf mich zu, die es zu bewältigen galt. Als erstes versuchte ich einfach nur Bilder auf der integrierten MicroSD-Karte zu speichern, was auch gelang. Also hoch damit auf einen Webserver. Dort kamen nur 4 Byte große Dateien an, die nix mit den Bildern zu tun hatten und deren Inhalt auch keinen Aufschluss darüber gab, warum da nur 4 Byte ankamen. Den FTP-Upload hab ich aus einem Beispielcode übernommen. Darin wird die Länge der Bilddatei dynamisch ermittelt, indem die Länge des Kamera-Bildspeichers ausgelesen wird. Der ursprünglicher Programmablauf (ähnlich wie in der Beispieldatei) war folgendermaßen:

1. Kamera initialisieren
2. Bild aufnehmen
3. Bildspeicher auslesen
4. Datei auf MicroSD-Karte schreiben
5. WLAN-Verbindung aufbauen
6. Einloggen auf FTP-Server
7. Daten aus Bildspeicher auf FTP übertragen
8. Springe zu Schritt 1

Als ich das dynamische Ermitteln der Bildspeicherlänge dann deaktiviert hatte kamen Bilddateien auf dem Server an. Die waren aber alle defekt, man erkannte nur einige Artefakte von dem was aufgenommen wurde. Die Bilder auf der SD-Karte dagegen waren in Ordnung. Ich denke, der Bildspeicher der Kamera ist nach einer Bildaufnahme nicht wirklich lange zuverlässig, gerade wenn nach der Aufnahme noch weitere Sachen im Programm ausgeführt werden (WLAN-Verbindung aufbauen z.B.). Da ich eh Strom sparen wollte verzichtete ich auf die Micro-SD-Karte und änderte mein Programm ab, so dass die WLAN-Verbindung schon vor der Bildaufnahme aufgebaut wird. Das Schreiben auf den FTP erfolgt direkt nach der Bildaufnahme. Das war die Lösung für das „kaputte Bilder“-Phänomen. Um die einzelnen Bilder zu sortieren lasse ich vor der Bildaufnahme noch die aktuelle Uhrzeit von einem NTP-Server holen und nutze Datum und Uhrzeit direkt als Dateinamen. Nach der Datenübertragung geht der ESP für eine definierte Zeit in den Tiefschlaf, dabei verbraucht er sogut wie keinen Strom. Nach der festgelegten Zeit erwacht der ESP wieder und der Ablauf beginnt von neuem. Der fertige Ablauf sieht jetzt so aus:

1. Kamera initialisieren
2. WLAN-Verbindung aufbauen
3. Uhrzeit holen
4. Bild aufnehmen
5. Bildspeicher auslesen
6. Einloggen auf FTP-Server
7. Daten aus Bildspeicher auf FTP übertragen
8. Schlafen und dann wieder zu Schritt 1

Damit landen nun ca. alle 15 Minuten ein neus Bild auf meinem FTP. Den fertigen Programmcode gibt es ganz am Ende.

Solarausbeute und größerer Akku:
Um die Solarausbeite zu erhöhen habe ich etwas herumexperimentiert und versucht mit 2 Panels auszukommen, letztendlich kaufte ich ein effizienteres Panel und hoffe dadurch die jetzt zwei 18650er Akkus laden zu können. Die Akkus sind parallel geschaltet und halten ohne Sonne knapp eine Woche durch.

besseres Bild kommt noch

Für meine Tests platzierte ich ein paar Sachen im Nistkasten, die auf der Werkbank rumlagen. Wichtig dabei war auch eine Uhr, so dass man auch erkennen kann ob neue Bilder aufgenommen wurden oder ob man immer das gleiche sieht. Der unterschiedliche Farbstich kommt vom automatischen Weißabgleich der Kamera. Den konnte ich auch nicht deaktivieren und auf Graustufen konnte ich das Modul nicht konfigurieren, da ich ein anderes, weitwinkligeres Kameramodul als das originale verwende.

Programmcode:

/******************************************************************************/
#include "esp_camera.h"
#include "Arduino.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <ESP32_FTPClient.h>
#include "soc/soc.h" // Disable brownout problems
#include "soc/rtc_cntl_reg.h" // Disable brownouz problems
#include "driver/rtc_io.h"
#include <NTPClient.h> // https://randomnerdtutorials.com/esp32-ntp-client-date-time-arduino-ide/ 
#include <WiFiUdp.h>

#define WIFI_SSID "WLAN-NAME"
#define WIFI_PASS "PASSWORT"

RTC_DATA_ATTR int bootCount = 0;

// Pin definition for 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


// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

// define FTP-Server
char ftp_server[] = "FTPSERVER";
char ftp_user[] = "FTPUSER";
char ftp_pass[] = "PASSWORT";

int SleepSeconds = 900;
int SleepMultiplier = 1000000;
long SleepMicros = 1000;
int irLEDs = 12; //Pin for IR-LEDs


// you can pass a FTP timeout and debug mode on the last 2 arguments
ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass, 5000, 2);

void setup()
{
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  Serial.begin( 115200 );
  Serial.setDebugOutput(true);

  camera_config_t config;
  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;


  pinMode(irLEDs, OUTPUT);
  digitalWrite(irLEDs, LOW); // turn on LEDs
  Serial.println("LEDs off");

  pinMode(4, INPUT);
  digitalWrite(4, LOW);
  rtc_gpio_hold_dis(GPIO_NUM_4);

  if (psramFound()) {

    Serial.println("Check PSRAM");
    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 8;
    config.fb_count = 2;
    Serial.println("PSRAM found");
  } else {

    Serial.println("No PSRAM found");
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // Init Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  WiFi.begin( WIFI_SSID, WIFI_PASS );

  Serial.println("Connecting Wifi...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());


  Serial.println("fetching time...");
  delay(2000);
  // Initialize a NTPClient to get time
  timeClient.begin();
  // Set offset time in seconds to adjust for your timezone, for example:
  // GMT +1 = 3600
  // GMT 0 = 0
  timeClient.setTimeOffset(3600);

  while (!timeClient.update()) {
    timeClient.forceUpdate();
  }
  // The formattedDate comes with the following format:
  // 2018-05-28T16:00:13Z
  formattedDate = timeClient.getFormattedDate();
  Serial.println(formattedDate);

  // Extract date
  int splitT = formattedDate.indexOf("T");
  dayStamp = formattedDate.substring(0, splitT);
  Serial.print("DATE: ");
  Serial.println(dayStamp);
  // Extract time
  timeStamp = formattedDate.substring(splitT + 1, formattedDate.length() - 1);
  Serial.print("HOUR: ");
  Serial.println(timeStamp);


  digitalWrite(irLEDs, HIGH); // turn on LEDs
  Serial.println("LEDs on");
  delay(1000);

  Serial.println("Taking Picture");
  camera_fb_t * fb = NULL;

  // Take Picture
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
  // If Error generate textfile
  String path = "/error_taking_pic" + (dayStamp) + "-" + (timeStamp) + ".inf";

  // Write Picture to FTP
  ftp.OpenConnection();
  delay(500);
  ftp.InitFile("Type I");
  ftp.NewFile(path.c_str());
  ftp.WriteData( fb->buf, fb->len ); 
  ftp.CloseFile();
  ftp.CloseConnection();
    return;
  }

  digitalWrite(irLEDs, LOW); // turn on LEDs
  Serial.println("LEDs off");

  // Filename generation
  String path = "/date_" + (dayStamp) + "_time_" + (timeStamp) + ".jpg";

  ftp.OpenConnection();
  delay(500);
  ftp.InitFile("Type I");
  ftp.NewFile(path.c_str());
  ftp.WriteData( fb->buf, fb->len );
  ftp.CloseFile();
  ftp.CloseConnection();
  Serial.printf("Picture file name: %s\n", path.c_str());

  esp_camera_fb_return(fb);

  delay(1000);

  rtc_gpio_hold_en(GPIO_NUM_4);

  SleepMicros = SleepSeconds * SleepMultiplier;

  esp_sleep_enable_timer_wakeup(SleepMicros);

  Serial.print("Going to sleep for ");
  Serial.print(SleepSeconds);
  Serial.print(" seconds, good night!");

  delay(1000);
  esp_deep_sleep_start();
  Serial.println("This will never be printed, so sad");
}

void loop()
{
}

Um das aktuellste Bild anzugeigen nutze ich ein PHP-Skript:

<link rel="stylesheet" type="text/css" href="style.css">

<?php
// Wo sind die Bilder
$folder = '.';

// Alle Dateien im Verzeichnis auslesen
$files = scandir($folder);

// Erstellen eines Arrays für JPGs
$jpg_files = array();

// Alle Dateien im Verzeichnis nach jpg durchsuchen
foreach ($files as $file) {
  // Überprüfen, ob ".jpg"
  if (pathinfo($file, PATHINFO_EXTENSION) == 'jpg') {
    // Hinzufügen der Datei zum Array
    $jpg_files[] = $file;
  }
}

// Sortieren der jpg-Dateien (neueste zuerst)
rsort($jpg_files);

echo '<div class="center-image">';
// Erste Datei verwenden, um das aktuellste Bild anzuzeigen
$latest_image = $folder . '/' . $jpg_files[0];
//echo '<img src="' . $latest_image . '" alt="Latest image" width="500px" id="latest-image">';
echo '<a href="' . $latest_image . '" target="_blank"> <img src="' . $latest_image . '" alt="Latest image" width="90%" id="latest-image"></a>';

echo '</div>';
echo '<div class="thumbnail-text">'.$jpg_files[0].'</div>';
echo '<div class="thumbnail-text">'.$jpg_files[imageUrl].'</div>';
?>

Ähnliche Beiträge

3 thoughts on “Der Nistkasten

  1. Hallo und guten Tag.
    Vielen Dank für die Veröffentlichung.
    Ich habe den Cide zum laufen gebracht und die Bilder werden in meinem Web Speicherort angezeigt.
    Mich würde nun interessieren, wie man immer das aktuellste Bild auf die Homepage bekommt.
    Für etwas Hilfe wäre ich sehr dankbar.
    Gruß Toby

    1. Hallo, ich habe das entsprechende php-Skript an den Beitrag angehängt. Das skript muss im gleichen Verzeichnis wie die Bilder liegen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Seite ist durch reCAPTCHA und Google geschützt Datenschutz-Bestimmungen und Nutzungsbedingungen anwenden.

Der Zeitraum für die reCAPTCHA-Überprüfung ist abgelaufen. Bitte laden Sie die Seite neu.