esp/skrift_mosfet.ino aktualisiert

This commit is contained in:
2026-09-23 15:33:47 +02:00
parent 16d603b19f
commit e2c64da8d9

View File

@@ -1,64 +1,67 @@
/*
* Skrift – ESP MOSFET-Trigger („Stromzufuhr aktivieren")
* ===========================================================================
* Kleiner WLAN-Empfänger für einen N-Kanal-MOSFET (Low-Side-Schalter).
* AutoRecover ruft bei jedem Plotter-Recover-Fenster den Endpunkt /trigger auf;
* der ESP schaltet dann den MOSFET (GPIO) und aktiviert damit die Stromzufuhr.
*
* Läuft auf ESP8266 (NodeMCU/Wemos D1 mini) UND ESP32 – die passenden Bibliotheken
* werden über die Board-Auswahl in der Arduino IDE automatisch gewählt.
* 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) – Pin-Zustand, Uptime, IP
* /trigger MOSFET aktivieren (Puls von PULSE_MS ms; 0 = dauerhaft an)
* /on MOSFET dauerhaft AN
* /off MOSFET AUS
* Optionaler Schutz: ist AUTH_TOKEN gesetzt, muss jeder Aufruf ?key=<token>
* mitschicken (muss zu esp_key in der AutoRecover-config.json passen).
* / 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 (Low-Side, N-Kanal-Logic-Level-MOSFET, z. B. IRLZ44N / AO3400-Modul):
* ESP GPIO (MOSFET_PIN) ─► Gate (ggf. 100–220 Ω in Reihe)
* Gate ─► 100 kΩ ─► GND (Pulldown, damit der MOSFET beim Booten sicher AUS ist)
* Last(+) an Versorgung+, Last(−) ─► Drain ; Source ─► GND (gemeinsame Masse!)
* GND von ESP und Last-Versorgung verbinden.
* ACTIVE_HIGH=true: GPIO HIGH = MOSFET leitet = „Strom an".
* Verdrahtung: GPIO 23 -> Gate (100 Ohm), Gate -> 100 kOhm -> GND,
* Source -> GND, Drain -> Minus der Last, Massen verbinden.
*/
#if defined(ESP32)
#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);
#else
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
#endif
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
// ── Konfiguration ──────────────────────────────────────────────────────────
const char* WIFI_SSID = "DEIN_WLAN";
const char* WIFI_PASS = "DEIN_WLAN_PASSWORT";
// ===== Konfiguration =====================================================
const char* WIFI_SSID = "Netz-Orth";
const char* WIFI_PASS = "PLATZHALTER@2025";
// GPIO, an dem das MOSFET-Gate hängt. ESP8266: 5 = D1 (Wemos/NodeMCU). ESP32: z. B. 23.
const int MOSFET_PIN = 5;
const bool ACTIVE_HIGH = true; // true: HIGH = MOSFET an ("Strom an")
const unsigned long PULSE_MS = 3000; // /trigger: Dauer der Aktivierung (0 = latch, bleibt an)
// 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
// Optionaler Schutz. Leer = kein Schutz. Muss zu esp_key in der config.json passen.
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 = "";
// Optionale feste IP (empfohlen, damit AutoRecover eine stabile Adresse hat).
// Auf true setzen und Werte anpassen; sonst DHCP.
const bool USE_STATIC_IP = false;
IPAddress IP_ADDR (192, 168, 2, 50);
IPAddress IP_GATE (192, 168, 2, 1);
IPAddress IP_MASK (255, 255, 255, 0);
IPAddress IP_DNS (192, 168, 2, 1);
// 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);
// ── Zustand ────────────────────────────────────────────────────────────────
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);
@@ -71,45 +74,108 @@ bool autorisiert() {
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 += "ip="; s += WiFi.localIP().toString(); s += "\n";
s += "uptime_s="; s += String(millis() / 1000); s += "\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 handleRoot() { sendeStatus("Skrift ESP MOSFET-Trigger"); }
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
if (PULSE_MS > 0) { // getimter Puls
pulsAktiv = true;
pulsBis = millis() + PULSE_MS;
sendeStatus("OK trigger (puls)");
} else { // dauerhaft an (latch)
} else { // dauerhaft an
pulsAktiv = false;
sendeStatus("OK trigger (latch)");
}
}
void handleOn() { if (!autorisiert()) { server.send(403, "text/plain", "verboten\n"); return; } pulsAktiv = false; mosfetSchreiben(true); sendeStatus("OK on"); }
void handleOff() { if (!autorisiert()) { server.send(403, "text/plain", "verboten\n"); return; } pulsAktiv = false; mosfetSchreiben(false); sendeStatus("OK off"); }
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);
pinMode(MOSFET_PIN, OUTPUT);
mosfetSchreiben(false); // sicher AUS beim Start
delay(50);
if (USE_STATIC_IP) WiFi.config(IP_ADDR, IP_GATE, IP_MASK, IP_DNS);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
pinMode(MOSFET_PIN, OUTPUT);
mosfetSchreiben(false); // sicher AUS beim Start
wlanStarten();
Serial.print("Verbinde WLAN");
while (WiFi.status() != WL_CONNECTED) { delay(400); Serial.print("."); }
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < WIFI_BOOT_TIMEOUT_MS) {
delay(300);
Serial.print(".");
}
Serial.println();
Serial.print("Verbunden. IP: "); Serial.println(WiFi.localIP());
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);
@@ -118,19 +184,39 @@ void setup() {
Serial.println("HTTP-Server auf Port 80 gestartet.");
}
// ===== Loop ==============================================================
void loop() {
server.handleClient();
// WLAN bei Bedarf neu verbinden (Plotter läuft lange).
// WLAN ueberwachen, Reconnect statt erneutem begin().
static unsigned long letzteWlanPruefung = 0;
if (millis() - letzteWlanPruefung > 10000) {
if (millis() - letzteWlanPruefung > WIFI_CHECK_MS) {
letzteWlanPruefung = millis();
if (WiFi.status() != WL_CONNECTED) WiFi.begin(WIFI_SSID, WIFI_PASS);
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.
// 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);
}
}