Skip to content

Dltd/OnlyFeathers

Repository files navigation

OnlyFeathers

A ESP32-Cam birdhouse camera project OnlyfeathersLogo

To learn some more about esp32 I wanted to setup a little birdnest cam. I added an external antenna for better WiFi coverage as well.

ProjectCollagePicture

I used an esp32-cam and designed a mount for it that also acts as a infrared light diffuser to light the birdhouse for pictures or video's.

As it appeared, it became already popular on the first day by a returning Great Tit. You can see it inspecting the cabling and protection in place. Visitor

For the esp32-cam I used the program here which is an improved version of the default example program for esp32-cam webserver. It works great and is better compared to the example program.

I used a bright 940nm IR LED, which I connected to the original SMD flash LED that comes with the board. It was not hard to pry off the led and use a soldering iron and desolder thread to make it nice and clean again.

finding it in several networks

I set it up to update the ip to a webserver that will receive the private IP address. It was added because I configured multiple SSID's as redundancy which have their own subnets, this way I can always find it, regardless of the network it has connected to.

If it does not connect to WiFi, the first SSID set in the myconfig.h file will be setup as an accesspoint on the cam.

Change this codesnippet to make it update the IP on a webserver when it has internet connectivity.

// If we have connected, inform user
        if (WiFi.status() == WL_CONNECTED) {
            Serial.println("Client connection succeeded");
            accesspoint = false;
            // Note IP details
            ip = WiFi.localIP();
            net = WiFi.subnetMask();
            gw = WiFi.gatewayIP();
            Serial.printf("IP address: %d.%d.%d.%d\r\n",ip[0],ip[1],ip[2],ip[3]);
            Serial.printf("Netmask   : %d.%d.%d.%d\r\n",net[0],net[1],net[2],net[3]);
            Serial.printf("Gateway   : %d.%d.%d.%d\r\n",gw[0],gw[1],gw[2],gw[3]);
            calcURLs();
            // Flash the LED to show we are connected
            //for (int i = 0; i < 5; i++) {
            //    flashLED(50);
            //    delay(150);

            // log IP 
            ip = WiFi.localIP();

            String url = "https://YOURWEBSERVER/updateIPhere.php"; // Replace with your actual URL
            String postData = "ip=" + ip.toString(); // Adjust the parameter name as needed
            
            HTTPClient http;
            http.begin(url);
            http.addHeader("Content-Type", "application/x-www-form-urlencoded");
            int httpResponseCode = http.POST(postData);
            if (httpResponseCode == HTTP_CODE_OK) {
                Serial.println("IP address sent successfully");
            } else {
                Serial.printf("Error sending IP address (HTTP code %d)\n", httpResponseCode);
            }

           }
        else {
            Serial.println("Client connection Failed");
            WiFi.disconnect();   // (resets the WiFi scan)
        }
    }

You will need some php similar to this to receive the data:

<?php
// thisistheipofthecam.php

// Check if the request method is POST
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // Retrieve the raw POST data (including all parameters)
    $rawData = file_get_contents("php://input");

    // Parse the raw data into an associative array
    parse_str($rawData, $postData);

    // Extract the relevant values (ip and timestamp)
    $ip = $postData["ip"];
    $timestamp = $postData["timestamp"];

    // Log the extracted data (you can customize the log file path)
    $logFilePath = "esp32-cam_IP.log";
    file_put_contents($logFilePath, "$timestamp $ip\n", FILE_APPEND);

    // Respond with a success message
    echo "Received data successfully";
} else {
    // Respond with an error message if the request method is not POST
    http_response_code(400); // Bad Request
    echo "Invalid request method";
}
?>

After it was put back on the shed, it had a new inhabitant the next day! Which makes it all worth the effort of course.

A Raspberry Pi is used to take pictures every 20 seconds, which can be converted to a timelapse video. For nightvision, it will turn on the LED and let the camera adjust itself to it for 4 seconds to take a picture and turn off the LED again. The script is scheduled in cron to execute every minute

I use this script to make it do that:

#!/usr/bin/bash

# Set the default shell and path for the script.
SHELL=/bin/sh PATH=/bin:/sbin:/usr/bin:/usr/sbin

# Log the timestamp of execution
echo "$(date) - Executing script" >> /var/log/onlyfeathers.log

# Loop 3 times to perform the actions below.
for ((i=0;i<3;i++)); do

        # Turn on the infrared light before taking the screengrab
        curl "http://onlyfeathers.local/control?var=lamp&val=95"
        # Sleep to let the camera adjust itself
        sleep 4
        # Download an image from the new host URL and save it with a timestamped filename.
        wget http://onlyfeathers.local/capture -O "/home/onlyfeathers/timelapsepics/only_feathers_$(date +%Y-%m-%d_%H-%M-%S).jpg"
        # Turn off the infrared light again
        sleep 1
        curl "http://onlyfeathers.local/control?var=lamp&val=0"

        # Find the most recently downloaded image.
        latest=$(ls /home/onlyfeathers/timelapsepics/only_feathers_*.jpg -rt1 | tail -1)
        # Extract the filename without the extension for watermarking.
        Watermarktext=$(basename "$latest" .jpg | sed 's/only_feathers_//')

        # Get the size of the latest image.
        ps=$(wc -c <"$latest")
        # Set a minimum size threshold.
        mn=7000

        # If the image is smaller than the threshold, adjust camera settings and retake the image.
        if [ $ps -le $mn ]; then
          curl "http://onlyfeathers.local/control?var=framesize&val=9"
          curl "http://onlyfeathers.local/control?var=raw_gma&val=0"
          curl "http://onlyfeathers.local/control?var=hmirror&val=0"
          curl "http://onlyfeathers.local/control?var=vflip&val=0"
          curl "http://onlyfeathers.local/control?var=dcw&val=0"
          sleep 5.0
          # Retake the image after the settings are adjusted.
          wget http://onlyfeathers.local/capture -O "/home/onlyfeathers/timelapsepics/only_feathers_$(date +%Y-%m-%d_%H-%M-%S).jpg"
          latest=$(ls /home/onlyfeathers/timelapsepics/only_feathers_*.jpg -rt1 | tail -1)
        fi

        # Apply a small watermark text to the image for the timestamp.
        Fontsize=10
        Font="Arial.ttf"
        Fontcolour="white"
        horizontal=$(identify -format "%w" "$latest")
        vertical=$(identify -format "%h" "$latest")
        Textdistancefromleft=10
        Textdistancefrombottom=10
        X=$Textdistancefromleft
        Y=$((vertical - Textdistancefrombottom))
        convert -quality 50 -pointsize $Fontsize -fill $Fontcolour -draw "text $X, $Y '$Watermarktext'" "$latest"  "$latest"

        # Copy the watermarked image to the web directory.
        cp $latest /var/www/html/only_feathers.jpg

        # Wait for 17.6 seconds before the next iteration of the loop.
        sleep 17.6
done
# Exit the script.
exit

Here are some video's of the building process and here are some pictures as well.

About

A ESP32-Cam birdhouse camera project

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published