\documentclass[a4paper,11pt]{article} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage[ngerman]{babel} \usepackage{geometry} \usepackage{amsmath} \usepackage{booktabs} \usepackage{listings} \usepackage{xcolor} \usepackage[unicode=true]{hyperref} \usepackage{enumitem} \lstset{ basicstyle=\ttfamily\small, keywordstyle=\color{blue}, commentstyle=\color{gray}, stringstyle=\color{orange}, backgroundcolor=\color{gray!10}, frame=single, breaklines=true, columns=fullflexible } \title{\textbf{Emergency Stop Button -- ESP32}\\ \large Technische Dokumentation} \author{} \date{Juni 2026} \begin{document} \maketitle \tableofcontents \newpage % ----------------------------------------------- \section{Ziel und Anforderungen} Ein physischer Notaus-Taster soll beim Drücken so schnell wie möglich einen API-Call absetzen. Der ESP32 befindet sich im Ruhezustand und wird durch den Tastendruck geweckt. \begin{itemize} \item Latenz Knopfdruck $\rightarrow$ API-Call: \textbf{< 250\,ms} \item Stromversorgung: LiPo 2000\,mAh (kabellos, batteriebetrieben) \item Möglichst lange Akkulaufzeit (Taster wird selten gedrückt, $\leq$1\,×/h) \item Einfache, wartungsarme Architektur \end{itemize} % ----------------------------------------------- \section{Architekturentscheidung} \subsection{Bewertete Optionen} \begin{center} \begin{tabular}{lllll} \toprule Option & Latenz & Ø Strom & Gateway nötig & Komplexität \\ \midrule Deep Sleep + WiFi (RTC-Memory) & 600--1300\,ms & $\approx$0{,}02\,mA & nein & niedrig \\ \textbf{WiFi Light Sleep (DTIM=10)} & \textbf{150--250\,ms} & \textbf{0{,}5--1\,mA} & \textbf{nein} & \textbf{niedrig} \\ BLE Light Sleep + Gateway & 100--300\,ms & 0{,}5--2\,mA & ja & hoch \\ \bottomrule \end{tabular} \end{center} \subsection{Entscheidung: WiFi Light Sleep} WiFi Light Sleep erfüllt alle Anforderungen ohne zusätzliche Infrastruktur: \begin{itemize} \item Der ESP32 hält die WiFi-Verbindung aufrecht und schläft nur die CPU \item Ein GPIO-Interrupt weckt den ESP32 sofort beim Tastendruck \item Der API-Call geht direkt vom ESP32 -- kein Container, kein Gateway \end{itemize} \textbf{Warum nicht BLE?} Bluetooth LE wäre minimal sparsamer ($\approx$0{,}5\,mA vs. $\approx$1\,mA), erfordert aber ein dauerhaft laufendes Gateway-Gerät (Raspberry Pi, PC), das seinerseits Strom verbraucht und eine Fehlerquelle darstellt. \textbf{Warum nicht Deep Sleep?} Deep Sleep braucht beim Aufwachen 600--1300\,ms (WiFi-Reconnect), was die Latenzanforderung von 250\,ms verfehlt. % ----------------------------------------------- \section{WiFi Light Sleep -- Funktionsprinzip} Im WiFi Light Sleep (Modem Sleep) schläft die CPU, während der WiFi-Stack aktiv bleibt. Der Router sendet alle 100\,ms einen Beacon; mit DTIM=10 wacht der ESP32 automatisch alle $\approx$1000\,ms für 1--2\,ms auf, um gepufferte Pakete abzuholen. Die Verbindungsassoziation bleibt dabei erhalten. \subsection{DTIM-Einstellung und Stromverbrauch} \begin{center} \begin{tabular}{lll} \toprule DTIM & Wakeup-Intervall & Ø Strom \\ \midrule 1 & 100\,ms & 5--10\,mA \\ 3 & 300\,ms & 2--4\,mA \\ 10 & 1000\,ms & 0{,}5--1\,mA \\ \bottomrule \end{tabular} \end{center} Empfohlen: \texttt{DTIM=10} -- sparsamste Option, Verbindung bleibt stabil. \subsection{Akkulaufzeit (2000 mAh LiPo)} Bei $\approx$1\,mA Durchschnittsstrom (DTIM=10, selten gedrückt) und 80\,\% nutzbarer Kapazität: \[ t = \frac{1600~\mathrm{mAh}}{1~\mathrm{mA}} \approx 67\text{--}80~\mathrm{Tage} \] Die \textbf{Latenz von 250\,ms} hat Vorrang vor maximaler Akkulaufzeit -- Deep Sleep scheidet daher aus (WiFi-Reconnect 600--1300\,ms). Mit 2000\,mAh und WiFi Light Sleep sind ca.\ 2,5 Monate Betrieb ohne Laden realistisch. % ----------------------------------------------- \section{Latenzbudget} \begin{center} \begin{tabular}{ll} \toprule Schritt & Zeit \\ \midrule ESP32 Wakeup aus Light Sleep & 1--5\,ms \\ WiFi-Verbindung prüfen (bereits aktiv) & 0\,ms \\ HTTP-Request aufbauen & 20--50\,ms \\ TLS-Handshake (HTTPS) & 50--150\,ms \\ Server-Antwort & 20--50\,ms \\ \midrule \textbf{Gesamt} & \textbf{$\approx$100--250\,ms} \\ \bottomrule \end{tabular} \end{center} % ----------------------------------------------- \section{Hardware} \subsection{Empfohlene Boards} \begin{center} \begin{tabular}{llll} \toprule Board & Lader & Preis & Bemerkung \\ \midrule \textbf{FireBeetle ESP32-E} (DFRobot) & integriert & 8--12\,€ & Low-Power optimiert \\ LOLIN D32 (Wemos) & TP4054 & 5--8\,€ & günstig, bewährt \\ Adafruit HUZZAH32 Feather & MCP73831 & $\approx$20\,€ & gute Dokumentation \\ \bottomrule \end{tabular} \end{center} \subsection{Akku-Spezifikation} Für den FireBeetle\,2 wird ein \textbf{1S LiPo mit JST-PH-2,0-Stecker und BMS} benötigt: \begin{center} \begin{tabular}{ll} \toprule Merkmal & Wert \\ \midrule Typ & LiPo / Li-Polymer, 1 Zelle (1S) \\ Nennspannung & 3{,}7\,V (voll: 4{,}2\,V) \\ Stecker & JST PH, 2{,}0\,mm Raster, 2-polig \\ Schutzschaltung & Ja (BMS/PCM) -- Pflicht bei Deep-Sleep-Betrieb \\ Kapazität & 2000\,mAh \\ \bottomrule \end{tabular} \end{center} \textbf{Achtung Polarität:} JST-PH-Stecker sind nicht normiert -- Polung vor dem Einstecken mit Multimeter prüfen. Sicherste Quelle: Akku direkt bei DFRobot kaufen. \subsection{Schaltung} \begin{lstlisting} ESP32 Taster GPIO 9 ---- [Taster] ---- GND (interner Pull-Up aktiv: HIGH = offen, LOW = gedrueckt) LiPo 2000mAh (JST PH 2.0, mit BMS) ---- BAT+ / BAT- des Boards \end{lstlisting} Kein weiteres Bauteil nötig -- der interne Pull-Up des ESP32 reicht. % ----------------------------------------------- \section{Software} \subsection{Abhängigkeiten (Arduino IDE)} \begin{itemize} \item Board-Package: \texttt{esp32} by Espressif (Boards Manager) \item Bibliotheken: \texttt{WiFi.h}, \texttt{HTTPClient.h} (im esp32-Package enthalten) \end{itemize} \subsection{Konfiguration in \texttt{EmergencyStopButton.ino}} \begin{lstlisting}[language=C] #define BUTTON_PIN 9 #define WIFI_SSID "DEIN_SSID" #define WIFI_PASSWORD "DEIN_PASSWORT" #define API_URL "https://deine-api.example.com/emergency-stop" #define API_TOKEN "DEIN_API_TOKEN" \end{lstlisting} \subsection{Ablauf} \begin{enumerate} \item \texttt{setup()}: WiFi verbinden, \texttt{WIFI\_PS\_MAX\_MODEM} aktivieren \item \texttt{loop()}: \texttt{esp\_light\_sleep\_start()} -- CPU schläft, WiFi aktiv \item Tastendruck löst GPIO-Interrupt aus -- ESP32 wacht auf \item \texttt{sendEmergencyStop()} sendet HTTP-POST an API \item Warten bis Taster losgelassen, zurück zu Schritt 2 \end{enumerate} \subsection{Kritische API-Funktion} \begin{lstlisting}[language=C] void sendEmergencyStop() { HTTPClient http; http.begin(API_URL); http.addHeader("Content-Type", "application/json"); http.addHeader("Authorization", "Bearer " API_TOKEN); http.setTimeout(3000); String body = "{\"event\":\"emergency_stop\",\"device\":\"esp32-estop\"}"; int httpCode = http.POST(body); http.end(); } \end{lstlisting} % ----------------------------------------------- \section{Deployment-Hinweise} \begin{itemize} \item \textbf{HTTPS}: TLS-Handshake kostet 50--150\,ms. Falls die Latenz kritisch ist und das Netz vertrauenswürdig, kann HTTP verwendet werden (dann $\approx$50--100\,ms Gesamt). \item \textbf{DTIM am Router}: Manche Router ignorieren den DTIM-Wunsch des Clients. Im Zweifelsfall DTIM=3 am Router einstellen. \item \textbf{API-Token}: Nicht im Quellcode einchecken -- z.\,B. in eine separate \texttt{secrets.h} auslagern, die im \texttt{.gitignore} steht. \item \textbf{Watchdog}: Bei produktivem Einsatz einen Hardware-Watchdog aktivieren, damit der ESP32 sich bei WiFi-Verlust selbst neu startet. \end{itemize} % ----------------------------------------------- \section{Dateien} \begin{center} \begin{tabular}{ll} \toprule Datei & Beschreibung \\ \midrule \texttt{EmergencyStopButton.ino} & Arduino-Sketch (Hauptprogramm) \\ \texttt{eStopESP32.tex} & Diese Dokumentation \\ \bottomrule \end{tabular} \end{center} \end{document}