esp/skrift_mosfet.ino aktualisiert
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user