An AI bird feeder that names every visitor by sight and by song
A Raspberry Pi at the window with a camera on the feeder and a microphone in the garden. One AI model identifies birds from their song, a second names them from a photo, and both land on one dashboard. When a first-ever visitor or a rarity turns up, your phone buzzes with its picture. Both models run on your PC in step 1, before you buy a thing.
- One dashboard merging what was seen and what was heard today, species by species
- A kit: camera script, model downloader, UK species list, dashboard, BirdNET-Go settings and systemd services
- A Raspberry Pi 5 4GB (£105.60) with the official 27W PSU (£11.50), active cooler and a 64GB microSD card
- Raspberry Pi Camera Module 3, standard lens (£24), plus a 300mm Pi 5 camera cable
- A USB lavalier microphone such as the Boya BY-LM40 (around £20 to £25)
- A window or pole feeder you can put 30 to 60cm from the camera
- Comfortable with SSH and a terminal: Terminal survival kit. Docker basics help: Docker without fear
Two AI brains, tested on your PC first
See both models identify real birds on your own computer before any hardware arrives.
Ears: BirdNETBuilt by the Cornell Lab of Ornithology and Chemnitz University of Technology, BirdNET listens in 3-second slices and recognises more than 6,000 species by their songs and calls. [BirdNET-Go](https://github.com/tphakala/birdnet-go) wraps it in a fast, always-on service with a web dashboard, clip recording, species tracking and push notifications.
Eyes: Google's AIY birds classifierA 3.4MB TensorFlow Lite model that names 965 species from a photo, every common UK garden bird included. It's small enough to run in tens of milliseconds on a Pi, so the camera can check every visit.
The kitDownload the kit or open kit.md. Make a folder, put the kit files in it, then set up a Python environment and fetch the model:
Windows (PowerShell)uv venv --python 3.12 birdnet-env birdnet-env\Scripts\activate uv pip install birdnet==1.1.1 ai-edge-litert pillow numpy python get_bird_model.pyMac / Linuxuv venv --python 3.12 birdnet-env source birdnet-env/bin/activate uv pip install birdnet==1.1.1 ai-edge-litert pillow numpy python get_bird_model.pyTest the eyesSave a few bird photos (your own, or Wikimedia Commons) and run the camera script in photo mode:
Terminalpython feeder_cam.py --image robin.jpg blue_tit.jpg goldfinch.jpg chaffinch.jpg nuthatch.jpg great_spotted_woodpecker.jpg magpie.jpgOutputfile top guess score runner-up blue_tit.jpg Blue tit 0.95 Great tit 0.01 (43 ms) chaffinch.jpg Chaffinch 0.92 Hawfinch 0.00 (67 ms) goldfinch.jpg Goldfinch 0.94 Thraupis abbas 0.00 (14 ms) great_spotted_woodpecker.jpg Great spotted woodpecker 0.91 Lophodytes cucullatus 0.00 (14 ms) magpie.jpg Magpie 0.68 Pica nuttalli 0.11 (18 ms) nuthatch.jpg Nuthatch 0.92 Sitta canadensis 0.02 (14 ms) robin.jpg Robin 0.64 Blackbird 0.04 (14 ms)Test the earsGrab a garden recording (your phone's voice recorder by an open window at dawn is perfect, or a recording from xeno-canto ↗) and run the same model BirdNET-Go uses:
Terminalpython hear_file.py garden.wav --min 0.7Outputfrom to species conf 00:00.00 00:03.00 Great Tit 0.98 00:09.00 00:12.00 Great Tit 0.94 00:27.00 00:30.00 Great Tit 0.84 00:30.00 00:33.00 European Robin 0.99 00:36.00 00:39.00 European Robin 0.93 00:57.00 01:00.00 Eurasian Blackbird 0.97 01:15.00 01:18.00 Eurasian Blackbird 0.96 7 detections at >= 0.7 (17 weaker guesses dropped)That is an 84-second clip of a great tit, a robin and a blackbird, and BirdNET names all three. Run it with
--min 0.1and you'll see why the threshold matters: the 17 weak guesses include a Cassin's Finch and a Plumbeous Rail, birds that have never been near a British garden. Step 4 adds a location filter that removes those for good.
feeder_cam.py --image names your birds with scores, and hear_file.py lists species from your recording.If it breakshear_file.py hangs on Windows + 1 more
hear_file.py hangs on Windows
python hear_file.py ...), not pasted into an interactive prompt. BirdNET starts worker processes, which on Windows only works from a file with the if __name__ == "__main__" guard the kit script already has.