263 lines
7.9 KiB
TeX
263 lines
7.9 KiB
TeX
\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}
|