EMSN
Nijverdal · Field journal

Type minstens twee letters. Resultaten verschijnen vanzelf, geen Enter nodig.

NL
Back to homepage
§ T-01 · Technology
Under the hood

Four Pis, a Synology
and a few thousand lines of code.

A tour through what lives where, and why. The Raspberry Pis under the gutter, the NAS in the shed, the models that recognise species, the plumbing that ties it all together. Not everything worked on day one. There's a story behind every line below.

§ T-02 · In one picture From a singing blackbird to this website

Seven steps between the bird and the screen.

A blackbird sings, a microphone picks it up, hooked into a Raspberry Pi running BirdNET, the NAS in the shed, a Cloudflare tunnel, and only then does this site show anything. Detail per component below.

Infographic: from a singing blackbird via microphone, Raspberry Pi, AI detection and NAS to this website.
§ T-03 · The Pi fleet Which computer does what

Four Pis and a Synology.

Each Pi has a single job. The NAS is the hub. PostgreSQL for the data, Grafana for the dashboards, a tunnel to the outside world. Mobiel sits apart: it runs on battery, out in the field.

  • emsn2-zolder

    192.168.x.x

    Main station: BirdNET-Pi + MQTT broker + reports API

    Hardware
    Raspberry Pi 5 (Rev 1.1)
    RAM
    8 GB
    OS
    Debian 13 trixie
    Sensors
    Stereo USB mic (North/South, 7m baseline)
    ~262 detections / hour
  • emsn2-berging

    192.168.x.x

    BirdNET-Pi + AtmosBird sky cam + MQTT bridge

    Hardware
    Raspberry Pi 5 (Rev 1.1)
    RAM
    8 GB
    OS
    Debian 13 trixie
    Sensors
    USB mic mono + HQ Camera IMX477 + M12 2.7mm F2.5
    ~307 detections / hour
  • emsn2-meteo

    192.168.x.x

    Weather gateway to Davis Vantage Pro 2

    Hardware
    Raspberry Pi Zero 2 W
    RAM
    512 MB
    OS
    Debian 12 bookworm
    Sensors
    Davis console: wind, temp, rain, humidity, pressure, UV
    ~57 readings / hour
  • emsn-sonar

    192.168.x.x

    Bat detection, dual stack

    Hardware
    Raspberry Pi 4B (Rev 1.5)
    RAM
    8 GB
    OS
    Debian 13 trixie
    Sensors
    Dodotronic Ultramic 200 kHz (ultrasonic)
    24 bat species in archive
  • emsn-mobiel

    192.168.x.x

    Field station (separate project, battery)

    Hardware
    Raspberry Pi 5 + Witty Pi 5
    RAM
    8 GB
    OS
    Debian 13
    Sensors
    USB mic + GPS (UART /dev/serial0)
    75 species from field sessions
  • NAS DS224+

    192.168.x.x

    PostgreSQL + Grafana + go2rtc + USB archive

    Hardware
    Synology DS224+
    RAM
    OS
    DSM
    Sensors
    4.0 TB of 7.3 TB used
§ T-04 · Pipelines How a reading travels from sensor to database

Six data streams, one database.

Each stream has its own path: different sensor, different processing script, its own buffer. But they all end up in the same PostgreSQL on the NAS.

01

Weather

259,124 records · 252 days
Davis Vantage Pro 2 (radio)
    │
    ▼
Davis console, USB serial
    │
    ▼
Pi Meteo: davis-weather-monitor.service
    │
    ▼
MQTT publish emsn2/meteo/weather/*
    │
    ▼
Mosquitto broker on Zolder
    │
    ▼
weather_ingest service
    │
    ▼
▶ PostgreSQL.weather_data

The outdoor sensors transmit to the Davis console by radio every minute. A Pi Zero reads that console over USB, publishes to MQTT, and the NAS writes the data away.

02

Acoustics (birds)

1,359,993 records · 189 days
USB microphone (48 kHz)
    │
    ▼
BirdNET-Pi: rolling 15-sec buffer + analyser
    │
    ▼
Local SQLite + MP3 clip + spectrogram
    │
    ▼
lifetime_sync.py (event-driven)
    │
    ▼
PostgreSQL.bird_detections
    │
    ▼
unified-enricher: vocalisation CNN + direction
    │
    ▼
▶ NAS USB archive (159 GB audio)

Each microphone listens 24/7. BirdNET-Pi on the Pi itself identifies the species, writes a small clip, and after that a custom CNN enriches the vocalisation and direction (only Zolder, which is stereo).

03

Bats

17,216 records · 61 days
Dodotronic Ultramic (192 kHz)
    │
    ▼
sonar-bavaria.service: dual stack
    │
    ▼
├─ BatDetect2 (PyTorch, UK-trained)
    │
    ▼
└─ BattyBirdNET-Bavaria (TF Lite, DE/NL)
    │
    ▼
Ensemble merge: store both
    │
    ▼
sonar-monitor.service via MQTT
    │
    ▼
▶ PostgreSQL.bat_detections

Two detectors run in parallel because they complement each other. BatDetect2 is strong on UK species, Bavaria on central-European ones. We do not force them to agree on a single answer.

04

Nest boxes

263,568 records · 161 days
3× Tuya nest-box camera
    │
    ▼
Tuya cloud (protect-eu.ismartlife.me)
    │
    ▼
go2rtc: pull RTSP, republish WebRTC
    │
    ▼
Auto-screenshot 6× per day (incl. night)
    │
    ▼
Pi Zolder reports-api → POST nestbox_media
    │
    ▼
PostgreSQL.nestbox_media + .nestbox_events
    │
    ▼
▶ NAS USB archive + dashboard

Cheap Chinese cameras with no open API. An open-source streaming gateway (go2rtc) signs into the Tuya cloud and republishes the stream locally. That way every frame lands in a database instead of in an app.

05

AtmosBird sky cam

21,655 records · 172 days
HQ Camera IMX477 + M12 wide-angle
    │
    ▼
atmosbird-stream-relay: MJPEG broker :8889
    │
    ▼
atmosbird-capture.timer: every 10 min
    │
    ▼
atmosbird-analysis.timer: every 15 min
    │
    ▼
   ONNX cloud classifier + ISS + moon + meteor
    │
    ▼
atmosbird-archive-sync.timer: hourly
    │
    ▼
▶ PostgreSQL.sky_observations + USB

The sky camera shoots upward every 10 minutes. A self-trained classifier says whether the sky is overcast, and in parallel the same script scans every frame for ISS passes, the moon, and meteors.

06

FlySafe radar

2,043 records · 172 days
KNMI BirdTAM (PNG, no API)
    │
    ▼
HTTP fetch every 30 min
    │
    ▼
flysafe-radar: colour analyser
    │
    ▼
Pixel classification into 5 levels
    │
    ▼
Extraction around EMSN coordinates
    │
    ▼
correlation_service: time-match with detections
    │
    ▼
▶ PostgreSQL.radar_observations

KNMI publishes bird-migration data as PNG maps only, no download API. A custom colour analyser reads the palette and converts it back into an intensity number per region.

§ T-05 · Software stack What we use and why

No hero software, just choices with a reason.

Each component does one thing. Most of it is open source. Others have already built and trained the heavy machine learning, I wire it together and only train my own models where the off-the-shelf ones fall short.

Detection & classification

  • The de-facto standard for edge bird detection, native SQLite + MP3 export, low CPU on a Pi 5.

  • State of the art for bat spectrograms, trained on a large UK dataset.

  • Better on central-European species than BatDetect2. Hence the dual stack.

  • Generic classifiers identify the species but not what the bird is doing (song, call, alarm). Three generations of models, flagship is "ultimate": 4 conv blocks, 256 filters, 35 MB per species.

  • MobileNetV2 nest-box classifier

    Lightweight, runs on a Pi, ImageNet-based transfer learning is enough for "empty / occupied / eggs". Still in development.

  • FlySafe BirdTAM colour analyser

    KNMI only ships PNGs, no API. Pixel classification turns it back into numbers.

Infrastructure

  • One central source of truth. SQLite per Pi does not scale for cross-station queries.

  • Lightweight message bus, the bridge pattern has worked between the Pis for years. Recently mTLS on port 8883.

  • A single abstraction over Tuya, RTSP and WebRTC. Browser rendering without plugins.

  • Visualisation and alerts, PostgreSQL as native data source.

  • Centralised logs from all Pis. Saves me from SSH-ing into four hosts just to debug something.

  • Secrets management. The current .secrets files are a temporary patch.

  • systemd timers + services

    No cron, no Docker overhead on the Pis. Native, with WatchdogSec for freezes.

  • UPS, RTC, scheduled power-on for battery field sessions.

Cloud & archive

  • Cloudflare Tunnel

    My ISP blocks inbound ports 80/443. Cloudflare opens an outbound tunnel to their edge. No port forwarding, auto-TLS, auto-DNS.

  • Hosting for this site. Auto-deploys on git push, free for hobby projects.

  • HiDrive (Strato)

    Off-site backup of the database and Pis over SSH key. No API key, no vendor lock-in.

  • NAS USB 8 TB

    Primary archive. 1 Gbit at home is faster than the cloud and costs nothing per TB.

§ T-06 · What went wrong Four stories worth knowing

Silent bugs, hard lessons.

Nothing works first time. But the worst failures aren't crashes. They're pipelines that look like they're running while quietly writing the wrong data. Four examples.

  1. 01

    The silent bias of the sky classifier

    For two months the AtmosBird sky classifier returned "99% overcast" every 15 minutes. No exception, no alert. The cause: `onnxruntime` had dropped out of the venv, and the fallback heuristic always returned near-complete cloud cover. Since then, post-deploy smoke tests check specifically whether the real model loads.

  2. 02

    One wrong timer = 200,000 wrong records

    The `lifetime-sync-berging.timer` turned out to be running on the Zolder Pi by accident. The script synced the Zolder SQLite once as zolder and once as berging: 201,224 records with the wrong station tag. Fix: a hostname check inside the script, plus a single source of truth listing "which service belongs on which host".

  3. 03

    Numba and the ProtectHome trap

    The audio library librosa uses numba for JIT compilation and wants to cache under `~/.cache/numba/`. But hardened systemd services have `ProtectHome=read-only`. Result: numba silently falls back to pure Python. 50× slower, only visible in debug logs. Nine days passed with no real classification before we caught it.

  4. 04

    Tuya cameras and the wonders of go2rtc

    The nest-box cameras are cheap Tuya devices with no RTSP or ONVIF. The clean solution would have been: buy different cameras. But go2rtc has a Tuya driver that signs in with your account and republishes the cloud stream locally. Suddenly every frame can land in a database. A Pi talks to a NAS that talks to a Chinese cloud that talks to a nest box in the front garden.

§ T-07 · Final figures What the whole setup has produced so far

All from the database, nothing estimated.

These numbers refresh through a read-only endpoint on the NAS. No intermediate step, no edit-deploy: look at them again in a week and they'll have grown.

Weather
259,124
Davis VP2
23 September 2025 · 252 days
Birds
1,359,993
BirdNET on 2 stations
25 November 2025 · 189 days
Bats
17,216
BatDetect2 + Bavaria
2 April 2026 · 61 days
Sky
21,655
AtmosBird HQ camera
12 December 2025 · 172 days
Nest boxes
263,568
3 Tuya cams
23 December 2025 · 161 days
Radar
2,043
KNMI FlySafe
12 December 2025 · 172 days
Audio archive on USB ~241 GB MP3 + 447 GB spectrograms
Self-trained models ~13 GB · 776 .pt files
Disaster recovery (SD images) ~1.9 TB
PostgreSQL DB itself ~2.8 GB metadata
§ T-08 · Code and sources Where the work lives

Almost everything is open source. The website runs on Astro, the data API is FastAPI on the NAS, the Pi scripts are in their own repos. Nothing has been polished for display. It's working code that runs, but it's there to read.

The figures on this page come from a single PostgreSQL query under 50 milliseconds on the NAS itself, cached for an hour. Visits are counted anonymously and without cookies using Umami (self-hosted) and Vercel Web Analytics. No ads, no tracking cookies, no profiles. More on the privacy page.