Satellite: Raspberry Pi
Deploy a Raspberry Pi as a BirdNET-NG satellite node for continuous bird audio capture.
Platform: Raspberry Pi — a dedicated, always-on Node.js agent that captures audio via ALSA and uploads to the hub over MQTTS.
Hardware Requirements
- Raspberry Pi 3B+ or newer (4 recommended)
- USB microphone or I2S MEMS microphone (e.g., Adafruit SPH0645)
- SD card (16GB+)
- Power supply
- Optional: GPS module (e.g., u-blox NEO-6M, Adafruit Ultimate GPS, BN-880)
Software Setup
1. Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt install -y nodejs
2. Install audio tools
sudo apt install -y alsa-utils
arecord -l # List audio devices
arecord -D plughw:1,0 -f S16_LE -r 48000 -c 1 -d 3 test.wav
aplay test.wav # Verify recording works
3. Register the satellite
Use the web UI to register, or via API:
curl -X POST https://birdnet.example.com/api/satellites \
-H "Authorization: Bearer <jwt-token>" \
-H "Content-Type: application/json" \
-d '{"tenantId":"<uuid>","name":"Garden Pi","latitude":43.56,"longitude":3.90}'
Save the returned id, mqtt.username, and mqtt.password.
4. Configure and run
git clone https://github.com/birdnet-ng/birdnet-ng.git
cd birdnet-ng
pnpm install && pnpm build
cd packages/satellite
cat > .env << 'EOF'
SATELLITE_ID=<from registration>
TENANT_ID=<your tenant>
MQTT_BROKER_URL=mqtts://mqtt.birdnet.example.com:8883
MQTT_USERNAME=<satellite id>
MQTT_PASSWORD=<from registration>
CAPTURE_MODE=alsa
AUDIO_DEVICE=plughw:1,0
LATITUDE=43.56
LONGITUDE=3.90
RECORDING_PROFILE=continuous
EOF
node dist/index.js
5. Run as a systemd service
sudo tee /etc/systemd/system/birdnet-satellite.service << 'EOF'
[Unit]
Description=BirdNET-NG Satellite
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/birdnet-ng/packages/satellite
EnvironmentFile=/home/pi/birdnet-ng/packages/satellite/.env
ExecStart=/usr/bin/node dist/index.js
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now birdnet-satellite
GPS Setup
For live GPS positioning instead of static coordinates:
sudo apt install -y gpsd gpsd-clients
Edit /etc/default/gpsd:
DEVICES="/dev/ttyUSB0" # or /dev/serial0 for GPIO UART
GPSD_OPTIONS="-n"
START_DAEMON="true"
sudo systemctl enable --now gpsd
cgps # Verify GPS fix
Add to .env:
GPS_MODE=gpsd
GPSD_HOST=localhost
GPSD_PORT=2947
When GPS mode is gpsd, the satellite reads live coordinates from the GPS daemon and updates the hub via telemetry. Falls back to static coordinates if GPS has no fix.
Recording Profiles
Profiles control when the satellite captures audio. They are pushed from the hub via MQTT and can be changed on the Satellites page.
| Profile | Schedule | Description |
|---|---|---|
continuous |
24/7 | Always recording |
dawn_chorus |
sunrise-30min to sunrise+2h | Peak bird activity |
night_migration |
sunset+30min to sunset+12h | Nocturnal flight calls |
low_power |
sunrise+/-30min, sunset+/-30min | Dawn and dusk only |
Sunrise and sunset times are calculated automatically from the satellite's GPS coordinates using the NOAA solar algorithm. Times update daily as day length changes with seasons.
When outside a recording window, the satellite:
- Pauses the capture loop (no CPU/disk usage)
- Continues sending heartbeats (stays "online" on the hub)
- Reports state as
scheduled_offin the heartbeat - Re-checks the schedule every 5 minutes
Heartbeat
The satellite sends a lightweight heartbeat at a configurable interval (default 30 seconds, set from hub tenant settings) via MQTT QoS 1. This keeps the satellite showing as "online" on the hub even when all audio chunks are filtered out (silence).
The heartbeat includes the satellite's current state (recording, scheduled_off, error) and noise_floor_rms for filter calibration, visible on the Satellites page.
On-Device Audio Filtering
The satellite uses adaptive noise floor + spectral peak SNR detection to filter audio before upload:
- Adaptive noise floor: tracks ambient noise level over time (smoothing alpha: 0.02)
- Minimum RMS energy: rejects silence below threshold (default 0.003)
- Spectral peak SNR: requires signal-to-noise ratio above threshold (default 1.5)
- Bird-band frequency check: confirms energy in the 1-10kHz bird frequency range
All filter settings are configurable from hub tenant settings (filter_enabled, filter_min_rms, filter_peak_snr, filter_bird_band_check, filter_noise_floor_alpha) and pushed to satellites via the MQTT config channel when changed. Satellites also apply the latest settings on reconnect.
The satellite reports noise_floor_rms in heartbeat telemetry for monitoring.
This typically filters 70-90% of chunks, reducing bandwidth and hub processing load. Disable with filter_enabled=false in tenant settings (or AUDIO_FILTER=false in the satellite .env as a local override).
Filtering is automatically disabled for simulate and replay capture modes.
Troubleshooting
| Symptom | Check |
|---|---|
| Satellite shows offline | Verify MQTT credentials, check journalctl -u birdnet-satellite |
| No detections | Check if all chunks are filtered (low audio activity), verify worker is running |
| GPS unavailable | Check cgps for fix, verify /dev/ttyUSB0 permissions |
| High CPU temp | Normal for Pi 4 under load; add heatsink or fan |
| Storage filling up | Increase RETENTION_HOURS or check outbox drain |