Vogelalbum 1.0.0

Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine
Sammlung, die sich von allein fuellt.

Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die
Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert —
ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische
Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten,
gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte,
Stufen und 15 Abzeichen.

Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto,
keine Telemetrie; die Bilder verlassen das eigene Netz nicht.

Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die
Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und
braucht deshalb weder eine erreichbare Adresse noch einen offenen Port —
er laeuft hinter einem gewoehnlichen Router.

Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home
Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen
je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs.

AGPL-3.0.
This commit is contained in:
christian committed 2026-10-15 14:30:02 +00:00
commit c11fe9ab04
189 files changed
+29806

No files matched your search

+164
View File
@@ -0,0 +1,164 @@
# Vogelkamera auf ESP32-S3
Kameraseite für Vogelalbum auf einem ESP32-S3 mit Kameramodul — das Gegenstück zu
[`../pi/`](../pi/README.md). Gleiche Pfade und gleiches JSON-Schema: der
Detector merkt nicht, ob ein Pi Zero oder ein ESP32 am anderen Ende hängt.
http://<kamera>:8080/stream MJPEG, ein Zuschauer
http://<kamera>:8080/ Vorschauseite
http://<kamera>:8081/status JSON: fps, RSSI, Temperatur, Laufzeit
http://<kamera>:8081/temp Chiptemperatur als Text
http://<kamera>:8081/gesund 200 oder 503
🔑 **Die Diagnose liegt auf einem eigenen Port — das ist der eine Unterschied
zum Pi.** Der Webserver im ESP32 arbeitet Anfragen mit *einem* Faden ab, und
der Stream gibt den nie zurück. Solange jemand zuschaut, käme auf 8080 niemand
mehr an `/status` — also genau dann nicht, wenn man es braucht. Am 23.09.2026
wirkte die Kamera deshalb tot, während sie brav streamte. Seitdem hat die
Diagnose ihren eigenen Port und ihren eigenen Faden.
⚠️ **Bis zum 09.10.2026 stand hier für alles `8080`.** Wer die Zeile kopierte,
bekam eine Anfrage, die stumm ins Zeitlimit lief — dasselbe Bild wie eine tote
Kamera. Der Port ist `VK_PORT + 1`; wer im `menuconfig` den Hauptport ändert,
verschiebt beide.
## Warum ESP32 statt Pi
Am Vogelhaus ist die Bildrate **bandbreitenbegrenzt, nicht rechenbegrenzt**:
der Pi Zero W drückt über seine Chip-Antenne rund 2 Mbit/s durchs WLAN, und
jeder gemessene Einbruch hing an der Funkstrecke. Ein ESP32-S3-Board mit
**externer Antenne** greift genau da an — und kostet ein Vielfaches weniger,
was für einen Bausatz den Unterschied zwischen verkaufbar und nicht
verkaufbar macht.
## Hardware
Getestet wird gegen ein **ESP32-S3-CAM mit OV3660** (N16R8-Modul: 16 MB Flash,
8 MB PSRAM, externe Antenne). Die Firmware läuft aber auf jedem Board, dessen
Pinbelegung im `menuconfig` hinterlegt ist.
> ⚠️ **Das Funkmodul muss ein ESP32-S3-WROOM-1*U* sein** — das *U* steht für
> die Antennenbuchse. Bei manchen Klonen liegt eine Antenne bei, das Modul hat
> aber nur die Platinenantenne; dann ist das Kabel Deko.
> ⚠️ **OV5640 nicht verwenden.** Auf diesen Boards liefert er dokumentiert ein
> schwarzes Bild, und der Sensor überhitzt, weil die Versorgung für seinen
> internen 1,5-V-Regler zu hoch liegt. OV2640 und OV3660 laufen.
## Bauen und flashen
```sh
. ~/esp/esp-idf/export.sh
cd esp32cam
idf.py set-target esp32s3
idf.py menuconfig # Menü "Vogelkamera": WLAN, Board, Bildgröße
idf.py -p /dev/ttyACM0 flash monitor
```
Die WLAN-Daten stehen bewusst **nicht** im Repo — sie landen über
`menuconfig` in `sdkconfig`, und das ist per `.gitignore` draußen.
Zwei USB-C-Buchsen am Board: eine hängt am USB-Seriell-Wandler (CH340), die
andere am nativen USB des Chips. Findet `idf.py flash` nichts, ist es die
andere Buchse. Je nach Board muss zum Flashen BOOT gehalten und RESET kurz
gedrückt werden.
### Wenn die Kamera nicht anspringt
`esp_camera_init` scheitert mit `ESP_ERR_NOT_FOUND` (0x105), wenn die
Pinbelegung nicht zum Board passt — dann ist die **Board-Auswahl im
`menuconfig`** falsch, nicht die Kamera defekt. Die Firmware bricht deshalb
nicht ab: der HTTP-Server läuft weiter und `/status` nennt den Fehlercode.
Ein Gerät im Garten, das bei einem Fehler stumm bleibt, kostet einen Weg
nach draußen.
Zweiter Kandidat: `CONFIG_SPIRAM_MODE_OCT` in `sdkconfig.defaults`. Octal ist
für die R8-Module richtig; ein R2- oder Quad-Modul braucht `QUAD`, sonst
findet die Kamera keinen Framebuffer.
## Ein Zuschauer, mit Absicht
Ein zweiter Client bekommt **503** statt eines halben Bildes. Der Grund steht
im Kopf von [`main/vogelkamera.c`](main/vogelkamera.c): ein zweiter Zuschauer
sättigt die Funkstrecke, der Detector fällt in Timeouts, und zwar ohne dass
irgendwo ein Fehler auftaucht — die Bildrate halbiert sich einfach. Genau das
ist bei der Entwicklung passiert: das Diagnosewerkzeug hat den Fehler erzeugt,
den es messen sollte.
Deshalb gibt es `/status` und `/temp`. **Beide fassen den Stream nicht an** und
sind der richtige Weg, die Kamera im Betrieb zu prüfen:
```sh
curl -s http://<kamera>:8081/status
{"kamera":"bereit","fehler":"0x0","laufzeit_s":615201,"bilder":3206804,
"fps":5.25,"letztes_bild_s":0.1,"zuschauer":true,"rssi":-71,
"temperatur_c":51.7,"speicher_frei":8104348,"psram_frei":7941536}
```
(Echte Ausgabe einer Kamera an einer Vogeltränke nach sieben Tagen
Dauerbetrieb.) **Port 8081, nicht 8080** — siehe oben.
`ping` taugt dafür **nicht** — ICMP fällt bei gesättigtem WLAN als Erstes aus,
während TCP weiterläuft. Eine Entwarnung per `ping` ist Glück, keine Messung.
Was die Werte sagen:
| Feld | Gesund | Woran es sonst liegt |
|---|---|---|
| `fps` | stabil über Stunden | fällt → Funkstrecke, zweiter Zuschauer, Hitze |
| `rssi` | über −70 dBm | darunter Antenne ausrichten oder näher ran |
| `temperatur_c` | unter 70 °C | geschlossenes Gehäuse in der Sonne drosselt |
| `letztes_bild_s` | unter 1 s bei Zuschauer | steht → Sensor hängt, siehe unten |
| `zuschauer` | `true` im Normalbetrieb | `false`, obwohl der Detector läuft → er kommt nicht durch |
Bleibt der Sensor 30 Versuche am Stück stumm, startet das Gerät selbst neu.
Zwei Sekunden Neustart sind billiger als ein Weg in den Garten.
## Einstellungen, die wirklich etwas bringen
**Bildgröße und Qualität sind Bandbreite, nicht Schönheit.** Weniger Qualität
heißt mehr Bilder pro Sekunde, und mehr Bilder finden mehr Vögel als schönere.
Default ist XGA (1024x768) bei Qualität 12.
**Der begrenzende Faktor ist der Bildwinkel, nicht die Auflösung.** Am
Futterhaus scheitert die Erkennung daran, dass der Vogel unter den nötigen
5–8 % Bildfläche bleibt. Mehr Megapixel ändern daran nichts, nähere Montage
schon — 30–40 cm an die Landefläche, und die Landefläche in die untere
Bildmitte, nicht an den Bildrand (der unterste Bildrand ist eine tote Zone,
siehe `python -m detector karte`).
**Kein WLAN-Stromsparen.** `WIFI_PS_NONE` ist gesetzt: der Empfänger darf
zwischen zwei Bildern nicht schlafen. Die Bildrate ist der Zweck des Geräts,
nicht die Batterielaufzeit.
## Feste Adresse
Der Detector braucht eine gleichbleibende Adresse. Der Dev-Server löst **kein
mDNS** auf (kein avahi), `.local`-Namen helfen also nicht — im Router eine
feste DHCP-Lease für die MAC eintragen.
## Im Dauerbetrieb
Die Firmware läuft an einer Vogeltränke im Garten. Gemessen am laufenden
Detector, drei aufeinanderfolgende Minuten:
| | Bilder/s |
|---|---|
| **ESP32-S3-CAM mit OV3660** (dieses Projekt) | **5,2** |
| Raspberry Pi Zero W mit OV5647 (zum Vergleich) | 4,4 |
Insgesamt hat der Detector an dieser Kamera bisher über 2,8 Millionen Bilder
verarbeitet. Die Bildrate ist bandbreitenlimitiert, nicht rechenlimitiert —
deshalb der Rat zur Modulvariante mit Antennenbuchse (ESP32-S3-WROOM-1**U**)
und externer Antenne.
**Ungetestet sind die übrigen Pinbelegungen**: Gebaut und betrieben wurde
bisher nur die S3-CAM-Variante. XIAO-S3 und AI-Thinker sind in `menuconfig`
vorgesehen, aber nie auf Hardware gelaufen.
## Was fehlt
OTA-Updates sind nicht eingebaut. Die Partitionstabelle lässt hinter der
4-MB-App genug Platz, um sie ohne Umbau nachzurüsten — für ein Gerät, das
später im Garten festsitzt, wäre das der nächste sinnvolle Schritt.