# 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://:8080/stream MJPEG, ein Zuschauer http://:8080/ Vorschauseite http://:8081/status JSON: fps, RSSI, Temperatur, Laufzeit http://:8081/temp Chiptemperatur als Text http://: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://: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.