222 lines
7.9 KiB
C++
222 lines
7.9 KiB
C++
/*
|
|
* Skrift ESP32 MOSFET-Trigger ("Stromzufuhr aktivieren")
|
|
* =========================================================================
|
|
* WLAN-Empfaenger fuer einen N-Kanal-MOSFET (Low-Side) oder ein Relaismodul.
|
|
* AutoRecover ruft /trigger auf, der ESP32 schaltet GPIO 23.
|
|
*
|
|
* Endpunkte (HTTP GET, Port 80):
|
|
* / Statusseite (Text)
|
|
* /status Status als JSON (fuer AutoRecover geeignet)
|
|
* /trigger Puls von PULSE_MS ms (0 = dauerhaft an / Latch)
|
|
* /on dauerhaft AN
|
|
* /off AUS
|
|
* Ist AUTH_TOKEN gesetzt, braucht jeder Aufruf ?key=<token>.
|
|
*
|
|
* Verdrahtung: GPIO 23 -> Gate (100 Ohm), Gate -> 100 kOhm -> GND,
|
|
* Source -> GND, Drain -> Minus der Last, Massen verbinden.
|
|
*/
|
|
|
|
#include <WiFi.h>
|
|
#include <WebServer.h>
|
|
#include <ESPmDNS.h>
|
|
|
|
// ===== Konfiguration =====================================================
|
|
const char* WIFI_SSID = "Netz-Orth";
|
|
const char* WIFI_PASS = "PLATZHALTER@2025";
|
|
|
|
// GPIO 23 ist am ESP32 sicher: kein Strapping-Pin, kein Boot-Puls.
|
|
// Weitere sichere Pins: 13, 14, 16, 17, 18, 19, 21, 22, 25, 26, 27, 32, 33
|
|
const int MOSFET_PIN = 23;
|
|
const bool ACTIVE_HIGH = true; // true: GPIO HIGH = Last an
|
|
|
|
const unsigned long PULSE_MS = 3000; // /trigger: Einschaltdauer, 0 = Latch
|
|
const unsigned long MAX_LATCH_MS = 600000; // Fail-safe: Latch nach 10 min beenden
|
|
|
|
// Leer = kein Schutz. Muss zu esp_key in der AutoRecover-config.json passen.
|
|
const char* AUTH_TOKEN = "";
|
|
|
|
// Feste IP empfohlen, damit AutoRecover eine stabile Adresse hat.
|
|
const bool USE_STATIC_IP = false;
|
|
IPAddress IP_ADDR (192, 168, 178, 152);
|
|
IPAddress IP_GATE (192, 168, 178, 1);
|
|
IPAddress IP_MASK (255, 255, 255, 0);
|
|
IPAddress IP_DNS (192, 168, 178, 1);
|
|
|
|
// Erreichbar als http://skrift-trigger.local/ (Windows braucht Bonjour).
|
|
const bool USE_MDNS = true;
|
|
const char* MDNS_NAME = "skrift-trigger";
|
|
|
|
const unsigned long WIFI_BOOT_TIMEOUT_MS = 20000; // Erstverbindung
|
|
const unsigned long WIFI_CHECK_MS = 5000; // Reconnect-Intervall
|
|
const unsigned long WIFI_GIVEUP_MS = 600000; // 10 min offline -> Neustart
|
|
|
|
// ===== Zustand ===========================================================
|
|
WebServer server(80);
|
|
|
|
bool mosfetAn = false;
|
|
bool pulsAktiv = false;
|
|
unsigned long pulsBis = 0;
|
|
unsigned long letzterRequest = 0; // fuer Latch-Fail-safe
|
|
unsigned long triggerZaehler = 0;
|
|
unsigned long letzterTriggerS = 0; // Uptime-Sekunden des letzten Triggers
|
|
unsigned long offlineSeit = 0; // 0 = online
|
|
|
|
// ===== Hilfsfunktionen ===================================================
|
|
void mosfetSchreiben(bool an) {
|
|
mosfetAn = an;
|
|
digitalWrite(MOSFET_PIN, (an == ACTIVE_HIGH) ? HIGH : LOW);
|
|
}
|
|
|
|
bool autorisiert() {
|
|
if (strlen(AUTH_TOKEN) == 0) return true;
|
|
return server.hasArg("key") && server.arg("key") == AUTH_TOKEN;
|
|
}
|
|
|
|
void sendeStatus(const char* hinweis) {
|
|
String s = String(hinweis) + "\n";
|
|
s += "mosfet="; s += (mosfetAn ? "an" : "aus"); s += "\n";
|
|
s += "puls="; s += (pulsAktiv ? "aktiv" : "-"); s += "\n";
|
|
s += "trigger_count="; s += String(triggerZaehler); s += "\n";
|
|
s += "letzter_trigger_s="; s += String(letzterTriggerS); s += "\n";
|
|
s += "wlan="; s += (WiFi.status() == WL_CONNECTED ? "ok" : "weg"); s += "\n";
|
|
s += "rssi="; s += String(WiFi.RSSI()); s += "\n";
|
|
s += "ip="; s += WiFi.localIP().toString(); s += "\n";
|
|
s += "uptime_s="; s += String(millis() / 1000); s += "\n";
|
|
server.send(200, "text/plain; charset=utf-8", s);
|
|
}
|
|
|
|
void sendeJson() {
|
|
String j = "{";
|
|
j += "\"mosfet\":"; j += (mosfetAn ? "true" : "false");
|
|
j += ",\"puls\":"; j += (pulsAktiv ? "true" : "false");
|
|
j += ",\"trigger_count\":"; j += String(triggerZaehler);
|
|
j += ",\"letzter_trigger_s\":";j += String(letzterTriggerS);
|
|
j += ",\"rssi\":"; j += String(WiFi.RSSI());
|
|
j += ",\"uptime_s\":"; j += String(millis() / 1000);
|
|
j += "}";
|
|
server.send(200, "application/json", j);
|
|
}
|
|
|
|
// ===== Handler ===========================================================
|
|
void handleRoot() { sendeStatus("Skrift ESP32 MOSFET-Trigger"); }
|
|
void handleStatus() { sendeJson(); }
|
|
|
|
void handleTrigger() {
|
|
if (!autorisiert()) { server.send(403, "text/plain", "verboten\n"); return; }
|
|
letzterRequest = millis();
|
|
triggerZaehler++;
|
|
letzterTriggerS = millis() / 1000;
|
|
mosfetSchreiben(true);
|
|
if (PULSE_MS > 0) { // getimter Puls
|
|
pulsAktiv = true;
|
|
pulsBis = millis() + PULSE_MS;
|
|
sendeStatus("OK trigger (puls)");
|
|
} else { // dauerhaft an
|
|
pulsAktiv = false;
|
|
sendeStatus("OK trigger (latch)");
|
|
}
|
|
}
|
|
|
|
void handleOn() {
|
|
if (!autorisiert()) { server.send(403, "text/plain", "verboten\n"); return; }
|
|
letzterRequest = millis();
|
|
pulsAktiv = false;
|
|
mosfetSchreiben(true);
|
|
sendeStatus("OK on");
|
|
}
|
|
|
|
void handleOff() {
|
|
if (!autorisiert()) { server.send(403, "text/plain", "verboten\n"); return; }
|
|
letzterRequest = millis();
|
|
pulsAktiv = false;
|
|
mosfetSchreiben(false);
|
|
sendeStatus("OK off");
|
|
}
|
|
|
|
// ===== WLAN ==============================================================
|
|
void wlanStarten() {
|
|
if (USE_STATIC_IP) WiFi.config(IP_ADDR, IP_GATE, IP_MASK, IP_DNS);
|
|
WiFi.persistent(false); // keine NVS-Schreibzyklen bei jedem begin()
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.setSleep(false); // WLAN dauerhaft aktiv, kein Modem-Sleep
|
|
WiFi.setAutoReconnect(true); // Treiber verbindet selbst nach
|
|
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
|
}
|
|
|
|
// ===== Setup =============================================================
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
delay(50);
|
|
|
|
pinMode(MOSFET_PIN, OUTPUT);
|
|
mosfetSchreiben(false); // sicher AUS beim Start
|
|
|
|
wlanStarten();
|
|
Serial.print("Verbinde WLAN");
|
|
unsigned long start = millis();
|
|
while (WiFi.status() != WL_CONNECTED && millis() - start < WIFI_BOOT_TIMEOUT_MS) {
|
|
delay(300);
|
|
Serial.print(".");
|
|
}
|
|
Serial.println();
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
Serial.print("Verbunden. IP: "); Serial.println(WiFi.localIP());
|
|
Serial.print("RSSI: "); Serial.println(WiFi.RSSI());
|
|
} else {
|
|
// Kein Bootloop: Server startet trotzdem, loop versucht weiter.
|
|
Serial.println("WLAN noch nicht da, versuche im Hintergrund weiter.");
|
|
offlineSeit = millis();
|
|
}
|
|
|
|
if (USE_MDNS && MDNS.begin(MDNS_NAME)) {
|
|
MDNS.addService("http", "tcp", 80);
|
|
Serial.printf("Erreichbar als http://%s.local/\n", MDNS_NAME);
|
|
}
|
|
|
|
server.on("/", handleRoot);
|
|
server.on("/status", handleStatus);
|
|
server.on("/trigger", handleTrigger);
|
|
server.on("/on", handleOn);
|
|
server.on("/off", handleOff);
|
|
server.onNotFound([]() { server.send(404, "text/plain", "nicht gefunden\n"); });
|
|
server.begin();
|
|
Serial.println("HTTP-Server auf Port 80 gestartet.");
|
|
}
|
|
|
|
// ===== Loop ==============================================================
|
|
void loop() {
|
|
server.handleClient();
|
|
|
|
// WLAN ueberwachen, Reconnect statt erneutem begin().
|
|
static unsigned long letzteWlanPruefung = 0;
|
|
if (millis() - letzteWlanPruefung > WIFI_CHECK_MS) {
|
|
letzteWlanPruefung = millis();
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
offlineSeit = 0;
|
|
} else {
|
|
if (offlineSeit == 0) offlineSeit = millis();
|
|
WiFi.reconnect();
|
|
// Dauerhaft offline: einmal sauber neu starten.
|
|
if (millis() - offlineSeit > WIFI_GIVEUP_MS) {
|
|
Serial.println("WLAN dauerhaft weg, Neustart.");
|
|
mosfetSchreiben(false); // Last vor dem Reset abschalten
|
|
delay(100);
|
|
ESP.restart();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Getimten Puls beenden (rollover-sicher).
|
|
if (pulsAktiv && (long)(millis() - pulsBis) >= 0) {
|
|
pulsAktiv = false;
|
|
mosfetSchreiben(false);
|
|
}
|
|
|
|
// Fail-safe: Latch beenden, wenn AutoRecover sich nicht mehr meldet.
|
|
if (mosfetAn && !pulsAktiv && MAX_LATCH_MS > 0
|
|
&& millis() - letzterRequest > MAX_LATCH_MS) {
|
|
Serial.println("Latch-Timeout, schalte aus.");
|
|
mosfetSchreiben(false);
|
|
}
|
|
} |