Problem mit Nodemcu v3 HTML Darstellung

Moin Moin,
Kurz zu meinem Programm ich möchte per Webinterface 4 Pumpen über einen Website steuern. Wenn man auf Button EIN klickt soll dieser aber nach einer definierten Zeit wieder ausgehen.

Zu dem Problem:
Die Darstellung auf der Website funktioniert nicht. Im Serial Monitor sehe ich Client verbunden und danach wieder direkt getrennt. Liegt der Fehler vielleicht in meinen Millis Funktionen ?

Hier einmal der Code:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

char* ssid = "xxxx";
char* password = "xxxx";

WiFiServer server(80);

String header;

String output5State = "off";
String output4State = "off";
String output0State = "off";
String output2State = "off";

unsigned long startTimePumpe1;
unsigned long startTimePumpe2;
unsigned long startTimePumpe3;
unsigned long startTimePumpe4;

const unsigned long intervalpumpe1 = 1000;
const unsigned long intervalpumpe2 = 1000;
const unsigned long intervalpumpe3 = 1000;
const unsigned long intervalpumpe4 = 1000;

const int output5 = 5;
const int output4 = 4;
const int output0 = 0;
const int output2 = 2;

void setup()
{
  Serial.begin(115200);
  pinMode(output5, OUTPUT);
  pinMode(output4, OUTPUT);
  pinMode(output0, OUTPUT);
  pinMode(output2, OUTPUT);

  digitalWrite(output5, LOW);
  digitalWrite(output4, LOW);
  digitalWrite(output0, LOW);
  digitalWrite(output2, LOW);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }
  Serial.println("");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop()
{
  WiFiClient client = server.available();

  if (client) {
    Serial.println("Neuer Client.");
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        header += c;
        if (c == '\n') {
          if (currentLine.length() == 0) {
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            static bool merkerpumpe1 = false;
            static bool merkerpumpe2 = false;
            static bool merkerpumpe3 = false;
            static bool merkerpumpe4 = false;

            if (millis() - startTimePumpe1 > intervalpumpe1)
            {
              merkerpumpe1 = !merkerpumpe1;
              startTimePumpe1 = millis();
            }
            if (header.indexOf("GET /5/on") && merkerpumpe1 == false) {
              Serial.println("GPIO 5 on");
              output5State = "on";
              digitalWrite(output5, LOW);
            }
            if (digitalRead(5) == LOW && merkerpumpe1 == true)
            {
              digitalWrite(2, HIGH);
              output5State = "off";
            }

            if (millis() - startTimePumpe2 > intervalpumpe2)
            {
              merkerpumpe2 = !merkerpumpe2;
              startTimePumpe2 = millis();
            }
            if (header.indexOf("GET /4/on") && merkerpumpe2 == false) {
              Serial.println("GPIO 4 on");
              output4State = "on";
              digitalWrite(output4, LOW);
            }
            if (digitalRead(4) == LOW && merkerpumpe2 == true)
            {
              digitalWrite(4, HIGH);
              output4State = "off";
            }

            if (millis() - startTimePumpe3 > intervalpumpe3)
            {
              merkerpumpe3 = !merkerpumpe3;
              startTimePumpe3 = millis();
            }
            if (header.indexOf("GET /0/on") && merkerpumpe3 == false) {
              Serial.println("GPIO 0 on");
              output0State = "on";
              digitalWrite(output0, LOW);
            }
            if (digitalRead(0) == LOW && merkerpumpe3 == true)
            {
              digitalWrite(0, HIGH);
              output0State = "off";
            }

            if (millis() - startTimePumpe4 > intervalpumpe4)
            {
              merkerpumpe4 = !merkerpumpe4;
              startTimePumpe4 = millis();
            }
            if (header.indexOf("GET /2/on") && merkerpumpe4 == false) {
              Serial.println("GPIO 2 on");
              output2State = "on";
              digitalWrite(output2, LOW);
            }
            if (digitalRead(2) == LOW && merkerpumpe4 == true)
            {
              digitalWrite(2, HIGH);
              output2State = "off";
            }

            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #333344; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #888899;}</style></head>");
            
            client.println("<body><h1>GREENHOUSE</h1>");
            
            client.println("<p>GPIO 5 - State " + output5State + "</p>");
            if (output5State == "off") {
              client.println("<p><a href=\"/5/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\"/5/off\"><button class=\"button button2\">AUS</button></a></p>");
            }

            client.println("<p>GPIO 4 - State " + output4State + "</p>");    
            if (output4State == "off") {
              client.println("<p><a href=\"/4/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\"/4/off\"><button class=\"button button2\">AUS</button></a></p>");
              // Zeige den aktuellen Status, und AN/AUS Buttons for GPIO 0  

              client.println("<p>GPIO 0 - State " + output0State + "</p>");
            }
            if (output0State == "off") {
              client.println("<p><a href=\“/0/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\“/0/off\"><button class=\"button button2\">AUS</button></a></p>");
            }

            client.println("<p>GPIO 2 - State " + output2State + "</p>");
            if (output2State == "off") {
              client.println("<p><a href=\“/2/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\“/2/off\"><button class=\"button button2\">AUS</button></a></p>");
            }

            client.println("</body></html>");
            client.println();
            break;
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
        }
      }
      header = "";
      client.stop();
      Serial.println("Client getrennt.");
      Serial.println("");
    }
  }
}

Vielen dank für jeden Hilfe

Du darfst die Abfrage millis() nicht innerhalb des html-Teils machen.

Setze Dir dort nur eine Variable und dann in einer weiteren Funktion das starten und beenden der Pumpe.

So eben ausgelagert. Jetzt schreibt er im Seriellen Monitor dauerhaft
GPIO 5 on
GPIO 4 on
GPIO 0 on
GPIO 2 on
GPIO 5 on
GPIO 4 on

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

char* ssid = "xxxx";
char* password = "xxxx";

WiFiServer server(80);

String header;

String output5State = "off";
String output4State = "off";
String output0State = "off";
String output2State = "off";

unsigned long startTimePumpe1;
unsigned long startTimePumpe2;
unsigned long startTimePumpe3;
unsigned long startTimePumpe4;

const unsigned long intervalpumpe1 = 1000;
const unsigned long intervalpumpe2 = 1000;
const unsigned long intervalpumpe3 = 1000;
const unsigned long intervalpumpe4 = 1000;

const int output5 = 5;
const int output4 = 4;
const int output0 = 0;
const int output2 = 2;

void setup()
{
  Serial.begin(115200);
  pinMode(output5, OUTPUT);
  pinMode(output4, OUTPUT);
  pinMode(output0, OUTPUT);
  pinMode(output2, OUTPUT);

  digitalWrite(output5, LOW);
  digitalWrite(output4, LOW);
  digitalWrite(output0, LOW);
  digitalWrite(output2, LOW);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }
  Serial.println("");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop()
{
Website();
Pumpen();
}

void Pumpen()
{
static bool merkerpumpe1 = false;
            static bool merkerpumpe2 = false;
            static bool merkerpumpe3 = false;
            static bool merkerpumpe4 = false;

            if (millis() - startTimePumpe1 > intervalpumpe1)
            {
              merkerpumpe1 = !merkerpumpe1;
              startTimePumpe1 = millis();
            }
            if (header.indexOf("GET /5/on") && merkerpumpe1 == false) {
              Serial.println("GPIO 5 on");
              output5State = "on";
              digitalWrite(output5, LOW);
            }
            if (digitalRead(5) == LOW && merkerpumpe1 == true)
            {
              digitalWrite(2, HIGH);
              output5State = "off";
            }

            if (millis() - startTimePumpe2 > intervalpumpe2)
            {
              merkerpumpe2 = !merkerpumpe2;
              startTimePumpe2 = millis();
            }
            if (header.indexOf("GET /4/on") && merkerpumpe2 == false) {
              Serial.println("GPIO 4 on");
              output4State = "on";
              digitalWrite(output4, LOW);
            }
            if (digitalRead(4) == LOW && merkerpumpe2 == true)
            {
              digitalWrite(4, HIGH);
              output4State = "off";
            }

            if (millis() - startTimePumpe3 > intervalpumpe3)
            {
              merkerpumpe3 = !merkerpumpe3;
              startTimePumpe3 = millis();
            }
            if (header.indexOf("GET /0/on") && merkerpumpe3 == false) {
              Serial.println("GPIO 0 on");
              output0State = "on";
              digitalWrite(output0, LOW);
            }
            if (digitalRead(0) == LOW && merkerpumpe3 == true)
            {
              digitalWrite(0, HIGH);
              output0State = "off";
            }

            if (millis() - startTimePumpe4 > intervalpumpe4)
            {
              merkerpumpe4 = !merkerpumpe4;
              startTimePumpe4 = millis();
            }
            if (header.indexOf("GET /2/on") && merkerpumpe4 == false) {
              Serial.println("GPIO 2 on");
              output2State = "on";
              digitalWrite(output2, LOW);
            }
            if (digitalRead(2) == LOW && merkerpumpe4 == true)
            {
              digitalWrite(2, HIGH);
              output2State = "off";
            }
}

void Website()
{
WiFiClient client = server.available();

  if (client) {
    Serial.println("Neuer Client.");
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        header += c;
        if (c == '\n') {
          if (currentLine.length() == 0) {
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #333344; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #888899;}</style></head>");
            
            client.println("<body><h1>GREENHOUSE</h1>");
            
            client.println("<p>GPIO 5 - State " + output5State + "</p>");
            if (output5State == "off") {
              client.println("<p><a href=\"/5/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\"/5/off\"><button class=\"button button2\">AUS</button></a></p>");
            }

            client.println("<p>GPIO 4 - State " + output4State + "</p>");
            if (output4State == "off") {
              client.println("<p><a href=\"/4/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\"/4/off\"><button class=\"button button2\">AUS</button></a></p>");

              client.println("<p>GPIO 0 - State " + output0State + "</p>");
            }
            if (output0State == "off") {
              client.println("<p><a href=\“/0/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\“/0/off\"><button class=\"button button2\">AUS</button></a></p>");
            }

            client.println("<p>GPIO 2 - State " + output2State + "</p>");
            if (output2State == "off") {
              client.println("<p><a href=\“/2/on\"><button class=\"button\">EIN</button></a></p>");
            } else {
              client.println("<p><a href=\“/2/off\"><button class=\"button button2\">AUS</button></a></p>");
            }

            client.println("</body></html>");
            client.println();
            break;
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
        }
      }
      header = "";
      client.stop();
      Serial.println("Client getrennt.");
      Serial.println("");
    }
  }
}

du hast da doch den

#include <ESP8266WebServer.h>

dann halte dich auch an das Beispiel

ESP8266WebServer HalloServer

wollen wir das ganze gemeinsam umbauen? Am ESP8266 gibts echt keinen Grund den ganzen HTML Header händisch hinzuschreiben. Das kann der ESP8266WebServer eigentlich selber - richtig.

Das millis() ist nur für die Steuerung da und gehört ausserhalb des HTML-Teils - der Rest an html muss da bleiben wo es ist.

Ich hät' mich gerne mal rangesetzt und das mit einem ArraySatz gebaut, aber mal arbeitest mit VariablenNamen, mal mit PinNummern und in Deinem ersten Sketchist da auch noch was kurioses: Zeile 90 / Zeile 92 :slight_smile:

Bei @noiasca bist in guten Händen.

ja diese Möglichkeit würde ich gerne annehmen habe mir das HalloServer Beispiel angeschaut stehe aber jetzt gerade auf dem Schlauch den Code darin rein zu bekommen

lies bitte mal das:

https://werner.rothschopf.net/microcontroller/202108_esp8266_esp12f_relay_x8_en.htm

wenn dir das gezeigte Layout grundsätzlich entgegenkommt, dann lad dir bitte das Zip am Ende der Seite runter.

Imho brauchen wir nur die Konfigurations-Datei auf deine 4 Relais reduzieren und dann eben die "ausschalt timer einbauen". Aber damit hättest du eine funktionierende Weboberfläche, ein Update der "Schalter" am websever, zu 98% fertig.

Wenn du dann noch ein paar Farben änderst - das bekommst dann wieder alleine hin.

Ich such in der zwischenzeit mal einen freien NodeMCU bei mir...

Ja das sieht sehr sehr gut aus

dann wäre das der 1te Schritt:

Step1:

im esp_webserver_generic.ino Haupttab

#define USE_BOARD auf 1 stellen

// #include <credentials.h> auskommentieren
und die wifi credentials richtig eintragen

nun müsste der Upload schon klappen,
der NodeMCU soll sich mit deinem Wifi verbinden können
und mit der IP vom Seriellen Monitor solltest du den NodeMCU auch erreichen.

also hab den Haupttab editiert
der serielle Monitor zeigt dauernd:
arted

arted

arted

und die website sieht so aus:

wenn du laufend ein "started" siehst, dann bootet er wohl dauernd.
nicht gut.

stellen wir mal die Pins richtig.

geh mal in den Tab config1.htm
du willst offenbar die pins 5, 4, 0 und 2 haben oder?

dann probier mal das, ich hab mal die PWM pins weggegeben und die LED activity auf 255 gesetzt:

#pragma once
#if USE_BOARD == 1                  // this is an ID for the configuration
#define TXT_BOARDNAME "Generic ESP Webserver" // the name of the board on webgui and Serial
HardwareSerial &Terminal = Serial;            // if you would need debug messages on another Serial, you could define it

// assign pins to arrays:
// Pins, which should be readed
Pin inputPin[] {
  // the pin    a name for the website
  {0,    "IO00"},   // the NodeMCU has a flash button
};

// Pins, which can be switched on or off
OutputPin outputPin[] {
  // pin  a name  LOW active or HIGH active
 // {2,    "IO02", HIGH},    // on some ESP8266 there is a blue LED on the ESP
  {5,    "IO05", LOW},
  {4,    "IO04", LOW},
  {0,    "IO00", LOW},
  {2,    "IO02", LOW},
};

#define NO_PWMPIN

const byte wifiActivityPin = 255;   // if you want to indicate Wifi activity - set to 255 if activity LED is not needed

5,4,0,2 genau richtig, jetzt beim hochladen hat der Esp sich auch gefangen das läuft :+1:

gut dann brauchen wir nur mehr die timer sache richtig?

genau das wäre noch gut die pumpen sollen nach einer definierbaren zeit automatisch wieder ausgestellt werden

ja das muss ich erst selber machen. sollte aber übersichtlich sein.
Machen wirs gleich sauber.
Erst brauchen wir mal einen variable. Ergänze die struktur für die pins

gehe in den general.h und ergänze

struct OutputPin             // used for output pins (pins for digitalWrite mainly)
{
  const uint8_t pin;         // the GPIO
  const char *label;         // a speaking name for the pin
  const uint8_t ON;          // level for ON (for high active set to HIGH)
  uint32_t previousMillis;  // timemanagement.
};

im Tab server wird so ca in Zeile 400 der pin umgeschaltet.
Da kommt eine Zeile dazu:


      if (digitalRead(outputPin[i].pin) != outputPin[i].ON)  // toggle: if the pin was on - switch it off and vice versa
      {
        digitalWrite(outputPin[i].pin, outputPin[i].ON);
        outputPin[i].previousMillis = millis();              // store timestamp
      } else {
        digitalWrite(outputPin[i].pin, !outputPin[i].ON);
      }

jetzt brauchen wir noch eine function die durch die pins looped und gegebenenfalls abschaltet.
Wie waren deine Ausschaltzeiten?

die Zeilen hab ich eingefügt super danke

Ausschaltzeiten wären bei 2000ms momentan

finally:

im Haupttab machst dir eine Funktion oberhalb von setup

void doOutputTimer()
{
  for (size_t i = 0; i < (sizeof(outputPin) / sizeof(outputPin[0])); i++)
  {
    if ((digitalRead(outputPin[i].pin) == outputPin[i].ON) && millis() - outputPin[i].previousMillis > 2000)
    {
      digitalWrite(outputPin[i].pin, !outputPin[i].ON); // switch off
    }
  }
}

im loop() rufst du dann nur einfach das

doOutputTimer();

auf

und dann gehst noch mal in den Tab general.h und initialisierst die neue Variable mit 0, dann ist das Warning auch weg

  uint32_t previousMillis = 0;   // timemanagement.

so, bei mir funktioniert das jetzt mal.

eins fällt mir noch auf:
Aktuell ist der Aktualisierungsintervall auf 5 Sekunden. Das könntest reduzieren auf 2 Sekunden, dann siehst du schneller die Updates auf der Weboberfläche.

Ist im Haupttab so ca Zeile 93:

const uint16_t ajaxIntervall = 5;      // intervall for AJAX or fetch API call of website in seconds

sauber traumhaft vielen lieben dank es funktioniert und sieht auch sehr gut aus. ein schönes Wochenende wünsche ich

wart noch, ich glaube das Struct im general.h soll so aussehen:

struct OutputPin             // used for output pins (pins for digitalWrite mainly)
{
    const uint8_t pin;         // the GPIO
    const char *label;         // a speaking name for the pin
    const uint8_t ON;          // level for ON (for high active set to HIGH)
    uint32_t previousMillis = 0;   // timemanagement.
    // wir brauchen einen Constructor wegen der abweichenden Initialisierung bei der Objektanlage:
    OutputPin (uint8_t pin, const char *label, uint8_t ON) : pin(pin), label(label), ON(ON) {}
};

die restlichen Sachen schaffst selber.
Die Farben sind eh alle im CSS als CSS Variablen.
Schmökere ein wenig im Code. Imho sollen die wichtigsten Sachen darin erklärt sein.