// WLAN: Zugangsdaten im NVS, Verbindung als Station, Einrichtung als // Zugangspunkt. Wer die Daten hat, verbindet sich; wer keine hat, macht // sein eigenes Netz auf. // // Uebernommen aus Hey Fred (firmware/main/netz.c) am 23.09.2026. Dort ist // es erprobt; hier geaendert wurden der NVS-Bereich und der Name des // Einrichtungsnetzes. #pragma once #include #include #include "esp_err.h" #define NETZ_SSID_MAX 32 #define NETZ_PASS_MAX 64 // Name des Zugangspunkts zur Einrichtung. ⚠ Anders als bei Fred NICHT // fest: Die letzten beiden Bytes der MAC haengen hinten dran, also etwa // "Vogelkamera-A4F2". Zwei Kameras im selben Garten -- Futterhaus und // Traenke -- wuerden sich sonst denselben Namen teilen, und man wuesste // beim Einrichten nicht, welche man gerade vor sich hat. #define NETZ_SETUP_PRAEFIX "Vogelkamera" // Der tatsaechliche Name, erst zur Laufzeit bekannt. const char *netz_setup_ssid(void); // Das Geheimnis fuer Updates ueber WLAN. // // ⚠ Bewusst NICHT einkompiliert (so macht es Hey Fred in secrets.h). Fuer // ein Geraet, das als Bausatz weitergegeben wird, waere das derselbe // Fehler wie eingebackene WLAN-Daten: Jeder muesste die Firmware selbst // uebersetzen, und alle Geraete haetten dasselbe Geheimnis. // // Stattdessen: Beim ersten Start wuerfelt sich jedes Geraet sein eigenes // und legt es im NVS ab. Zu sehen bekommt man es im Einrichtungsnetz -- // wer dort steht, steht ohnehin vor dem Geraet. const char *netz_ota_token(void); typedef struct { char ssid[NETZ_SSID_MAX + 1]; int8_t rssi; bool verschluesselt; } netz_fund_t; // Einmal beim Start aufrufen, vor allem anderen. void netz_init(void); // Liegen Zugangsdaten im NVS? bool netz_zugang_vorhanden(void); // Der gespeicherte Netzname, oder "" wenn keiner da ist. Fuer die Anzeige // auf der Einrichtungsseite: Erst damit sieht man, ob das Speichern // ueberhaupt ankommt -- ohne Kabel, ohne Raten. const char *netz_gespeicherte_ssid(void); // Gibt false zurueck, wenn das Schreiben scheitert -- der Grund steht dann // in *fehler. ⚠ Frueher brach das Geraet bei einem Fehler einfach ab // (ESP_ERROR_CHECK): Es startete mitten im Absenden neu, der Browser blieb // haengen, nichts war gespeichert, und niemand erfuhr warum. bool netz_zugang_speichern(const char *ssid, const char *passwort, esp_err_t *fehler); void netz_zugang_loeschen(void); // Verbindet mit den gespeicherten Daten. Blockiert bis zum Erfolg oder bis // die Versuche aufgebraucht sind. Gibt false zurück, wenn es nicht klappt — // dann gehört Fred in den Einrichtungsmodus. bool netz_verbinden(void); // Öffnet den Zugangspunkt für die Einrichtung. Danach ist Fred unter // 192.168.4.1 erreichbar. void netz_setup_ap_start(void); // Sucht Netze in der Umgebung. Schreibt bis `max` Funde nach `ziel` und gibt // die Anzahl zurück. Doppelte SSIDs werden übersprungen, der stärkste // Fund gewinnt. int netz_suchen(netz_fund_t *ziel, int max); // Sucht EINMAL und merkt sich das Ergebnis. // // ⚠ Der Grund ist unangenehm und kostet sonst Stunden: Eine WLAN-Suche // wechselt den Funkkanal. Laeuft gleichzeitig der eigene Zugangspunkt, // fliegen die verbundenen Geraete dabei raus. Sucht man also, waehrend // jemand das Formular betrachtet, verliert genau dieser Jemand die // Verbindung -- und sein Absenden kommt nie an. Am 23.09.2026 genau so // passiert, an Handy wie an Mac. // // Deshalb: einmal suchen, bevor der erste Besucher da ist, und danach // nur noch die Liste ausliefern. int netz_funde_holen(netz_fund_t *ziel, int max); void netz_funde_auffrischen(void); // IP als Zeichenkette, leer wenn nicht verbunden. const char *netz_ip(void); // Wie oft die Verbindung seit dem Start abgerissen ist, und warum zuletzt. // Der Grund ist der Code des WLAN-Treibers: 15 und 204 stehen für ein // falsches Passwort, 201 für „Netz nicht gefunden", 2 bis 4 zeigen auf // Verschlüsselung und Management-Frames. int netz_trennungen(void); int netz_letzter_grund(void); // Läuft die Verbindung gerade? bool netz_verbunden(void); // Holt die Uhrzeit per SNTP und stellt sie auf deutsche Zeit. Blockiert // nicht — das Ergebnis trudelt im Hintergrund ein. void zeit_holen(void); // Steht die Uhr schon auf einer plausiblen Zeit? bool zeit_steht(void); // Empfangsstaerke zum Zugangspunkt in dBm (negativ; naeher an null ist // besser). 0 heisst: nicht ermittelbar. int netz_feldstaerke(void);