Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ESP-Kerberos

License: MIT Platform: PlatformIO Framework: Arduino Board: ESP32-C6 Web Flasher

Warning

🚧 Work in Progress — ESP-Kerberos befindet sich noch mitten in der Entwicklungsphase und wird aktuell umgesetzt. Hier kann sich tĂ€glich etwas Ă€ndern!

ESP-Kerberos

Batteriebetriebener Öffnungsmelder auf ESP32-C6-Basis fĂŒr Paketboxen mit integrierter Briefbox.

Wacht ĂŒber bis zu 4 unabhĂ€ngige EingĂ€nge gleichzeitig — typisch Brief-Einwurf, Paket-Einwurf, Brief-Entnahme und Paket-Entnahme — funktioniert aber mit jedem potentialfreien Schalter fĂŒr jede Art "etwas wurde geöffnet/ausgelöst"-Erkennung — siehe Sensor/Schalter. Die gesamte Konfiguration (WLAN, EingĂ€nge, Meldekanal inkl. MQTT) lĂ€uft ĂŒber ein WebUI im Access-Point-Modus — kein Neu-Kompilieren pro GerĂ€t nötig, siehe Setup.

Kernidee: der ESP32 verbringt praktisch seine gesamte Lebenszeit im Deep Sleep (Verbrauch im niedrigen ”A-Bereich) und wacht ausschließlich durch den externen Schalter auf. Nach dem Wecken verbindet er sich per WLAN, schickt eine Push-Nachricht und legt sich sofort wieder schlafen — die aktive Wachzeit pro Auslösung liegt im Sekundenbereich.

🔌 Web-Flasher — Firmware direkt aus dem Browser

Kein PlatformIO, kein Terminal, kein Compiler nötig: Firmware direkt aus dem Browser flashen, per ESP Web Tools.

👉 docbigs-lab.github.io/ESP-Kerberos — ein Klick auf „Install" reicht.

Funktionsprinzip

Deep Sleep ──(Schalter löst aus)──▶ Wake ──▶ WLAN verbinden ──▶ Benachrichtigung senden
     â–Č                                                                  │
     │                                                                  â–Œ
     └──────────────── Cooldown-Sleep (nur Timer-Wakeup) ◀─────────────┘
  1. Deep Sleep als Grundzustand, Aufwachen ĂŒber EXT1-Wakeup an einem GPIO.
  2. Entprellung: nach dem Wachwerden kurzer Delay + erneute Pin-PrĂŒfung, um Kontaktprellen nicht als Auslösung zu werten.
  3. WLAN-Verbindung (optional mit statischer IP statt DHCP, spart Verbindungszeit).
  4. Push-Benachrichtigung per HTTPS an den konfigurierten Meldekanal (Telegram, Pushover, ntfy.sh oder MQTT — siehe MQTT / Home Assistant) — kein SMTP/E-Mail-Versand, das wĂ€re auf dem Chip deutlich langsamer.
  5. Cooldown-Sleep: nach dem Versand schlĂ€ft der Chip fĂŒr einige Sekunden mit ausschließlich Timer-Wakeup (Schalter-Wakeup ist in dieser Zeit deaktiviert), damit Nachschwingen/Prellen keine weiteren Nachrichten auslöst. Erst danach wird der Schalter wieder scharf geschaltet.
  6. Bleibt ein Eingang lange ausgelöst (z. B. Klappe bleibt offen): Nach jedem Cooldown-Ablauf prĂŒft der Chip kurz und ohne WLAN/Nachricht, ob der Kontakt noch aktiv ist. Ist das der Fall, schlĂ€ft er einfach direkt fĂŒr einen weiteren Cooldown-Zyklus weiter — ganz ohne erneuten Versand. Erst wenn der Kontakt wieder in Ruhe ist, wird der Trigger erneut scharf geschaltet; eine neue Auslösung löst dann wieder genau eine Nachricht aus. Pro Auslöse-Flanke gibt es also immer nur eine Nachricht, unabhĂ€ngig davon, wie lange der Kontakt offen bleibt.

Konfiguration ĂŒber WebUI (Access Point + Captive Portal)

Die gesamte Konfiguration (WLAN, EingĂ€nge, Meldekanal) erfolgt zur Laufzeit ĂŒber ein WebUI — keine Zugangsdaten-Datei, kein Neu-Kompilieren pro GerĂ€t:

  • Erststart: Ist noch keine Konfiguration im Flash gespeichert, öffnet das GerĂ€t automatisch ein eigenes WLAN namens ESP-Kerberos-Setup-XXXX (XXXX = letzte zwei MAC-Adress-Bytes, zur Unterscheidung mehrerer GerĂ€te).

  • AP-Passwort: Das Setup-WLAN ist WPA2-geschĂŒtzt. Solange kein eigenes Passwort gesetzt wurde, gilt der aus der MAC-Adresse abgeleitete Default kerberos-XXXX (dieselben vier Hex-Zeichen wie im WLAN-Namen) — pro GerĂ€t unterschiedlich, kein fĂŒr alle GerĂ€te identisches Passwort. Im Formular unter "Zugriffsschutz (Setup-AP)" durch ein eigenes Passwort (mind. 8 Zeichen) ersetzbar; leeres Feld lĂ€sst das aktuelle/Standard-Passwort unverĂ€ndert. SchĂŒtzt sowohl die Konfigurationsseite (inkl. WLAN-/Meldekanal-Zugangsdaten) als auch das Firmware-Update vor jedem, der nur in Funkreichweite ist.

  • Mit diesem WLAN verbinden — auf den meisten Smartphones öffnet sich automatisch ein Anmelde-Popup mit der Konfigseite (Captive Portal). Öffnet sich nichts von selbst, http://192.168.4.1 manuell im Browser aufrufen.

  • Formular ausfĂŒllen: Das SSID-Feld schlĂ€gt gefundene WLANs vor (nach SignalstĂ€rke sortiert, stĂ€rkstes zuerst — der Button "Netzwerke neu suchen" stĂ¶ĂŸt einen erneuten Scan an, ohne bereits eingetragene Werte in anderen Feldern zu verwerfen), man kann aber auch jede beliebige SSID frei eintippen (z. B. bei versteckten Netzwerken). Bei aktivierter "Statische IP verwenden"-Checkbox zusĂ€tzlich IP-Adresse, Gateway, Subnetzmaske und DNS-Server eintragen (sonst bleibt es bei DHCP, Standardfall). Dazu gemeinsame Cooldown-Dauer und optionales Heartbeat-Intervall (siehe Heartbeat), sowie bis zu 4 unabhĂ€ngige EingĂ€nge (siehe Mehrere EingĂ€nge) — standardmĂ€ĂŸig sind alle 4 aktiv, vorbelegt mit den Paketbox-Rollen Brief-Einwurf/Paket-Einwurf/Brief-Entnahme/Paket-Entnahme, per Checkbox einzeln ab-/zuwĂ€hlbar —, jeder mit eigenem GPIO-Pin, eigener Auslöse-PolaritĂ€t und eigenem Nachrichtentext, gewĂŒnschter Meldekanal (gilt fĂŒr alle EingĂ€nge gemeinsam, inkl. MQTT — siehe MQTT / Home Assistant) mit dessen Zugangsdaten. Speichern → GerĂ€t startet neu und verbindet sich mit dem konfigurierten WLAN.

    ESP-Kerberos Setup-Formular: WLAN, Cooldown, EingÀnge 1-4, Meldekanal
  • Rekonfiguration: Kurz nach einem echten Power-On/Reset (Batterie/USB neu anschließen oder EN-Taste drĂŒcken — nicht nach einem normalen Deep-Sleep-Aufwachen durch einen Trigger) öffnet sich ein 5-Sekunden-Fenster: Wird in dieser Zeit die BOOT-Taste des Boards gedrĂŒckt, öffnet das GerĂ€t das Setup-WLAN erneut, unabhĂ€ngig davon, ob schon eine Konfiguration existiert. Ohne Tastendruck lĂ€uft der Normalbetrieb einfach weiter. Bei einer bereits konfigurierten Rekonfiguration kehrt das Portal ohne Eingabe nach 10 Minuten InaktivitĂ€t automatisch in den Normalbetrieb mit der bisherigen Konfiguration zurĂŒck. Ablauf zum Rekonfigurieren: Reset-Taste drĂŒcken, loslassen, dann innerhalb von 5 Sekunden die BOOT-Taste drĂŒcken (nicht gleichzeitig mit dem Reset — der ROM-Bootloader prĂŒft deren Pegel nur exakt im Reset-Moment fĂŒr den Flash-Download-Modus, danach ist GPIO9 ein normaler Eingang).

Die Konfiguration wird im NVS-Flash (Preferences) gespeichert und ĂŒbersteht Neustarts sowie Firmware-Updates ĂŒber die OTA-Funktion (solange der Flash nicht komplett gelöscht wird).

  • AP-Passwort vergessen? Es gibt bewusst keinen eigenen Software-Reset dafĂŒr — stattdessen ĂŒber USB oder den Web-Flasher neu flashen. Das schreibt Bootloader, Partitionstabelle und Firmware auf einen Schlag neu; die LĂŒcke im gemergten Image ĂŒberschreibt dabei auch die NVS-Partition mit gelöschtem Flash-Inhalt. Das GerĂ€t startet danach komplett zurĂŒckgesetzt — WLAN, EingĂ€nge, Meldekanal und AP-Passwort sind weg, nicht nur Letzteres, und mĂŒssen ĂŒber das Setup-Portal neu eingerichtet werden.

Firmware-Update (OTA)

Firmware-Updates lassen sich, ohne das GerĂ€t per USB anzuschließen, direkt ĂŒber das (durch das AP-Passwort geschĂŒtzte) Setup-Portal einspielen:

  1. Ins Setup-WLAN verbinden (Rekonfiguration öffnet das Portal auch bei bereits konfigurierten GerÀten).
  2. Auf der Konfigseite oben auf "Firmware-Update" klicken.
  3. ESP-Kerberos-ota.bin aus docs/ im Projekt-Repository auswĂ€hlen und hochladen — die Datei entsteht automatisch bei jedem pio run (via merge_bin.py), kein manuelles Suchen im PlatformIO-Build-Ordner nötig.
  4. Nach erfolgreichem Upload startet das GerÀt automatisch mit der neuen Firmware neu.

Wichtig — die richtige Datei: Hochgeladen wird ESP-Kerberos-ota.bin (reine App-Partition), nicht ESP-Kerberos-merged.bin aus demselben Ordner (die enthĂ€lt zusĂ€tzlich Bootloader und Partitionstabelle fĂŒr den USB-Erstflash und ist fĂŒr OTA ungeeignet). Ein versehentlicher Upload der falschen Datei fĂŒhrt zu einem nicht startenden GerĂ€t — Wiederherstellung dann nur per USB oder Web-Flasher (siehe AP-Passwort vergessen oben, Reflash setzt dabei die gesamte Konfiguration zurĂŒck).

Bewusst kein automatischer Versions-Check und kein Bezug einer Firmware-Datei von extern — der Upload passiert rein lokal im Setup-AP, ausgelöst durch den Nutzer selbst. Das hĂ€lt den sonst auf minimale Wachzeit optimierten Normalbetrieb (Deep Sleep/Trigger-Wake) vollstĂ€ndig unberĂŒhrt: OTA ist nur im ohnehin seltenen Rekonfigurations-Fenster verfĂŒgbar, nie im Hintergrund wĂ€hrend des Batteriebetriebs.

Sensor / Schalter

Elektrisch ist jeder potentialfreie Schalter (dry contact) gleich verkabelt: eine Ader an den Trigger-Pin, die andere an GND. Der interne Pull-up hĂ€lt den Pin im Ruhezustand HIGH; schließt der Schalter, geht der Pin auf LOW. Diese Verkabelung ist unabhĂ€ngig vom Schaltertyp immer identisch:

 GPIOx (Trigger-Pin) ──┬── Schalter ── GND
                       │
                   (interner Pull-up, per Software aktiviert)

Funktioniert z. B. mit:

  • Reedkontakt (Magnetkontakt) — klassisch fĂŒr Klappen/TĂŒren/Fenster
  • Mikroschalter / Endschalter — z. B. an einer Klappe, die beim Öffnen einen Hebel drĂŒckt
  • Taster — fĂŒr "manueller Alarmknopf"-AnwendungsfĂ€lle
  • Kipp-/Neigungsschalter — z. B. Kippt-Erkennung
  • Jeder andere einfache mechanische Schalter mit zwei potentialfreien AnschlĂŒssen

Welche Pin-PolaritĂ€t als "Auslösung" gilt, hĂ€ngt nur davon ab, ob der jeweilige Schalter beim auslösenden Ereignis öffnet oder schließt — das wird im WebUI pro Eingang eingestellt, die Verkabelung selbst Ă€ndert sich dabei nicht.

Wichtige EinschrĂ€nkung (ESP32-C6): Nur die GPIOs 0–7 sind RTC/LP-fĂ€hig und damit grundsĂ€tzlich als Deep-Sleep-Wakeup-Quelle nutzbar. Jeder andere Pin kann den Chip nicht aus dem Deep Sleep wecken. Welche dieser acht Pins auf dem jeweiligen Board tatsĂ€chlich herausgefĂŒhrt sind, hĂ€ngt vom konkreten Modul ab (Pinout/Silkscreen prĂŒfen). Auf dem Referenzboard (FireBeetle 2 ESP32-C6) ist GPIO0 zusĂ€tzlich fest mit dem eingebauten Akku-Spannungsteiler verdrahtet (siehe BatterieĂŒberwachung) und daher als Trigger-Pin ungeeignet — die WebUI-Auswahl fĂŒr Trigger-EingĂ€nge ist deshalb auf GPIO1–7 begrenzt.

Die werkseitig vorbelegten Pins (GPIO1, 2, 3, 4) passen zum Referenzboard (FireBeetle 2 ESP32-C6, siehe Hardware) — dort ist GPIO0 fest mit dem eingebauten Akku-Spannungsteiler verdrahtet (siehe BatterieĂŒberwachung) und daher als Trigger-Pin ungeeignet. Auf anderen Boards kann die sinnvolle Vorbelegung abweichen — die Zuordnung ist pro Eingang im Setup-Portal unter "GPIO-Pin" frei Ă€nderbar, ein Neuflashen ist dafĂŒr nicht nötig.

Mehrere EingÀnge

ESP-Kerberos ĂŒberwacht bis zu 4 unabhĂ€ngige EingĂ€nge gleichzeitig — namensgebender Regelfall ist eine Paketbox mit integrierter Briefbox (Brief-Einwurf, Paket-Einwurf, Brief-Entnahme, Paket-Entnahme), es funktioniert aber genauso fĂŒr jede andere Kombination (z. B. Briefkasten + GartentĂŒr + Garagentor). Jeder Eingang ist im WebUI einzeln:

  • aktivierbar/deaktivierbar (nicht benötigte EingĂ€nge einfach abwĂ€hlen),
  • einem eigenen GPIO-Pin (0–7) zugeordnet,
  • mit eigener Auslöse-PolaritĂ€t (öffnet/schließt) konfigurierbar — jeder Eingang kann anders verdrahtet sein, das ist unabhĂ€ngig von den anderen,
  • mit eigenem Nachrichtentext versehen, damit die Push-Nachricht (bzw. bei MQTT: der Home-Assistant-Entity-Name) erkennen lĂ€sst, welcher Kontakt ausgelöst hat.

Der Meldekanal (Telegram/Pushover/ntfy/MQTT) und die Cooldown-Dauer gelten gerĂ€teweit fĂŒr alle EingĂ€nge gemeinsam. Löst mehr als ein Eingang gleichzeitig aus, wird fĂŒr jeden aktiven Eingang eine eigene Nachricht ĂŒber dieselbe WLAN-Verbindung verschickt. Wird kein einziger Eingang aktiviert, bleibt Eingang 1 als Sicherheitsnetz automatisch aktiv — sonst hĂ€tte das GerĂ€t keine Aufwach-Quelle mehr und wĂŒrde nie wieder von selbst aus dem Deep Sleep zurĂŒckkehren.

MeldekanÀle

Genau ein Kanal ist pro GerÀt aktiv, umschaltbar im WebUI:

  • Telegram — Bot ĂŒber @BotFather anlegen, Token kopieren; eigene Chat-ID z. B. ĂŒber @userinfobot ermitteln.
  • Pushover — App-Token und User-Key aus dem eigenen Pushover-Konto.
  • ntfy.sh — kein Konto nötig, einfach einen (möglichst schwer zu erratenden) Topic-Namen wĂ€hlen; Server-URL ist im WebUI ĂŒberschreibbar fĂŒr selbstgehostete ntfy-Instanzen.
  • MQTT — Anbindung an beliebige Smart-Home-Systeme, inkl. automatischer Home-Assistant- Discovery. Siehe MQTT / Home Assistant.

Heartbeat

Optionaler periodischer Wake unabhĂ€ngig von echten Auslösungen (WebUI-Feld „Heartbeat-Intervall (Minuten)", 0 = deaktiviert, Default 60 min) — lĂ€uft als zusĂ€tzliche Deep-Sleep-Wakeup-Quelle parallel zum EXT1-Trigger, ohne dass der Chip dafĂŒr dauerhaft wach bleibt oder pollt.

  • Bei MQTT-Kanal: hĂ€lt Home-Assistant-Discovery- und State-Topics aktuell, falls der Broker zwischenzeitlich Retained Messages verloren hat (z. B. Neustart ohne Persistence) — sonst mĂŒsste man dafĂŒr auf den nĂ€chsten echten Power-On/Reset warten. Details siehe MQTT / Home Assistant weiter unten.
  • Bei Push-KanĂ€len (Telegram/Pushover/ntfy): per Checkbox „TĂ€gliches Lebenszeichen per Push" zuschaltbar, sendet dann unabhĂ€ngig vom (ggf. kĂŒrzeren) technischen Heartbeat-Intervall höchstens 1x/Tag eine „ESP-Kerberos ist online"-Nachricht (inkl. Akkustand, falls BatterieĂŒberwachung aktiv) — bewusst selten, um bei einer Briefkasten-App nicht zu nerven. Push-KanĂ€le kennen anders als Home Assistant kein „nicht verfĂŒgbar", das ist hier der einzige Weg, ĂŒberhaupt mitzubekommen, dass das GerĂ€t noch antwortet.
  • Stromkosten: ein zusĂ€tzlicher Wake-Zyklus kostet laut Hardware ≈5,1 ”Ah. Beim Default von 60 Minuten macht das rechnerisch ≈122 ”Ah/Tag zusĂ€tzlich — bei einem 3000-mAh-Akku vernachlĂ€ssigbar (die Selbstentladung der Zelle dominiert ohnehin, siehe Hardware-Abschnitt).

MQTT / Home Assistant

Anders als die drei Push-KanĂ€le (die pro Auslösung eine einmalige Nachricht schicken) bildet der MQTT-Kanal den aktuellen Zustand jedes Eingangs ab — genau wie ein handelsĂŒblicher TĂŒr-/Fenstersensor:

  • Pro Eingang wird ein retained ON/OFF-Status auf <Basis-Topic>/input<N>/state veröffentlicht (N = 0–3). Öffnet ein Kontakt → ON, schließt er wieder → OFF. Das Basis-Topic ist im WebUI frei wĂ€hlbar, leer gelassen wird automatisch kerberos/<GerĂ€te-ID> verwendet (<GerĂ€te-ID> = letzte zwei MAC-Adress-Bytes).
  • Bei jeder Auslösung wird kurz vor dem ON immer zuerst ein OFF gepusht. Das erzwingt einen echten Zustandswechsel in Home Assistant, selbst wenn der dort zuletzt bekannte (retained) Zustand schon ON war — z. B. weil ein frĂŒheres OFF wegen eines WLAN-/Broker-Ausfalls nie ankam. Ohne diesen Puls wĂŒrde ein erneutes ON mit identischem Payload keinen state_changed-Event auslösen, wodurch Automationen mit trigger: state, to: "on" die Auslösung verpassen wĂŒrden.
  • Home-Assistant-Autodiscovery (per Checkbox im WebUI an-/abschaltbar) legt fĂŒr jeden aktivierten Eingang automatisch einen binary_sensor (device_class door) an, gruppiert unter einem gemeinsamen GerĂ€t „ESP-Kerberos <GerĂ€te-ID>“ — keine manuelle Konfiguration in Home Assistant nötig. Deaktivierte EingĂ€nge bzw. abgeschaltete Discovery entfernen die zugehörige Entity automatisch wieder aus Home Assistant.
  • Kein Dauerbetrieb, reiner Publish-Kanal: Das GerĂ€t verbindet sich nur kurz zum Broker, wenn es ohnehin wach ist (Auslösung, RĂŒckkehr in Ruhezustand, einmalig nach jedem Power-On/Reset fĂŒr die Discovery-Configs, oder beim optionalen Heartbeat) — passend zum Kernprinzip minimaler Wachzeit. Es gibt bewusst kein eingehendes Command-Topic/Subscribe (kein "2-Weg" im engeren Sinn) und keine klassische MQTT-Availability/Last-Will-Verfolgung, da ein GerĂ€t, das die meiste Zeit im Deep Sleep verbringt, damit fĂ€lschlich dauerhaft als "nicht verfĂŒgbar" erschiene. Als optionale Alternative dazu: Ist der Heartbeat aktiv, tragen alle Discovery-Configs ein expire_after (2x Heartbeat-Intervall) — Home Assistant markiert die Entities dann von sich aus als "nicht verfĂŒgbar", wenn tatsĂ€chlich zu lange nichts mehr ankam.
  • Heartbeat-Sensor als "letzter Kontakt": Eine zusĂ€tzliche sensor-Entity zĂ€hlt Wake-Zyklen (Trigger und Heartbeat) auf <Basis-Topic>/heartbeat/state. Der ESP32 kennt dabei keine Uhrzeit (keine NTP-Synchronisation) — Home Assistant trackt fĂŒr jede Entity ohnehin automatisch einen last_updated-Zeitstempel, der genau diese Rolle ĂŒbernimmt, direkt in der HA-UI bzw. per Template nutzbar.
  • Publishes werden bewusst entzerrt, nicht als Burst gefeuert: Nach jedem einzelnen publish() folgt eine kurze Pause (mqttClient.loop() + ~20 ms), zusĂ€tzlich ~200 ms Einschwingzeit direkt nach dem WLAN-Connect, bevor ĂŒberhaupt losgesendet wird. Ohne das gingen bei einem Direktstart aus dem Deep Sleep (kein vorher verbundener USB-Host, z. B. reiner Akku-/Netzteilbetrieb) vereinzelt einzelne der bis zu 14 Nachrichten (7 Discovery + 4 State + 2 Batterie + 1 Heartbeat) auf Funkebene verloren — via MQTT QoS 0 (PubSubClient-Standard) gibt es dafĂŒr weder BestĂ€tigung noch Retry, sodass die Firmware den Verlust nie bemerkte (Log zeigte durchgehend "erfolgreich", am Broker kam aber nichts an). Betraf reproduzierbar den ersten Verbindungsversuch nach einem Kaltstart ohne angeschlossenen USB-Host; ein zweiter Versuch kurz danach funktionierte meist schon. Statische IP statt DHCP behob es NICHT (schließt DHCP-/ARP-Timing als Ursache aus) — die Entzerrung schon (3/3 TestlĂ€ufe auf Akku bzw. Fremdnetzteil erfolgreich, 2026-07-19).
  • Verbindung erfolgt unverschlĂŒsselt (TCP, Standardport 1883) — gedacht fĂŒr einen Broker im eigenen lokalen Netz (z. B. das Mosquitto-Add-on von Home Assistant). Nutzername/Passwort sind optional.

BatterieĂŒberwachung

Optionale Akku-Spannungsmessung, per Default deaktiviert, im WebUI unter "Batterie" aktivierbar:

  • Messzeitpunkt: bei einer echten Trigger-Auslösung (EXT1-Wakeup), nicht bei jedem Cooldown-/Poll-Wake. Es gibt bewusst keine eigene periodische Wakeup-Quelle nur fĂŒr die Akkumessung — der optionale Heartbeat ist ein allgemeiner, fĂŒr andere Zwecke gedachter Mechanismus, den die Batteriemeldung lediglich mitnutzt, sobald ohnehin gerade gewacht/verbunden wird: bei MQTT-Kanal bei jedem Heartbeat-Wake, bei Push-KanĂ€len nur innerhalb des tĂ€glichen Lebenszeichens (falls beides aktiviert ist). In der Praxis bedeutet das: der Akkustand wird bei jeder Auslösung mitgemeldet, ganz ohne zusĂ€tzliche WLAN-Verbindung, die nicht ohnehin schon aufgebaut wĂŒrde.
  • Messung: ADC-Spannungsteiler, Default-Werte (Pin GPIO0, Faktor ×2) passen zum eingebauten Teiler des Referenzboards (FireBeetle 2 ESP32-C6, siehe DFRobot-Wiki) und sind bei abweichender Hardware im WebUI anpassbar. Die Umrechnung in Prozent erfolgt ĂŒber eine lineare NĂ€herung (3,3 V = 0 %, 4,2 V = 100 %) — kein Fuel-Gauge-IC, keine Kalibrierung gegen Zellalterung, ausreichend fĂŒr eine grobe Warnschwelle, nicht fĂŒr eine prĂ€zise Restlaufzeitanzeige.
  • Meldung je nach Meldekanal (siehe MeldekanĂ€le):
    • MQTT: kontinuierlicher Zustand als zwei retained Sensor-Entities (.../battery/state in Prozent, .../battery_voltage/state in Volt), inkl. Home-Assistant-Autodiscovery (device_class battery/voltage) nach demselben Muster wie die EingĂ€nge.
    • Push-KanĂ€le (Telegram/Pushover/ntfy): einmalige Warnmeldung beim Unterschreiten der im WebUI konfigurierten Warnschwelle (Default 15 %) — kein Dauer-Spam bei jeder weiteren Auslösung. Die Warnung wird erst wieder gelöscht (und kann erneut auslösen), sobald der Ladezustand die Schwelle um mehr als 10 Prozentpunkte ĂŒbersteigt (Hysterese gegen Flattern knapp um die Schwelle).

Hardware

  • Microcontroller: DFRobot FireBeetle 2 ESP32-C6 (DFR1075) — ESP32-C6-Modul (RISC-V, WLAN 6 + BLE 5.0), Referenz-/Empfehlungsboard fĂŒr dieses Projekt. Der Code selbst bleibt board-agnostisch, solange GPIO0–7 (Trigger) zugĂ€nglich sind; auf dem FireBeetle 2 ist GPIO0 allerdings fest fĂŒr die eingebaute Akku-Spannungsmessung reserviert (siehe BatterieĂŒberwachung) und steht daher nicht als Trigger-Pin zur VerfĂŒgung. Die BOOT-Taste (GPIO9) wird von der Firmware fĂŒr die Rekonfiguration genutzt (5-Sekunden-Fenster nach Power-On/Reset) — zusĂ€tzlich zu ihrer ĂŒblichen Rolle beim Flashen ĂŒber esptool.

  • Stromversorgung: Getesteter/empfohlener Akku: Meshnology-Akku (gleiche Baureihe, hier in der 3000-mAh-Variante statt der ursprĂŒnglich dokumentierten 5000-mAh-Zelle), 3,7 V nominal, PH-2.0-Steckverbinder (passend zum Akku-Anschluss des FireBeetle 2, mechanisch gesteckt statt gedrahtet), mit eingebauter Schutzplatine gegen Tiefentladung/Kurzschluss. Max. Ladestrom laut Datenblatt der 3000-mAh-Variante vor Inbetriebnahme gegenprĂŒfen — bei der 5000-mAh-Variante lag er bei 2,5 A und damit weit ĂŒber den ≈540 mA, mit denen der eingebaute Lade-IC des Boards (CN3165, USB-C) tatsĂ€chlich lĂ€dt (unkritisch, nur entsprechend lange Ladezeit); kleinere Zellen können einen geringeren zulĂ€ssigen Ladestrom haben. Überlade-/ Überentladeschwellen (4,2 V / 3,0 V) und Ladetemperaturbereich (0–45 °C) decken sich mit den Werten, auf denen die BatterieĂŒberwachung dieses Projekts aufbaut, siehe BatterieĂŒberwachung. Bewusst kein 18650: runde Zellen sind ohne eigene Schutzplatine ĂŒblich und mĂŒssten separat abgesichert werden.

    Empfohlener Akku: Meshnology, 3,7V/3000mAh, PH-2.0-Stecker

    ⚠ Sicherheitshinweise:

    • Akku nicht im eingebauten/verschlossenen Zustand laden. Stattdessen den Akku aus der Paketbox entnehmen und ĂŒber sein eigenes, mitgeliefertes USB-Ladekabel außerhalb laden (der Lade-IC des Boards bleibt dabei ungenutzt) — sowohl wegen des Thermal-Runaway-Risikos einer Lithium-Zelle in einem geschlossenen GehĂ€use als auch weil ein USB-Kabel durch eine Paketbox ohnehin unpraktikabel ist.
    • Laden bei KĂ€lte ist nicht vorgesehen und wird von der Firmware nicht abgesichert (kein Temperatur-Gate) — nur bei Zimmertemperatur laden (0–45 °C laut Akku-Datenblatt). Siehe auch Bekannte offene Punkte.
  • Gemessener Ruhestrom & Akkulaufzeit (2026-07-16, PPK2-Messung): Deep-Sleep-Durchschnitt 37,88 ”A, stabil ĂŒber mehrere Minuten gemessen (kurze periodische Spikes bis ~490 ”A alle ~23 ms sind normales Regler-/RTC-Refresh-Verhalten des Power-Managements, kein Firmware-Effekt — im Deep Sleep lĂ€uft kein Anwendungscode). Ein einzelner Meldezyklus (EXT1-Wake → WLAN verbinden → Nachricht senden → zurĂŒck in Sleep) kostet zusĂ€tzlich ≈ 5,1 ”Ah. Diese Messung stammt von vor der MQTT-Publish-Entzerrung (s. u.) — bei aktivem MQTT-Kanal kommen dadurch nochmal grob 0,3–0,7 s zusĂ€tzliche Wachzeit pro Zyklus dazu, bislang nicht neu gemessen. Beispielrechnung mit dem oben empfohlenen 3,7 V/3000-mAh-Akku:

    • Ruhestrom: 37,88 ”A × 24 h ≈ 0,91 mAh/Tag
    • Selbst bei 20 Auslösungen/Tag: zusĂ€tzlich nur ≈ 0,10 mAh/Tag
    • Gesamt ≈ 1,01 mAh/Tag → 3000 mAh Ă· 1,01 mAh/Tag ≈ ~8 Jahre rechnerische Standby-Zeit

    In der Praxis begrenzt nicht der Stromverbrauch der Elektronik die Akkulaufzeit, sondern die Selbstentladung der Li-Ion-Zelle (typ. 2–5 %/Monat).

  • GehĂ€use: Elektronik möglichst außerhalb metallischer GehĂ€use (Faraday-KĂ€fig-Effekt) montieren, nur die dĂŒnne Schalter-Verkabelung nach innen fĂŒhren.

Setup

  1. Bauen und flashen (Konfiguration erfolgt anschließend ĂŒber das WebUI):
    pio run --target upload
    
    Alternativ ohne Kommandozeile: siehe Web-Flasher oben.
  2. GerĂ€t startet, öffnet mangels Konfiguration automatisch das Setup-WLAN ESP-Kerberos-Setup-XXXX — siehe Konfiguration ĂŒber WebUI.
  3. Formular ausfĂŒllen und speichern. GerĂ€t startet neu und lĂ€uft im Normalbetrieb.

Projektstruktur

ESP-Kerberos/
├── platformio.ini
├── merge_bin.py         # Post-Build-Hook: erzeugt docs/ESP-Kerberos-merged.bin
├── README.md
├── LICENSE
├── docs/                # GitHub-Pages-Root: Web-Flasher
│   ├── index.html       # ESP Web Tools UI
│   ├── manifest.json    # Firmware-Manifest fĂŒr den Flasher
│   ├── ESP-Kerberos-merged.bin  # von merge_bin.py generiert — USB-Erstflash, nicht fĂŒr OTA
│   └── ESP-Kerberos-ota.bin     # von merge_bin.py generiert — Upload im Setup-Portal (/update)
└── src/
    ├── main.cpp            # Boot-Orchestrierung, Sleep/Wake, Entprellung
    ├── kerberos_config.h   # Config-Struct + Enums (header-only)
    ├── config_store.h/.cpp # NVS laden/speichern (Preferences)
    ├── portal.h/.cpp       # AP + Captive Portal + Webformular + OTA-Firmware-Upload
    ├── notify.h/.cpp       # Telegram/Pushover/ntfy-Versand
    ├── mqtt.h/.cpp         # MQTT-Zustands-Publish + Home-Assistant-Discovery
    ├── battery.h/.cpp      # Akku-ADC-Messung + Prozent-NĂ€herung
    └── debug_log.h         # Serial-Debug-Logging, per Build-Flag abschaltbar

Debug-Logging ĂŒber die serielle Konsole

StandardmĂ€ĂŸig aktiv (KERBEROS_DEBUG=1), 115200 Baud (pio device monitor). Loggt Boot-/Wake-Ursache, WLAN-Verbindungsstatus, Portal-/Scan-Ereignisse und HTTP-Antwortcodes der MeldekanĂ€le — bewusst ohne Passwörter/Tokens im Log. FĂŒr den spĂ€teren Batteriebetrieb abschaltbar (spart die minimale Restlaufzeit, die Serial.begin()/Prints kosten):

build_flags = -DKERBEROS_DEBUG=0

Bekannte offene Punkte

  • Ruhestrom durch interne Pull-ups bei geschlossenem Ruhekontakt: Der interne Pull-up fĂŒr den EXT1-Wakeup hĂ€lt zusĂ€tzlich die RTC_PERIPH-DomĂ€ne wĂ€hrend des Deep Sleep aktiv, was den Ruhestrom leicht erhöht. Relevanter ist der Fall triggerOnOpen = true (Default aller 4 EingĂ€nge): Der Ruhezustand ist dabei "Kontakt geschlossen", der interne Pull-up (laut ESP32-C6-Datenblatt nĂ€herungsweise 45 kΩ) liegt dann dauerhaft ĂŒber den geschlossenen Schalter an GND — rechnerisch rund 70 ”A pro so beschaltetem Eingang (3,3 V Ă· 45 kΩ), bei allen 4 EingĂ€ngen im ungĂŒnstigsten Fall in Summe rund 280 ”A zusĂ€tzlich, auch im Deep Sleep. Das ist eine SchĂ€tzung nach Datenblattwert, keine gemessene Zahl — eine PPK2-Verifikation der tatsĂ€chlichen internen Pull-up-StromstĂ€rke des C6 im Deep Sleep steht noch aus. Zwei Möglichkeiten zur EntschĂ€rfung, falls relevant:
    • Kontakte/Schalter so montieren bzw. wĂ€hlen, dass der Ruhezustand "offen" ist (triggerOnOpen = false) — dann fließt im Ruhezustand kein Pull-up-Strom ĂŒber den Schalter, nur kurzzeitig wĂ€hrend der Auslösung selbst.
    • Statt des internen Pull-ups einen externen ~1-MΩ-Pull-up je Eingang verwenden (reduziert den Ruhestrom um etwa den Faktor 20 gegenĂŒber den internen ~45 kΩ) — dafĂŒr aktuell keine Firmware-UnterstĂŒtzung, da die interne rtc_gpio_pullup_en()-Konfiguration fest verdrahtet ist.
  • Akku-Sicherheit bei Außenmontage (z. B. Briefkasten): Li-Ion-Zellen dĂŒrfen laut den meisten Zellenherstellern nicht unter 0 °C geladen werden (Lithium-Plating-Risiko). Ob der TEMP-Pin des CN3165 an einen NTC im Akkupack angeschlossen ist oder nur fest verdrahtet (d. h. die TemperaturĂŒberwachung faktisch deaktiviert), lĂ€sst sich aus dem Schaltplan allein nicht zuverlĂ€ssig klĂ€ren und muss an der realen Hardware nachgemessen bzw. im DFRobot-Wiki verifiziert werden. Bis dahin gilt uneingeschrĂ€nkt: Akku nur kontrolliert bei Zimmertemperatur laden (siehe Hardware), nicht bei Wintermontage im Freien.
  • Software-Reset fĂŒr vergessenes AP-Passwort (erwogen, zurĂŒckgestellt): Aktuell nur per USB/Web-Flasher behebbar (AP-Passwort vergessen), was einen Computer/Browser voraussetzt. Eine BOOT-Taste-Geste (z. B. ~10s halten → Preferences::clear() + Neustart) wĂ€re bequemer fĂŒr ein bereits fest montiertes GerĂ€t, wurde aber zurĂŒckgestellt — dupliziert technisch nur den bereits vorhandenen Reflash-Reset und wĂŒrde die BOOT-Taste-Zeitfenster-Logik (main.cpp, aktuell reine Ja/Nein-Erkennung statt Haltedauer-Messung) nicht unerheblich verkomplizieren.

About

🐕📩 Batteriebetriebener Öffnungsmelder fĂŒr Paketboxen auf ESP32-C6-Basis. Bis zu 4 unabhĂ€ngige EingĂ€nge, Deep Sleep im ”A-Bereich, Push via Telegram/Pushover/ntfy oder MQTT mit Home-Assistant-Autodiscovery. Komplette Konfiguration per WebUI, Firmware-Flash direkt aus dem Browser.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages