Build a bird feeder that counts its visitors

Print a window bird feeder with a sensor perch, count every visit with an infrared sensor and log visits and temperature to Google Sheets for a citizen-science chart.
  • Level:Advanced
  • Duration: 2 h 30 min
  • Ages 12+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build a bird feeder that counts its visitors – EEMENTOR STEM™ lesson

Scientists count birds to learn how nature is changing, and anyone can help: this is called citizen science. In this capstone you build a feeder that counts its own visitors. An infrared obstacle sensor watches the perch; when a bird lands, the ESP32 counts a visit and writes it into a Google Sheet with the time and the temperature from the DHT11. After a week you can see which hours are busiest and whether birds come more often when it is cold.

What you'll learn

  • How to count separate visits instead of every sensor flicker
  • How to log events with a time stamp to Google Sheets
  • How to design a printed part around a sensor's field of view
  • How to plan an outdoor project: power, weather and animal safety

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • IR obstacle avoidance sensor module (45-in-1 Sensor Kit)
  • DHT11 temperature and humidity sensor (ESP32 IoT Starter Kit)
  • 3 long female-to-male jumper wires for the sensor (or a 4-core cable)
  • A free Google account (ask a parent or teacher if you are under 13)
  • A 2.4 GHz Wi-Fi network that reaches the window
  • Bird seed, string and a USB charger or power bank
  • The printed parts from the 3D printing box below (bird feeder), or the cardboard version described there
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)

Wiring

Breadboard wiring diagram: Build a bird feeder that counts its visitors
Breadboard view. Check each connection against the wiring table below.
From To
Obstacle sensor OUT ESP32 GPIO 27
Obstacle sensor + and GND 3V3 and GND
DHT11 S (signal) ESP32 GPIO 26
DHT11 + and − 3V3 and GND

Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.

Prepare the spreadsheet

  1. Create a blank spreadsheet at sheets.google.com called Bird visits, and rename its first tab to Readings.
  2. Type Time, Visit, Seconds and Temperature in the first row.
  3. Open Extensions → Apps Script, replace the code with the script below and change YOUR_SECRET_WORD to a word only you know (letters and numbers only).
  4. Click Deploy → New deployment → Web app, set Execute as: Me and Who has access: Anyone, deploy, allow access and copy the Web app URL (it ends in /exec). The Google Sheets lesson shows each step in detail.
const SECRET = 'YOUR_SECRET_WORD';

function doGet(e) {
  if (e.parameter.key !== SECRET) return ContentService.createTextOutput('wrong key');
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Readings');
  sheet.appendRow([new Date(), Number(e.parameter.visit), Number(e.parameter.seconds), Number(e.parameter.t)]);
  return ContentService.createTextOutput('OK');
}

The sketch

Replace YOUR_WIFI_NAME and YOUR_WIFI_PASSWORD with your own network details (2.4 GHz only).

Paste your web app URL and secret word, and install the DHT sensor library by Adafruit.

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <DHT.h>

const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* SCRIPT_URL = "https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec";
const char* SECRET = "YOUR_SECRET_WORD";

const int SENSOR_PIN = 27;              // LOW when something is on the perch
const unsigned long MIN_VISIT = 300;    // ignore flickers shorter than 0.3 s
const unsigned long GONE_AFTER = 5000;  // the perch must be empty 5 s before a new visit

DHT dht(26, DHT11);
unsigned long visits = 0;
bool onPerch = false;
unsigned long arrived = 0, lastSeen = 0;

bool sendToSheet(const String& query) {
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;
  http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);   // Google answers with a redirect first
  http.begin(client, String(SCRIPT_URL) + "?key=" + SECRET + "&" + query);
  int code = http.GET();
  String answer = http.getString();
  http.end();
  Serial.printf("Google answered %d: %s\n", code, answer.c_str());
  return code == 200 && answer == "OK";
}

void setup() {
  Serial.begin(115200);
  pinMode(SENSOR_PIN, INPUT);
  dht.begin();
  WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" connected. Watching the perch...");
}

void loop() {
  bool seen = digitalRead(SENSOR_PIN) == LOW;
  unsigned long now = millis();

  if (seen) {
    if (!onPerch && arrived == 0) arrived = now;        // something just landed
    if (!onPerch && now - arrived >= MIN_VISIT) {       // ...and stayed: a visit
      onPerch = true;
      visits++;
      Serial.printf("Visit %lu\n", visits);
    }
    lastSeen = now;
  } else if (!onPerch) {
    arrived = 0;                                        // only a flicker
  }

  if (onPerch && now - lastSeen > GONE_AFTER) {         // the bird has left
    onPerch = false;
    unsigned long seconds = (lastSeen - arrived) / 1000;
    arrived = 0;
    float t = dht.readTemperature();
    String query = "visit=" + String(visits) + "&seconds=" + String(seconds) + "&t=" + (isnan(t) ? String("") : String(t, 1));
    if (WiFi.status() == WL_CONNECTED) sendToSheet(query);
    else WiFi.reconnect();
  }
  delay(20);
}

Build it in stages

  1. Indoors first: adjust the obstacle sensor's screw so it sees your finger on the perch but not the seed tray behind it.
  2. Spreadsheet: put your finger on the perch for a few seconds, take it away, and wait 5 seconds. A row appears with the visit number, the seconds and the temperature.
  3. Outdoors: hang the feeder and let it run for a day. Birds may need a few days to find a new feeder.
  4. Analyse: make a chart of visits per hour (Insert → Pivot table, rows = hour of the Time column, values = COUNTA of Visit).

How it works

An obstacle sensor flickers when leaves move or a bird hops along the perch. Counting every flicker would give hundreds of "visits". The sketch counts a visit only when something stays at least 0.3 s, and treats the visit as over only when the perch has been empty for 5 s. Short gaps in between are ignored. This kind of logic, with a minimum time to start and a timeout to end, is how real wildlife counters and people counters in shops work.

Each finished visit becomes one row in the sheet: the time comes from Google, so the ESP32 doesn't need a clock. With the temperature in every row, you can later check whether cold days bring more visitors.

client.setInsecure() keeps the connection encrypted but skips checking the server's certificate. That keeps the sketch short and is fine for a learning project. For a device you leave running for years, load the server's root certificate with client.setCACert() instead.

Try this next

  • Add the light level (photoresistor) to see when birds start feeding in the morning.
  • Weigh the seed every morning and compare the weight eaten with the number of visits.
  • Share your data with a bird-count project such as eBird or your country's garden bird survey.

Troubleshooting

  • Lots of visits with no birds: the sensor sees the tray, leaves or sunlight. Turn its screw anticlockwise and shade it.
  • Visits never end: something stays in front of the sensor; check nothing is stuck on the perch.
  • No rows in the sheet: test the web app URL in a browser first, as in the Google Sheets lesson.
  • The ESP32 resets outdoors: long wires and cold power banks can drop the voltage; keep the ESP32 indoors and run only the sensor outside.
  • It never connects to Wi-Fi: check the network name and password (both are case-sensitive) and make sure the network is 2.4 GHz.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit