Gewächshausbewässerung, Sperre immer wieder aktiv

Ich habe mir eine kleine Bewässerungsanlage für mein Gewächshaus gebaut. Insgesamt 4 Kreisläufe, jeweils mit einem Feuchtigkeitssensor. Ich habe mir eine HTML Seite gebaut, dabei ist mir aufgefallen, das die Sperre immer wieder aktiv ist, obwohl das jeweilige Relais nicht aktiv war und der Wert nicht unter "Trocken" war, wenn der Kreislauf nicht deaktiviert wurde.
Woran liegt es?
Desweiteren ist es aktuell so, wenn der Arduino Neustartet, werden alle Relais am Tag eingeschaltet (wenn die Kreisläufe nicht per Schalter deaktiviert worden sind), obwohl der Feuchtigkeitswert passend ist, ich denke es liegt daran das er etwas länger braucht um die Wert auszuwerten, wie kann man das verhindern?

Hier mal der Sketch:

// Gewaechshauswebif v3.1 vom 28.06.2022
const byte LDR = 0; // LDR am Pin A0

const byte Feuchtigkeitssensor1pin = 1; // Anschluss des 1. Feuchtigkeitssensor am Pin A1
const byte Feuchtigkeitssensor2pin = 2; // Anschluss des 2. Feuchtigkeitssensor am Pin A2
const byte Feuchtigkeitssensor3pin = 3; // Anschluss des 3. Feuchtigkeitssensor am Pin A3
const byte Feuchtigkeitssensor4pin = 4; // Anschluss des 4. Feuchtigkeitssensor am Pin A4

const int MaximaleFeuchte = 320; // Maximaler Feuchtigkeitswert (kleinster Wert)
const int MinimaleFeuchte = 1025; // Minimaler Feuchtigkeitswert (größter Wert)
float Spanne; // Zum Berechnen der Spanne
const int Trocken = 25; // Wert ab dem Bewässert werden soll

float Prozent1; // Zum Berechnen der Feuchtigkeit in Prozent vom 1. Feuchtigkeitssensor
float Prozent2; // Zum Berechnen der Feuchtigkeit in Prozent vom 2. Feuchtigkeitssensor
float Prozent3; // Zum Berechnen der Feuchtigkeit in Prozent vom 3. Feuchtigkeitssensor
float Prozent4; // Zum Berechnen der Feuchtigkeit in Prozent vom 4. Feuchtigkeitssensor

int Schalter1 = 2; // Schalter zum Öffnen aller Ventile , am Pin D2
int Schalter2 = 3; // Schalter zum Deaktivieren des Kreislauf 1, am Pin D3
int Schalter3 = 4; // Schalter zum Deaktivieren des Kreislauf 2, am Pin D4
int Schalter4 = 5; // Schalter zum Deaktivieren des Kreislauf 3, am Pin D5
int Schalter5 = 6; // Schalter zum Deaktivieren des Kreislauf 4, am Pin D6

const byte Relais1pin = 7; // Relais 1 für den Kreislauf 1, am Pin D6
const byte Relais2pin = 8; // Relais 2 für den Kreislauf 2, am Pin D7
const byte Relais3pin = 9; // Relais 3 für den Kreislauf 3, am Pin D8
const byte Relais4pin = 19; // Relais 4 für den Kreislauf 4, am Pin A5

const int TAG = 20; // Wert ab dem der Tag beginnt

float sperre1restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 1
float sperre2restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 2
float sperre3restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 3
float sperre4restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 4

//Ethernet Shield
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x00, 0x10, 0xFA, 0x6E, 0x28, 0x4A }; // MAC Adresse des Arduino Boards

EthernetServer server(80); // Port Einstellung (Standard für HTML : 80)

void setup ()
{
  // Ethernet Shield
  Ethernet.begin(mac); // Ethernet initialisieren
  server.begin(); // Auf Clients warten
  Serial.print("Arduino's IP Address: ");
  Serial.println(Ethernet.localIP());

  Serial.print("DNS Server's IP Address: ");
  Serial.println(Ethernet.dnsServerIP());

  Serial.print("Gateway's IP Address: ");
  Serial.println(Ethernet.gatewayIP());

  Serial.print("Network's Subnet Mask: ");
  Serial.println(Ethernet.subnetMask());

  Serial.begin(57600); // Serielle Schnittstelle initialisieren

  pinMode(Feuchtigkeitssensor1pin, INPUT); // Pin Modus definieren, Eingang
  pinMode(Feuchtigkeitssensor2pin, INPUT); // Pin Modus definieren, Eingang
  pinMode(Feuchtigkeitssensor3pin, INPUT); // Pin Modus definieren, Eingang
  pinMode(Feuchtigkeitssensor4pin, INPUT); // Pin Modus definieren, Eingang

  pinMode(Relais1pin, OUTPUT); // Pin Modus definieren, Ausgang
  pinMode(Relais2pin, OUTPUT); // Pin Modus definieren, Ausgang
  pinMode(Relais3pin, OUTPUT); // Pin Modus definieren, Ausgang
  pinMode(Relais4pin, OUTPUT); // Pin Modus definieren, Ausgang

  pinMode(Schalter1, INPUT_PULLUP);  // Schalter zum Öffnen aller Ventile
  pinMode(Schalter2, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 1
  pinMode(Schalter3, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 2
  pinMode(Schalter4, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 3
  pinMode(Schalter5, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 4

  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
}

void loop ()
{
  int LDR1 = analogRead (0) * 1.0; // LDR1

  int Feuchtigkeitssensor1pin = analogRead(1); // 1. Feuchtigkeitssensor
  int Feuchtigkeitssensor2pin = analogRead(2); // 2. Feuchtigkeitssensor
  int Feuchtigkeitssensor3pin = analogRead(3); // 3. Feuchtigkeitssensor
  int Feuchtigkeitssensor4pin = analogRead(4); // 4. Feuchtigkeitssensor

  int Schalter1Status = digitalRead(Schalter1); // Auslesen des Zustand vom Schalter1
  int Schalter2Status = digitalRead(Schalter2); // Auslesen des Zustand vom Schalter2
  int Schalter3Status = digitalRead(Schalter3); // Auslesen des Zustand vom Schalter3
  int Schalter4Status = digitalRead(Schalter4); // Auslesen des Zustand vom Schalter4
  int Schalter5Status = digitalRead(Schalter5); // Auslesen des Zustand vom Schalter5

  Prozent1 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor1pin); // Prozent berechnen für den 1. Feuchtigkeitssensor
  Prozent2 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor2pin); // Prozent berechnen für den 2. Feuchtigkeitssensor
  Prozent3 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor3pin); // Prozent berechnen für den 3. Feuchtigkeitssensor
  Prozent4 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor4pin); // Prozent berechnen für den 4. Feuchtigkeitssensor

  if (Schalter1Status == LOW)
  {
    digitalWrite(Relais1pin, HIGH); // Relais 1 aktivieren
    digitalWrite(Relais2pin, HIGH); // Relais 2 aktivieren
    digitalWrite(Relais3pin, HIGH); // Relais 3 aktivieren
    digitalWrite(Relais4pin, HIGH); // Relais 4 aktivieren
  }


  if (LDR1 < TAG && Schalter1Status == HIGH)
  {
    digitalWrite(Relais1pin, LOW); // Relais 1 deaktivieren
    digitalWrite(Relais2pin, LOW); // Relais 2 deaktivieren
    digitalWrite(Relais3pin, LOW); // Relais 3 deaktivieren
    digitalWrite(Relais4pin, LOW); // Relais 4 deaktivieren
  }

  if (LDR1 >= TAG && Schalter1Status == HIGH) // Wenn es Tag ist
  {
    // 1. Kreislauf
    if ((Feuchtigkeitssensor1pin >= Trocken) && (sperre1() == false) && (Schalter2Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre1 nicht aktiv ist und Schalter 2 nicht aktiv ist
    {
      digitalWrite(Relais1pin, HIGH); // Relais1 aktivieren
    }
    if (digitalRead(Relais1pin) == HIGH)
    {
      static unsigned long laststart1;
      if (millis() - laststart1 > 3800) // Bewässerungszeit 3,8s
      {
        laststart1 = millis();
        digitalWrite(Relais1pin, LOW); // Relais1 deaktivieren
      }
    }

    // 2. Kreislauf
    if ((Feuchtigkeitssensor2pin >= Trocken) && (sperre2() == false) && (Schalter3Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre2 nicht aktiv ist und Schalter 3 nicht aktiv ist
    {
      digitalWrite(Relais2pin, HIGH); // Relais2 aktivieren
    }
    if (digitalRead(Relais2pin) == HIGH)
    {
      static unsigned long laststart2;
      if (millis() - laststart2 > 3800) // Bewässerungszeit 3,8s
      {
        laststart2 = millis();
        digitalWrite(Relais2pin, LOW); // Relais2 deaktivieren
      }
    }

    // 3. Kreislauf
    if ((Feuchtigkeitssensor3pin >= Trocken) && (sperre3() == false) && (Schalter4Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre3 nicht aktiv ist und Schalter 4 nicht aktiv ist
    {
      digitalWrite(Relais3pin, HIGH); // Relais3 aktivieren
    }
    if (digitalRead(Relais3pin) == HIGH)
    {
      static unsigned long laststart3;
      if (millis() - laststart3 > 3800) // Bewässerungszeit 3,8s
      {
        laststart3 = millis();
        digitalWrite(Relais3pin, LOW); // Relais3 deaktivieren
      }
    }

    // 4. Kreislauf
    if ((Feuchtigkeitssensor4pin >= Trocken) && (sperre4() == false) && (Schalter5Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre4 nicht aktiv ist und Schalter 5 nicht aktiv ist
    {
      digitalWrite(Relais4pin, HIGH); // Relais4 aktivieren
    }
    if (digitalRead(Relais4pin) == HIGH)
    {
      static unsigned long laststart4;
      if (millis() - laststart4 > 3800) // Bewässerungszeit 3,8s
      {
        laststart4 = millis();
        digitalWrite(Relais4pin, LOW); // Relais4 deaktivieren
      }
    }
  }

  if (sperre1() == false)
  {
    sperre1restzeit = 0;
  }

    if (sperre2() == false)
  {
    sperre2restzeit = 0;
  }
  
    if (sperre3() == false)
  {
    sperre3restzeit = 0;
  }
  
    if (sperre4() == false)
  {
    sperre4restzeit = 0;
  }
  
  EthernetClient client = server.available(); //Prüfen, ob Client Seite aufruft
  if (client)
  { // Seitenaufruf durch User
    // Ausgabe in HTML
    server.print(F("HTTP/1.1 200 OK\r\nServer: Arduino UNO R3\r\nContent-Type: text/html\r\n\r\n"
                   "<!DOCTYPE html>"
                   "<html lang='de'>"
                   "<head>"
                   "<meta charset='utf-8'>"
                   "<link rel=icon type=image/vnd.microsoft.icon href=http://IP DES NAS/Gfp.png>"
                   "<title>Gewächshausbewässerung</title>"
                   "</head>"
                   "<style>"
                   "body { background-image: url('http://IP DES NAS/Gewächshaus.png');"
                   "background-repeat: no-repeat;"
                   "background-attachment: fixed;"
                   "background-position: center;"
                   "</style>"
                   "<center><img src=http://IP DES NAS/Gfp2.png width=390 height=54><BR/>"
                   "<font size=7><strong>Gewächshausbewässerung</strong></font size><BR/><BR/>"
                   "<font size=5>LDR: "));
    server.print(LDR1);
    server.print(F("<BR/>Bewässerung: "));
    server.print(LDR1 >= TAG ? "Ein" : "Aus");
    server.print(F("<BR/>Schalter1 (Alle Ventile geöffnet): "));
    server.print(Schalter1Status ? "Aus" : "Ein");

    server.print(F("<BR/><BR/>"
                   "<table>"
                   "<table border=1>"
                   "<tr>"
                   "<td>Kreislauf</td>"
                   "<td align=right>1</td>"
                   "<td align=right>2</td>"
                   "<td align=right>3</td>"
                   "<td align=right>4</td>"
                   "</tr>"
                   "<tr>"
                   "<td>Kreislauf aktiv</td>"
                   "<td align = right>"));
    server.print(Schalter2Status ? "Ein" : "Aus");
    server.print(F("<td align = right>"));
    server.print(Schalter3Status ? "Ein" : "Aus");
    server.print(F("<td align = right>"));
    server.print(Schalter4Status ? "Ein" : "Aus");
    server.print(F("<td align = right>"));
    server.print(Schalter5Status ? "Ein" : "Aus");
    server.print(F("</tr>"
                   "<tr>"
                   "<td>Feuchtigkeit in %</td>"
                   "<td align = right>"));
    server.print(Prozent1);
    server.print(F("<td align = right>"));
    server.print(Prozent2);
    server.print(F("<td align = right>"));
    server.print(Prozent3);
    server.print(F("<td align = right>"));
    server.print(Prozent4);
    server.print(F("</tr>"
                   "<tr>"
                   "<td>Relaisstatus</td>"
                   "<td align = right>"));
    server.print(digitalRead(Relais1pin) ? "Ein" : "Aus");
    server.print(F("<td align = right>"));
    server.print(digitalRead(Relais2pin) ? "Ein" : "Aus");
    server.print(F("<td align = right>"));
    server.print(digitalRead(Relais3pin) ? "Ein" : "Aus");
    server.print(F("<td align = right>"));
    server.print(digitalRead(Relais4pin) ? "Ein" : "Aus");
    server.print(F("</tr>"
                   "<tr>"
                   "<td>Relaissperre</td>"
                   "<td align = right>"));
    server.print(sperre1() ? "Aktiv" : "Inaktiv");
    server.print(F("<td align = right>"));
    server.print(sperre2() ? "Aktiv" : "Inaktiv");
    server.print(F("<td align = right>"));
    server.print(sperre3() ? "Aktiv" : "Inaktiv");
    server.print(F("<td align = right>"));
    server.print(sperre4() ? "Aktiv" : "Inaktiv");
    server.print(F("</tr>"
                   "<tr>"
                   "<td>Rest Sperrzeit in Min.</td>"
                   "<td align = right>"));
    server.print(sperre1restzeit);
    server.print(F("<td align = right>"));
    server.print(sperre2restzeit);
    server.print(F("<td align = right>"));
    server.print(sperre3restzeit);
    server.print(F("<td align = right>"));
    server.print(sperre4restzeit);
    server.print(F("</tr>"
                   "</table>"
                   "</font size>"
                   "</center>"
                   "</body>"
                   "</html>"));
    delay(500); // Kurzer Delay, um Daten zu senden
    client.stop(); // Verbindung mit dem Client trennen
  }

  // Die Werte des Feuchtigkeitssensor Ausgeben
  static unsigned long lastprint;
  if (millis() - lastprint > 3000) // Testausgabe alle 3s
  {
    lastprint = millis();
    Serial.print(F("LDR: ")); Serial.println(LDR1); // LDR oben links
    Serial.print(F("Feuchtigkeitssensor1: ")); Serial.println(Feuchtigkeitssensor1pin); // 1. Feuchtigkeitssensor
    Serial.print(Prozent1); Serial.println(F(" % "));
    Serial.print(F("Feuchtigkeitssensor2: ")); Serial.println(Feuchtigkeitssensor2pin); // 2. Feuchtigkeitssensor
    Serial.print(Prozent2); Serial.println(F(" % "));
    Serial.print(F("Feuchtigkeitssensor3: ")); Serial.println(Feuchtigkeitssensor3pin); // 3. Feuchtigkeitssensor
    Serial.print(Prozent3); Serial.println(F(" % "));
    Serial.print(F("Feuchtigkeitssensor4: ")); Serial.println(Feuchtigkeitssensor4pin); // 4. Feuchtigkeitssensor
    Serial.print(Prozent4); Serial.println(F(" % "));
    Serial.print(F("Schalter 1: ")); Serial.println(Schalter1Status); // Zustand Schalter 1
    Serial.print(F("Schalter 2: ")); Serial.println(Schalter2Status); // Zustand Schalter 2
    Serial.print(F("Schalter 3: ")); Serial.println(Schalter3Status); // Zustand Schalter 3
    Serial.print(F("Schalter 4: ")); Serial.println(Schalter4Status); // Zustand Schalter 4
    Serial.print(F("Schalter 5: ")); Serial.println(Schalter5Status); // Zustand Schalter 5
    Serial.print(F("Relais 1: ")); Serial.println(digitalRead(Relais1pin)); // Zustand Relais 1
    Serial.print(F("Sperre Relais 1: ")); Serial.println(sperre1()); // Sperre Relais 1
    Serial.print(F("Relais 2: ")); Serial.println(digitalRead(Relais2pin)); // Zustand Relais 2
    Serial.print(F("Sperre Relais 2: ")); Serial.println(sperre2()); // Sperre Relais 2
    Serial.print(F("Relais 3: ")); Serial.println(digitalRead(Relais3pin)); // Zustand Relais 3
    Serial.print(F("Sperre Relais 3: ")); Serial.println(sperre3()); // Sperre Relais 3
    Serial.print(F("Relais 4: ")); Serial.println(digitalRead(Relais4pin)); // Zustand Relais 4
    Serial.print(F("Sperre Relais 4: ")); Serial.println(sperre4()); // Sperre Relais 4
    Serial.print(F("Sperre 1 Restzeit:")); Serial.println(sperre1restzeit); // Restzeit der Sperre 1
    Serial.print(F("Sperre 2 Restzeit:")); Serial.println(sperre2restzeit); // Restzeit der Sperre 2
    Serial.print(F("Sperre 3 Restzeit:")); Serial.println(sperre3restzeit); // Restzeit der Sperre 3
    Serial.print(F("Sperre 4 Restzeit:")); Serial.println(sperre4restzeit); // Restzeit der Sperre 4
  }
}

bool sperre1()
{
  static bool sperre1 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais1pin};

  if (!sperre1)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre1 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre1 = false;
  sperre1restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  return sperre1;
}

bool sperre2()
{
  static bool sperre2 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais2pin};

  if (!sperre2)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre2 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre2 = false;
  sperre2restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  return sperre2;
}

bool sperre3()
{
  static bool sperre3 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais3pin};

  if (!sperre3)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre3 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre3 = false;
  sperre3restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  if (sperre3 = false)
  return sperre3;
}

bool sperre4()
{
  static bool sperre4 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais4pin};

  if (!sperre4)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre4 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre4 = false;
  sperre4restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  return sperre4;
}

Desweiteren wir in der HTML Seite wenn die Sperrzeit abgelaufen dennoch ein Wert von z.B. 71203 angezeigt, das sollte ja diese Zeilen verhindern:

  if (sperre1() == false)
  {
    sperre1restzeit = 0;
  }

    if (sperre2() == false)
  {
    sperre2restzeit = 0;
  }
  
    if (sperre3() == false)
  {
    sperre3restzeit = 0;
  }
  
    if (sperre4() == false)
  {
    sperre4restzeit = 0;
  }

Was wieso geht es nicht? Habe ich da einen flaschen gedanken?

Bist Du Dir sicher?

Du hast int, wo Du nur const byte brauchst,

und int, wo unsigned int ausreicht

Allerdings verstehe ich nicht, warum Du da nicht den gesetzten Namen benutzt:

Hinweis:
Da muss A1 stehen und nicht 1!

Im setup weg damit:

Hier brauchst Du nur bool!

Und jetzt: hier beisst etwas ganz böse:

Feuchtigkeitssensor1pin ist ein Pin und nicht der Wert!
Damit geht das:

richtig in die Hose....

  • Auch wenn das noch irgendwie einen Wert ausgeben würde: Wenn Du von einem kleinen Wert einen grossen abziehst wird das nicht das machen, was Du willst :wink: (ja, ich weiss, das das max sein soll)

Na dann.

ich habe dich am 15. Jänner schon mal gewarnt, und abgeraten das F-Makro mit dem server.print zu verwenden.

weiters löse deine ganzen Code-Duplikate auf! Leg dir Arrays für deine Pins und Restzeiten an, und schreib dir EINE Funktion die du für jeden Index aufrufst.

Du legst in deiner Funktion ein lokale Variable der Größe 1 and greifst dann in einer For Schleife einmal auf relaisPin[b] zu ... ich mein das kannst aber dann auch gleich ohne For Schleife machen.

warum auf einmal Floats?

float sperre1restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 1

War die Verwendung deiner Schalternummerierung wirklich eine gute Idee?

    if ((Feuchtigkeitssensor2pin >= Trocken) && (sperre2() == false) && (Schalter3Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre2 nicht aktiv ist und Schalter 3 nicht aktiv ist

wenn 2 und 2 und 3 dann mach was mit 2. Sicherheitshalber auch noch ein Kommentar dazu geschrieben. Spätestens beim Kommentar hätte dir ja auffallen können dass das besser gehen muss.

Generell rate ich dir, deinen Code mit Serial Print ausgaben auszustatten damit du feststellen kannst in welche Codeabschnitte zuschlagen. So kannst du wenigstens verfolgen was dein Programm macht.

Sehe ich das richtig du möchtest nur

eine Automatik die

  • abhängig vom Feuchtigkeitswert
  • abhängig vom "Schalter Pin"
  • abhängig davon wie lange der Ausgang schon inaktiv ist ("die Sperrzeit muss abgelaufen sein")
    einen Ausgang schalten?

Und parallel dazu, gibts halt auch eine Weboberfläche die den Ausgang hart schaltet.

Da hat noiasca völlig recht
Solcher Spaghetticode (lange wie mehrere Spaghettis) ist übel zu lesen und ich weigere mich dazu.

Funktionen sind dazu da funktionswerte zu bekommen und einen zurückzugeben. Da braucht es nicht 4 Funktionen sperre1() biis sperre4() die das gleiche an verschiedenen Pins machen. Deinen Sketch kann man auf 1/4 reduzieren ohne irgendwelche Kniffe anzuwenden die den Sketch noch weiter verkleinern.

Grüße Uwe

Wenn ich den Sensor in die Lufthalte hat er den höchsten Wert und die niedriste Feuchtigkeit.

Ich habe den Sketch mal angepasst:

// Gewaechshauswebif v3.2 vom 28.06.2022
const byte LDR = A0; // LDR am Pin A0

const byte Feuchtigkeitssensor1 = A1; // Anschluss des 1. Feuchtigkeitssensor am Pin A1
const byte Feuchtigkeitssensor2 = A2; // Anschluss des 2. Feuchtigkeitssensor am Pin A2
const byte Feuchtigkeitssensor3 = A3; // Anschluss des 3. Feuchtigkeitssensor am Pin A3
const byte Feuchtigkeitssensor4 = A4; // Anschluss des 4. Feuchtigkeitssensor am Pin A4

const int MaximaleFeuchte = 320; // Maximaler Feuchtigkeitswert (kleinster Wert)
const int MinimaleFeuchte = 1025; // Minimaler Feuchtigkeitswert (größter Wert)
float Spanne; // Zum Berechnen der Spanne
const int Trocken = 25; // Wert ab dem Bewässert werden soll

float Prozent1; // Zum Berechnen der Feuchtigkeit in Prozent vom 1. Feuchtigkeitssensor
float Prozent2; // Zum Berechnen der Feuchtigkeit in Prozent vom 2. Feuchtigkeitssensor
float Prozent3; // Zum Berechnen der Feuchtigkeit in Prozent vom 3. Feuchtigkeitssensor
float Prozent4; // Zum Berechnen der Feuchtigkeit in Prozent vom 4. Feuchtigkeitssensor

const byte Schalter5 = 2; // Schalter zum Öffnen aller Ventile , am Pin D2
const byte Schalter1 = 3; // Schalter zum Deaktivieren des Kreislauf 4, am Pin D6
const byte Schalter2 = 4; // Schalter zum Deaktivieren des Kreislauf 1, am Pin D3
const byte Schalter3 = 5; // Schalter zum Deaktivieren des Kreislauf 2, am Pin D4
const byte Schalter4 = 6; // Schalter zum Deaktivieren des Kreislauf 3, am Pin D5

const byte Relais1pin = 7; // Relais 1 für den Kreislauf 1, am Pin D6
const byte Relais2pin = 8; // Relais 2 für den Kreislauf 2, am Pin D7
const byte Relais3pin = 9; // Relais 3 für den Kreislauf 3, am Pin D8
const byte Relais4pin = 19; // Relais 4 für den Kreislauf 4, am Pin A5

const int TAG = 20; // Wert ab dem der Tag beginnt

float sperre1restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 1
float sperre2restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 2
float sperre3restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 3
float sperre4restzeit = 0; // Berechnung der Zeit bis zum Ende der Sperrzeit 4

//Ethernet Shield
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A }; // MAC Adresse des Arduino Boards

EthernetServer server(80); // Port Einstellung (Standard für HTML : 80)

void setup ()
{
  // Ethernet Shield
  Ethernet.begin(mac); // Ethernet initialisieren
  server.begin(); // Auf Clients warten
  Serial.print("Arduino's IP Address: ");
  Serial.println(Ethernet.localIP());

  Serial.print("DNS Server's IP Address: ");
  Serial.println(Ethernet.dnsServerIP());

  Serial.print("Gateway's IP Address: ");
  Serial.println(Ethernet.gatewayIP());

  Serial.print("Network's Subnet Mask: ");
  Serial.println(Ethernet.subnetMask());

  Serial.begin(57600); // Serielle Schnittstelle initialisieren

  pinMode(Relais1pin, OUTPUT); // Pin Modus definieren, Ausgang
  pinMode(Relais2pin, OUTPUT); // Pin Modus definieren, Ausgang
  pinMode(Relais3pin, OUTPUT); // Pin Modus definieren, Ausgang
  pinMode(Relais4pin, OUTPUT); // Pin Modus definieren, Ausgang

  pinMode(Schalter1, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 4
  pinMode(Schalter2, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 1
  pinMode(Schalter3, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 2
  pinMode(Schalter4, INPUT_PULLUP);  // Schalter zum Deaktivieren des Kreislauf 3
  pinMode(Schalter5, INPUT_PULLUP);  // Schalter zum Öffnen aller Ventile

  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
}

void loop ()
{
  int LDR1 = analogRead (0) * 1.0; // LDR1

  unsigned int Feuchtigkeitssensor1 = analogRead(1); // 1. Feuchtigkeitssensor
  unsigned int Feuchtigkeitssensor2 = analogRead(2); // 2. Feuchtigkeitssensor
  unsigned int Feuchtigkeitssensor3 = analogRead(3); // 3. Feuchtigkeitssensor
  unsigned int Feuchtigkeitssensor4 = analogRead(4); // 4. Feuchtigkeitssensor

  bool Schalter1Status = digitalRead(Schalter1); // Auslesen des Zustand vom Schalter1
  bool Schalter2Status = digitalRead(Schalter2); // Auslesen des Zustand vom Schalter2
  bool Schalter3Status = digitalRead(Schalter3); // Auslesen des Zustand vom Schalter3
  bool Schalter4Status = digitalRead(Schalter4); // Auslesen des Zustand vom Schalter4
  bool Schalter5Status = digitalRead(Schalter5); // Auslesen des Zustand vom Schalter5

  Prozent1 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor1); // Prozent berechnen für den 1. Feuchtigkeitssensor
  Prozent2 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor2); // Prozent berechnen für den 2. Feuchtigkeitssensor
  Prozent3 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor3); // Prozent berechnen für den 3. Feuchtigkeitssensor
  Prozent4 = (100 / Spanne) * (MinimaleFeuchte - Feuchtigkeitssensor4); // Prozent berechnen für den 4. Feuchtigkeitssensor

  if (Schalter5Status == LOW)
  {
    digitalWrite(Relais1pin, HIGH); // Relais 1 aktivieren
    digitalWrite(Relais2pin, HIGH); // Relais 2 aktivieren
    digitalWrite(Relais3pin, HIGH); // Relais 3 aktivieren
    digitalWrite(Relais4pin, HIGH); // Relais 4 aktivieren
  }


  if (LDR1 < TAG && Schalter5Status == HIGH)
  {
    digitalWrite(Relais1pin, LOW); // Relais 1 deaktivieren
    digitalWrite(Relais2pin, LOW); // Relais 2 deaktivieren
    digitalWrite(Relais3pin, LOW); // Relais 3 deaktivieren
    digitalWrite(Relais4pin, LOW); // Relais 4 deaktivieren
  }

  if (LDR1 >= TAG && Schalter5Status == HIGH) // Wenn es Tag ist
  {
    // 1. Kreislauf
    if ((Feuchtigkeitssensor1 >= Trocken) && (sperre1() == false) && (Schalter1Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre1 nicht aktiv ist und Schalter 2 nicht aktiv ist
    {
      digitalWrite(Relais1pin, HIGH); // Relais1 aktivieren
    }
    if (digitalRead(Relais1pin) == HIGH)
    {
      static unsigned long laststart1;
      if (millis() - laststart1 > 3800) // Bewässerungszeit 3,8s
      {
        laststart1 = millis();
        digitalWrite(Relais1pin, LOW); // Relais1 deaktivieren
      }
    }

    // 2. Kreislauf
    if ((Feuchtigkeitssensor2 >= Trocken) && (sperre2() == false) && (Schalter2Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre2 nicht aktiv ist und Schalter 3 nicht aktiv ist
    {
      digitalWrite(Relais2pin, HIGH); // Relais2 aktivieren
    }
    if (digitalRead(Relais2pin) == HIGH)
    {
      static unsigned long laststart2;
      if (millis() - laststart2 > 3800) // Bewässerungszeit 3,8s
      {
        laststart2 = millis();
        digitalWrite(Relais2pin, LOW); // Relais2 deaktivieren
      }
    }

    // 3. Kreislauf
    if ((Feuchtigkeitssensor3 >= Trocken) && (sperre3() == false) && (Schalter3Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre3 nicht aktiv ist und Schalter 4 nicht aktiv ist
    {
      digitalWrite(Relais3pin, HIGH); // Relais3 aktivieren
    }
    if (digitalRead(Relais3pin) == HIGH)
    {
      static unsigned long laststart3;
      if (millis() - laststart3 > 3800) // Bewässerungszeit 3,8s
      {
        laststart3 = millis();
        digitalWrite(Relais3pin, LOW); // Relais3 deaktivieren
      }
    }

    // 4. Kreislauf
    if ((Feuchtigkeitssensor4 >= Trocken) && (sperre4() == false) && (Schalter4Status == HIGH)) // Wenn die Bodenfeuchte kleiner oder gleich als Trocken ist, die Sperre4 nicht aktiv ist und Schalter 5 nicht aktiv ist
    {
      digitalWrite(Relais4pin, HIGH); // Relais4 aktivieren
    }
    if (digitalRead(Relais4pin) == HIGH)
    {
      static unsigned long laststart4;
      if (millis() - laststart4 > 3800) // Bewässerungszeit 3,8s
      {
        laststart4 = millis();
        digitalWrite(Relais4pin, LOW); // Relais4 deaktivieren
      }
    }
  }

  if (sperre1() == false)
  {
    sperre1restzeit = 0;
  }

  if (sperre2() == false)
  {
    sperre2restzeit = 0;
  }

  if (sperre3() == false)
  {
    sperre3restzeit = 0;
  }

  if (sperre4() == false)
  {
    sperre4restzeit = 0;
  }

  EthernetClient client = server.available(); //Prüfen, ob Client Seite aufruft
  if (client)
  { // Seitenaufruf durch User
    // Ausgabe in HTML
    server.print("HTTP/1.1 200 OK\r\nServer: Arduino UNO R3\r\nContent-Type: text/html\r\n\r\n"
                 "<!DOCTYPE html>"
                 "<html lang='de'>"
                 "<head>"
                 "<meta charset='utf-8'>"
                 "<link rel=icon type=image/vnd.microsoft.icon href=http://IP DES NAS/Gfp.png>"
                 "<title>Gewächshausbewässerung</title>"
                 "</head>"
                 "<style>"
                 "body { background-image: url('http://IP DES NAS/Gewächshaus.png');"
                 "background-repeat: no-repeat;"
                 "background-attachment: fixed;"
                 "background-position: center;"
                 "</style>"
                 "<center><img src=http://IP DES NAS/Gfp2.png width=390 height=54><BR/>"
                 "<font size=7><strong>Gewächshausbewässerung</strong></font size><BR/><BR/>"
                 "<font size=5>LDR: ");
    server.print(LDR1);
    server.print("<BR/>Bewässerung: ");
    server.print(LDR1 >= TAG ? "Ein" : "Aus");
    server.print("<BR/>Schalter1 (Alle Ventile geöffnet): ");
    server.print(Schalter5Status ? "Aus" : "Ein");

    server.print("<BR/><BR/>"
                 "<table>"
                 "<table border=1>"
                 "<tr>"
                 "<td>Kreislauf</td>"
                 "<td align=right>1</td>"
                 "<td align=right>2</td>"
                 "<td align=right>3</td>"
                 "<td align=right>4</td>"
                 "</tr>"
                 "<tr>"
                 "<td>Kreislauf aktiv</td>"
                 "<td align = right>");
    server.print(Schalter1Status ? "Ein" : "Aus");
    server.print("<td align = right>");
    server.print(Schalter2Status ? "Ein" : "Aus");
    server.print("<td align = right>");
    server.print(Schalter3Status ? "Ein" : "Aus");
    server.print("<td align = right>");
    server.print(Schalter4Status ? "Ein" : "Aus");
    server.print("</tr>"
                 "<tr>"
                 "<td>Feuchtigkeit in %</td>"
                 "<td align = right>");
    server.print(Prozent1);
    server.print("<td align = right>");
    server.print(Prozent2);
    server.print("<td align = right>");
    server.print(Prozent3);
    server.print("<td align = right>");
    server.print(Prozent4);
    server.print("</tr>"
                 "<tr>"
                 "<td>Relaisstatus</td>"
                 "<td align = right>");
    server.print(digitalRead(Relais1pin) ? "Ein" : "Aus");
    server.print("<td align = right>");
    server.print(digitalRead(Relais2pin) ? "Ein" : "Aus");
    server.print("<td align = right>");
    server.print(digitalRead(Relais3pin) ? "Ein" : "Aus");
    server.print("<td align = right>");
    server.print(digitalRead(Relais4pin) ? "Ein" : "Aus");
    server.print("</tr>"
                 "<tr>"
                 "<td>Relaissperre</td>"
                 "<td align = right>");
    server.print(sperre1() ? "Aktiv" : "Inaktiv");
    server.print("<td align = right>");
    server.print(sperre2() ? "Aktiv" : "Inaktiv");
    server.print("<td align = right>");
    server.print(sperre3() ? "Aktiv" : "Inaktiv");
    server.print("<td align = right>");
    server.print(sperre4() ? "Aktiv" : "Inaktiv");
    server.print("</tr>"
                 "<tr>"
                 "<td>Rest Sperrzeit in Min.</td>"
                 "<td align = right>");
    server.print(sperre1restzeit);
    server.print("<td align = right>");
    server.print(sperre2restzeit);
    server.print("<td align = right>");
    server.print(sperre3restzeit);
    server.print("<td align = right>");
    server.print(sperre4restzeit);
    server.print("</tr>"
                 "</table>"
                 "</font size>"
                 "</center>"
                 "</body>"
                 "</html>");
    delay(500); // Kurzer Delay, um Daten zu senden
    client.stop(); // Verbindung mit dem Client trennen
  }

  // Die Werte des Feuchtigkeitssensor Ausgeben
  static unsigned long lastprint;
  if (millis() - lastprint > 3000) // Testausgabe alle 3s
  {
    lastprint = millis();
    Serial.print(F("LDR: ")); Serial.println(LDR1); // LDR oben links
    Serial.print(F("Feuchtigkeitssensor1: ")); Serial.println(Feuchtigkeitssensor1); // 1. Feuchtigkeitssensor
    Serial.print(Prozent1); Serial.println(F(" % "));
    Serial.print(F("Feuchtigkeitssensor2: ")); Serial.println(Feuchtigkeitssensor2); // 2. Feuchtigkeitssensor
    Serial.print(Prozent2); Serial.println(F(" % "));
    Serial.print(F("Feuchtigkeitssensor3: ")); Serial.println(Feuchtigkeitssensor3); // 3. Feuchtigkeitssensor
    Serial.print(Prozent3); Serial.println(F(" % "));
    Serial.print(F("Feuchtigkeitssensor4: ")); Serial.println(Feuchtigkeitssensor4); // 4. Feuchtigkeitssensor
    Serial.print(Prozent4); Serial.println(F(" % "));
    Serial.print(F("Schalter 1: ")); Serial.println(Schalter5Status); // Zustand Schalter 1
    Serial.print(F("Schalter 2: ")); Serial.println(Schalter1Status); // Zustand Schalter 2
    Serial.print(F("Schalter 3: ")); Serial.println(Schalter2Status); // Zustand Schalter 3
    Serial.print(F("Schalter 4: ")); Serial.println(Schalter3Status); // Zustand Schalter 4
    Serial.print(F("Schalter 5: ")); Serial.println(Schalter4Status); // Zustand Schalter 5
    Serial.print(F("Relais 1: ")); Serial.println(digitalRead(Relais1pin)); // Zustand Relais 1
    Serial.print(F("Sperre Relais 1: ")); Serial.println(sperre1()); // Sperre Relais 1
    Serial.print(F("Relais 2: ")); Serial.println(digitalRead(Relais2pin)); // Zustand Relais 2
    Serial.print(F("Sperre Relais 2: ")); Serial.println(sperre2()); // Sperre Relais 2
    Serial.print(F("Relais 3: ")); Serial.println(digitalRead(Relais3pin)); // Zustand Relais 3
    Serial.print(F("Sperre Relais 3: ")); Serial.println(sperre3()); // Sperre Relais 3
    Serial.print(F("Relais 4: ")); Serial.println(digitalRead(Relais4pin)); // Zustand Relais 4
    Serial.print(F("Sperre Relais 4: ")); Serial.println(sperre4()); // Sperre Relais 4
    Serial.print(F("Sperre 1 Restzeit:")); Serial.println(sperre1restzeit); // Restzeit der Sperre 1
    Serial.print(F("Sperre 2 Restzeit:")); Serial.println(sperre2restzeit); // Restzeit der Sperre 2
    Serial.print(F("Sperre 3 Restzeit:")); Serial.println(sperre3restzeit); // Restzeit der Sperre 3
    Serial.print(F("Sperre 4 Restzeit:")); Serial.println(sperre4restzeit); // Restzeit der Sperre 4
  }
}

bool sperre1()
{
  static bool sperre1 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais1pin};

  if (!sperre1)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre1 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre1 = false;
  sperre1restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  return sperre1;
}

bool sperre2()
{
  static bool sperre2 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais2pin};

  if (!sperre2)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre2 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre2 = false;
  sperre2restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  return sperre2;
}

bool sperre3()
{
  static bool sperre3 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais2pin};

  if (!sperre3)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre3 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre3 = false;
  sperre3restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  return sperre3;
}

bool sperre4()
{
  static bool sperre4 = false;
  const unsigned long sperrzeit = 1000UL * 60 * 25; // Sperrzeit von 25min
  static unsigned long sperrStart = millis();
  static bool lastZustand[1] = {HIGH};
  uint8_t relaisPin[1] = {Relais4pin};

  if (!sperre4)                                           // Keine Sperre aktiv
    for (byte b = 0; b < 1; b++)
    {
      if (lastZustand[b] != !digitalRead(relaisPin[b]))  // Letzter Zustand weicht vom aktuellen ab
      {
        sperrStart = millis();
        lastZustand[b] = !lastZustand[b];
        sperre4 = true;
      }
    }
  if (millis() - sperrStart > sperrzeit) sperre4 = false;
  sperre4restzeit = (float) (sperrzeit - (millis() - sperrStart)) / 60000; // Ausgabe in Min
  return sperre4;
}

So habe ich auch die Nachkomma stellen.

Ist doch drin :wink:

Habe es jetzt angepasst, das alles zu 1 passt und zu 2, usw.!

Ja genau!

Wieso Schaltet die Weboberfläche den Ausgang :thinking:

Das ist ein Trugschluß. Oder Holzweg.
Ich hab Dir im Sonnentracker gestern was hinterlassen.

1 Like

ich hab mal den Sketch eingestampft auf ein struct array.
der Webserver IST IMMER NOCH SCHMUTZIG. Es geht mir nur darum das struct array zu zeigen.

Ob die Logik passt kann ich nicht beurteilen, aus meiner Sicht ist es jetzt aber besser lesbar.
dass man da einige Else setzen könnte ist mir klar, aber so sinds 3 separate Teile deren man leichter folgen kann.

Ausgangsbasis war #1 ... können also Fehler drinnen sein.
Webserver schau ich mir erst am Abend.

// Gewaechshauswebif v3.1 vom 28.06.2022
// https://forum.arduino.cc/t/gewachshausbewasserung-sperre-immer-wieder-aktiv/1007214/5

constexpr byte ldrPin = A0; // LDR am Pin
constexpr int MaximaleFeuchte = 320; // Maximaler Feuchtigkeitswert (kleinster Wert)
constexpr int MinimaleFeuchte = 1025; // Minimaler Feuchtigkeitswert (größter Wert)
constexpr int Trocken = 25; // Wert ab dem Bewässert werden soll

constexpr byte hauptschalterPin = 2;
float Spanne; // Zum Berechnen der Spanne

constexpr int TAG = 20; // Wert ab dem der Tag beginnt
constexpr uint32_t timeActive {3800UL}; // Bewässerungszeit 3,8s
constexpr uint32_t timeBlock {10000UL}; // "Sperrzeit" später anpassen auf

struct Circuit {
  const byte sensorPin;      // Anschluss des Feuchtigkeitssensor
  const byte schalterPin;    // Schalter zum Deaktivieren des Kreislauf
  const byte relaisPin;      // Relais für den Kreislauf
  uint32_t previousMillis = 0 - timeActive - timeBlock; // letzte Einschaltzeit // damit die Zeit bei Start "abgelaufen" ist
  float prozent = 0;            // Zum Berechnen der Feuchtigkeit in Prozent vom Feuchtigkeitssensor

  Circuit(const byte sensorPin, const byte schalterPin, const byte relaisPin) : sensorPin(sensorPin), schalterPin(schalterPin), relaisPin(relaisPin)
  {}
};

Circuit circuit[] {
  {A1, 3, 7},
  {A2, 4, 8},
  {A3, 5, 9},
  {A4, 6, 10}
};

//Ethernet Shield
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x00, 0x10, 0xFA, 0x6E, 0x28, 0x4A }; // MAC Adresse des Arduino Boards

EthernetServer server(80); // Port Einstellung (Standard für HTML : 80)

void setup ()
{
  Serial.begin(57600); // Serielle Schnittstelle initialisieren

  // Ethernet Shield
  Ethernet.begin(mac); // Ethernet initialisieren
  server.begin(); // Auf Clients warten
  Serial.print(F("Arduino's IP Address: "));
  Serial.println(Ethernet.localIP());

  Serial.print(F("DNS Server's IP Address: "));
  Serial.println(Ethernet.dnsServerIP());

  Serial.print(F("Gateway's IP Address: "));
  Serial.println(Ethernet.gatewayIP());

  Serial.print(F("Network's Subnet Mask: "));
  Serial.println(Ethernet.subnetMask());

  for (auto &i : circuit)
  {
    pinMode(i.sensorPin, INPUT);
    pinMode(i.schalterPin, INPUT_PULLUP);  // Schalter zum Öffnen aller Ventile
    pinMode(i.relaisPin, OUTPUT); // Pin Modus definieren, Ausgang
  }
  pinMode(hauptschalterPin, INPUT_PULLUP); // ex Schalter1

  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
}

void loop ()
{
  for (size_t i = 0; i < sizeof(circuit) / sizeof(circuit[0]); i++)
    circuitRun(i);
  clientRun();
  debugOutputRun();
}

bool isDay()
{
  int ldrRaw = analogRead (0) * 1.0; // LDR1
  if (ldrRaw >= TAG)
    return true;
  else
    return false;
}

// hier die ganze Logik für EINEN Kreis
void circuitRun(const byte i)
{
  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
  circuit[i].prozent = (100 / Spanne) * (MinimaleFeuchte - analogRead(circuit[i].sensorPin)); // Prozent berechnen für den Feuchtigkeitssensor

  // Hauptschalter behandeln
  if (digitalRead(hauptschalterPin) == LOW && digitalRead(circuit[i].relaisPin) == LOW)
  {
    Serial.print(F("Hauptschalter LOW  --> Kreis EIN für i=")); Serial.println(i);
    digitalWrite(circuit[i].relaisPin, HIGH); // Relais aktivieren
  }

  if (isDay() && digitalRead(hauptschalterPin) == HIGH && digitalRead(circuit[i].relaisPin) == HIGH)
  {
    Serial.print(F("Tag, Hauptschalter HIGH --> Kreis AUS für i=")); Serial.println(i);
    digitalWrite(circuit[i].relaisPin, LOW); // Relais deaktivieren
  }

  if (isDay() && digitalRead(hauptschalterPin) == LOW) // wenn Tag und nicht ohnehin über Hauptschalter eingeschaltet...
  {
    if ( digitalRead(circuit[i].relaisPin) == LOW)
    {
      // Feuchte testen und gegebenfalls einschalten vorrausgesetzt Sperrzeit ist abgelaufen
      if (circuit[i].prozent  <= Trocken && millis() - circuit[i].previousMillis > (timeActive + timeBlock))
      {
        Serial.print(F("Zu trocken --> Kreis EIN für i=")); Serial.println(i);
        digitalWrite(circuit[i].relaisPin, HIGH); // Relais aktivieren
      }
    }
    // auf Zeitablauf prüfen:
    if (digitalRead(circuit[i].relaisPin) == HIGH && millis() - circuit[i].previousMillis > 3800)
    {
      Serial.print(F("Zeitablauf --> Kreis AUS für i=")); Serial.println(i);
      digitalWrite(circuit[i].relaisPin, LOW); // Relais deaktivieren
    }
  }
}

void debugOutputRun()
{
  // Die Werte des Feuchtigkeitssensor Ausgeben
  static unsigned long lastprint;
  if (millis() - lastprint > 3000) // Testausgabe alle 3s
  {
    lastprint = millis();
    int j = 0;
    for (auto &i : circuit)
    {
      Serial.print(F("Kreis ")); Serial.println(j);
      Serial.print(F("Feuchtigkeitssensor: ")); Serial.println(analogRead(i.sensorPin));
      Serial.print(F("Prozent: ")); Serial.println(i.prozent);
      Serial.print(F("Schalter: ")); Serial.println(digitalRead(i.schalterPin));
      Serial.print(F("Relais: ")); Serial.println(digitalRead(i.relaisPin));
      Serial.print(F("letzte Schaltzeit: ")); Serial.println(i.previousMillis / 1000);
      j++;
    }
    Serial.println();
    Serial.print(F("aktuelle Zeit: ")); Serial.println(millis() / 1000);
    Serial.print(F("Hauptschalter: ")); Serial.println(digitalRead(hauptschalterPin));
    Serial.print(F("LDR: ")); Serial.println(analogRead(ldrPin)); // LDR oben links
  }
}

// schmutzig, schmutzig, schmutzig gehört komplett umgebaut
void clientRun()
{
  EthernetClient client = server.available(); //Prüfen, ob Client Seite aufruft
  if (client)
  { // Seitenaufruf durch User
    // Ausgabe in HTML
    server.print(F("HTTP/1.1 200 OK\r\nServer: Arduino UNO R3\r\nContent-Type: text/html\r\n\r\n"
                   "<!DOCTYPE html>"
                   "<html lang='de'>"
                   "<head>"
                   "<meta charset='utf-8'>"
                   "<link rel=icon type=image/vnd.microsoft.icon href=http://IP DES NAS/Gfp.png>"
                   "<title>Gewächshausbewässerung</title>"
                   "</head>"
                   "<style>"
                   "body { background-image: url('http://IP DES NAS/Gewächshaus.png');"
                   "background-repeat: no-repeat;"
                   "background-attachment: fixed;"
                   "background-position: center;"
                   "</style>"
                   "<center><img src=http://IP DES NAS/Gfp2.png width=390 height=54><BR/>"
                   "<font size=7><strong>Gewächshausbewässerung</strong></font size><BR/><BR/>"
                   "<font size=5>LDR: "));
    server.print(analogRead(ldrPin));
    server.print(F("<BR/>Tageszeit: "));
    server.print(isDay() ? "Tag" : "Nacht");
    server.print(F("<BR/>Schalter1 (Alle Ventile geöffnet): "));
    server.print(digitalRead(hauptschalterPin) ? "Aus" : "Ein");

    server.print(F("<BR/><BR/>\n"
                   "<table>"
                   "<table border=1>"
                   "<tr>"
                   "<td>Kreislauf</td>"
                   "<td align=right>0</td>"
                   "<td align=right>1</td>"
                   "<td align=right>2</td>"
                   "<td align=right>3</td>"
                   "</tr>"));

    // ein Code für alle Kreisläufe !


    server.print(F("<tr>\n"
                   "<td>Kreislauf aktiv</td>\n"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "<tr>"
                   "<td>Feuchtigkeit in %</td>"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(i.prozent);
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "<tr>"
                   "<td>Relaisstatus</td>"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "<tr>"
                   "<td>Relaissperre</td>"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(i.previousMillis / 1000); // das muss man noch umbauen
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "</table>\n"
                   "</font size>"
                   "</center>"
                   "</body>"
                   "</html>"));
    delay(500); // Kurzer Delay, um Daten zu senden
    client.stop(); // Verbindung mit dem Client trennen
  }
}

243 Zeilen statt 430.

edit: bild mit Netzwerkanalyse:

und bevor die Frage aufkommt, ja Array Felder fangen mit 0 zu zählen an ... daher Kreis 0, 1, 2, 3

Du bist mir zuvor gekommen...

Aber Feuchtigkeit 145% ? :slight_smile:

wenn es hilft, arbeite ruhig daran weiter. Ich kann erst am Abend wieder reinsehen,...

für die 145 kann ich nix. das ist eigentlich von ihm übernommen ...

(meine analogPins hängen in der Luft...)

edit:
der maintab.ino
(mit allen Fehlern aus #5)
die alte Client Funktion könnte man löschen, ist nur zum Vergleich

// Gewaechshauswebif v3.1 vom 28.06.2022
// https://forum.arduino.cc/t/gewachshausbewasserung-sperre-immer-wieder-aktiv/1007214/9

#include <StreamLib.h>           // download from library manager

constexpr byte ldrPin = A0; // LDR am Pin
constexpr int MaximaleFeuchte = 320; // Maximaler Feuchtigkeitswert (kleinster Wert)
constexpr int MinimaleFeuchte = 1025; // Minimaler Feuchtigkeitswert (größter Wert)
constexpr int Trocken = 25; // Wert ab dem Bewässert werden soll

constexpr byte hauptschalterPin = 2;
float Spanne; // Zum Berechnen der Spanne

constexpr int TAG = 20; // Wert ab dem der Tag beginnt
constexpr uint32_t timeActive {3800UL}; // Bewässerungszeit 3,8s
constexpr uint32_t timeBlock {10000UL}; // "Sperrzeit" später anpassen auf

struct Circuit {
  const byte sensorPin;      // Anschluss des Feuchtigkeitssensor
  const byte schalterPin;    // Schalter zum Deaktivieren des Kreislauf
  const byte relaisPin;      // Relais für den Kreislauf
  uint32_t previousMillis = 0 - timeActive - timeBlock; // letzte Einschaltzeit // damit die Zeit bei Start "abgelaufen" ist
  float prozent = 0;            // Zum Berechnen der Feuchtigkeit in Prozent vom Feuchtigkeitssensor

  Circuit(const byte sensorPin, const byte schalterPin, const byte relaisPin) : sensorPin(sensorPin), schalterPin(schalterPin), relaisPin(relaisPin)
  {}
};

Circuit circuit[] {
  {A1, 3, 7},
  {A2, 4, 8},
  {A3, 5, 9},
  {A4, 6, 10}
};

//Ethernet Shield
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x00, 0x10, 0xFA, 0x6E, 0x28, 0x4A }; // MAC Adresse des Arduino Boards

EthernetServer server(80); // Port Einstellung (Standard für HTML : 80)

void setup ()
{
  Serial.begin(57600); // Serielle Schnittstelle initialisieren

  // Ethernet Shield
  Ethernet.begin(mac); // Ethernet initialisieren
  server.begin(); // Auf Clients warten
  Serial.print(F("Arduino's IP Address: "));
  Serial.println(Ethernet.localIP());

  Serial.print(F("DNS Server's IP Address: "));
  Serial.println(Ethernet.dnsServerIP());

  Serial.print(F("Gateway's IP Address: "));
  Serial.println(Ethernet.gatewayIP());

  Serial.print(F("Network's Subnet Mask: "));
  Serial.println(Ethernet.subnetMask());

  for (auto &i : circuit)
  {
    pinMode(i.sensorPin, INPUT);
    pinMode(i.schalterPin, INPUT_PULLUP);  // Schalter zum Öffnen aller Ventile
    pinMode(i.relaisPin, OUTPUT); // Pin Modus definieren, Ausgang
  }
  pinMode(hauptschalterPin, INPUT_PULLUP); // ex Schalter1

  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
}

void loop ()
{
  for (size_t i = 0; i < sizeof(circuit) / sizeof(circuit[0]); i++)
    circuitRun(i);
  //clientRun();  // alter hässlicher Server
  checkForClient(); // auf Basis von https://werner.rothschopf.net/microcontroller/202011_arduino_webserver_optimieren.htm
  debugOutputRun();
}

bool isDay()
{
  int ldrRaw = analogRead (0) * 1.0; // LDR1
  if (ldrRaw >= TAG)
    return true;
  else
    return false;
}

// hier die ganze Logik für EINEN Kreis
void circuitRun(const byte i)
{
  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
  circuit[i].prozent = (100 / Spanne) * (MinimaleFeuchte - analogRead(circuit[i].sensorPin)); // Prozent berechnen für den Feuchtigkeitssensor

  // Hauptschalter behandeln
  if (digitalRead(hauptschalterPin) == LOW && digitalRead(circuit[i].relaisPin) == LOW)
  {
    Serial.print(F("Hauptschalter LOW  --> Kreis EIN für i=")); Serial.println(i);
    digitalWrite(circuit[i].relaisPin, HIGH); // Relais aktivieren
  }

  if (isDay() && digitalRead(hauptschalterPin) == HIGH && digitalRead(circuit[i].relaisPin) == HIGH)
  {
    Serial.print(F("Tag, Hauptschalter HIGH --> Kreis AUS für i=")); Serial.println(i);
    digitalWrite(circuit[i].relaisPin, LOW); // Relais deaktivieren
  }

  if (isDay() && digitalRead(hauptschalterPin) == LOW) // wenn Tag und nicht ohnehin über Hauptschalter eingeschaltet...
  {
    if ( digitalRead(circuit[i].relaisPin) == LOW)
    {
      // Feuchte testen und gegebenfalls einschalten vorrausgesetzt Sperrzeit ist abgelaufen
      if (circuit[i].prozent  <= Trocken && millis() - circuit[i].previousMillis > (timeActive + timeBlock))
      {
        Serial.print(F("Zu trocken --> Kreis EIN für i=")); Serial.println(i);
        digitalWrite(circuit[i].relaisPin, HIGH); // Relais aktivieren
      }
    }
    // auf Zeitablauf prüfen:
    if (digitalRead(circuit[i].relaisPin) == HIGH && millis() - circuit[i].previousMillis > 3800)
    {
      Serial.print(F("Zeitablauf --> Kreis AUS für i=")); Serial.println(i);
      digitalWrite(circuit[i].relaisPin, LOW); // Relais deaktivieren
    }
  }
}

void debugOutputRun()
{
  // Die Werte des Feuchtigkeitssensor Ausgeben
  static unsigned long lastprint;
  if (millis() - lastprint > 3000) // Testausgabe alle 3s
  {
    lastprint = millis();
    int j = 0;
    for (auto &i : circuit)
    {
      Serial.print(F("Kreis ")); Serial.println(j);
      Serial.print(F("Feuchtigkeitssensor: ")); Serial.println(analogRead(i.sensorPin));
      Serial.print(F("Prozent: ")); Serial.println(i.prozent);
      Serial.print(F("Schalter: ")); Serial.println(digitalRead(i.schalterPin));
      Serial.print(F("Relais: ")); Serial.println(digitalRead(i.relaisPin));
      Serial.print(F("letzte Schaltzeit: ")); Serial.println(i.previousMillis / 1000);
      j++;
    }
    Serial.println();
    Serial.print(F("aktuelle Zeit: ")); Serial.println(millis() / 1000);
    Serial.print(F("Hauptschalter: ")); Serial.println(digitalRead(hauptschalterPin));
    Serial.print(F("LDR: ")); Serial.println(analogRead(ldrPin)); // LDR oben links
  }
}

// schmutzig, schmutzig, schmutzig gehört komplett umgebaut
void clientRun()
{
  EthernetClient client = server.available(); //Prüfen, ob Client Seite aufruft
  if (client)
  { // Seitenaufruf durch User
    // Ausgabe in HTML
    server.print(F("HTTP/1.1 200 OK\r\nServer: Arduino UNO R3\r\nContent-Type: text/html\r\n\r\n"
                   "<!DOCTYPE html>"
                   "<html lang='de'>"
                   "<head>"
                   "<meta charset='utf-8'>"
                   "<link rel=icon type=image/vnd.microsoft.icon href=http://IP DES NAS/Gfp.png>"
                   "<title>Gewächshausbewässerung</title>"
                   "</head>"
                   "<style>"
                   "body { background-image: url('http://IP DES NAS/Gewächshaus.png');"
                   "background-repeat: no-repeat;"
                   "background-attachment: fixed;"
                   "background-position: center;"
                   "</style>"
                   "<center><img src=http://IP DES NAS/Gfp2.png width=390 height=54><BR/>"
                   "<font size=7><strong>Gewächshausbewässerung</strong></font size><BR/><BR/>"
                   "<font size=5>LDR: "));
    server.print(analogRead(ldrPin));
    server.print(F("<BR/>Tageszeit: "));
    server.print(isDay() ? "Tag" : "Nacht");
    server.print(F("<BR/>Schalter1 (Alle Ventile geöffnet): "));
    server.print(digitalRead(hauptschalterPin) ? "Aus" : "Ein");

    server.print(F("<BR/><BR/>\n"
                   "<table>"
                   "<table border=1>"
                   "<tr>"
                   "<td>Kreislauf</td>"
                   "<td align=right>0</td>"
                   "<td align=right>1</td>"
                   "<td align=right>2</td>"
                   "<td align=right>3</td>"
                   "</tr>"));

    // ein Code für alle Kreisläufe !


    server.print(F("<tr>\n"
                   "<td>Kreislauf aktiv</td>\n"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "<tr>"
                   "<td>Feuchtigkeit in %</td>"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(i.prozent);
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "<tr>"
                   "<td>Relaisstatus</td>"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "<tr>"
                   "<td>Relaissperre</td>"));
    for (auto &i : circuit)
    {
      server.print(F("<td align = right>"));
      server.print(i.previousMillis / 1000); // das muss man noch umbauen
      server.print(F("</td>"));
    }
    server.print(F("</tr>\n"
                   "</table>\n"
                   "</font size>"
                   "</center>"
                   "</body>"
                   "</html>"));
    delay(500); // Kurzer Delay, um Daten zu senden
    client.stop(); // Verbindung mit dem Client trennen
  }
}

ein tab server

/* *******************************************************************
   Webserver
   simple Arduino Webserver by Noiasca
   you only need to adopt this folder 
   - if you need more pages
   - if you want to read parameters
   ***************************************************************** */

void checkForClient()
{
  EthernetClient client = server.available();

  if (client) {
    Serial.println(F("\n[server] client connected"));
    uint8_t i = 0;                                // index / current read position
    const uint16_t buffersize = 100;              // size of read buffer (reads a complete line) (if larger than 255, modify i also!
    const uint16_t smallbuffersize = 30;          // a smaller buffer for results
    char lineBuffer[buffersize] {'\0'};           // buffer for incomming data
    char method[8];                               // largest one 7+1. HTTP request methods in RFC7231 + RFC5789: GET HEAD POST PUT DELETE CONNECT OPTONS TRACE PATCH
    char uri[smallbuffersize];                    // the requestet page, shorter than smallbuffersize - method
    char requestParameter[smallbuffersize];       // parameter appended to the URI after a ?
    char postParameter[smallbuffersize];          // parameter transmitted in the body / by POST
    enum class Status {REQUEST, CONTENT_LENGTH, EMPTY_LINE, BODY};
    Status status = Status::REQUEST;

    const size_t MESSAGE_BUFFER_SIZE = 64;                  // size of buffer
    char buffer[MESSAGE_BUFFER_SIZE];                       // a buffer for the StreamLib
    BufferedPrint message(client, buffer, sizeof(buffer));  // the StreamLib object to replace client print

    while (client.connected()) {
      while (client.available()) {
        char c = client.read();
        Serial.print(c);  // Debug print received characters to Serial monitor
        if ( c == '\n' )
        {
          if (status == Status::REQUEST)               // read the first line
          {
            //Serial.print(F("lineBuffer="));Serial.println(lineBuffer);
            // now split the input
            char *ptr;
            ptr = strtok(lineBuffer, " ");  // strtok willdestroy the newRequest
            strlcpy(method, ptr, smallbuffersize);
            Serial.print(F("method=")); Serial.println(method);
            ptr = strtok(NULL, " ");
            strlcpy(uri, ptr, smallbuffersize);   // enthält noch evtl. parameter
            if (strchr(uri, '?') != NULL)
            {
              ptr = strtok(uri, "?");  // split URI from parameters
              strcpy(uri, ptr);
              ptr = strtok(NULL, " ");
              strcpy(requestParameter, ptr);
              Serial.print(F("requestParameter=")); Serial.println(requestParameter);
            }
            Serial.print(F("uri=")); Serial.println(uri);
            status = Status::EMPTY_LINE;                          // jump to next status
          }
          else if (status == Status::CONTENT_LENGTH)              // MISSING check for Content-Length
          {
            status = Status::EMPTY_LINE;
          }
          else if (status > Status::REQUEST && i < 2)            // check if we have an empty line
          {
            status = Status::BODY;
          }
          else if (status == Status::BODY)
          {
            strlcpy(postParameter, lineBuffer, smallbuffersize);
            break; // we have received one line payload and break out
          }
          i = 0;
          strcpy(lineBuffer, "");
        }
        else
        {
          if (i < buffersize)
          {
            lineBuffer[i] = c;
            i++;
            lineBuffer[i] = '\0';
          }
          // MISSING wenn status 3 und content-length --> abbrechen.
        }
      }
      if (status == Status::BODY) // status 3 could end without linefeed, therefore we takeover here also
      {
        strlcpy(postParameter, lineBuffer, smallbuffersize);
      }
      Serial.print(F("postParameter=")); Serial.println(postParameter);
      // Simple evaluation of postParameter from body
      // Example Post data looks like pinD2=On
      //if ( strncmp( postParameter, "pinD", 4) == 0 ) {
      //  byte pin = postParameter[4] - 48; // Convert ascii to int
      // // Serial.println(pin);
      //  if ( strncmp( postParameter + 5, "=On", 3) == 0 ) {
      //    digitalWrite(pin, 1);
      //  }
      //  else if ( strncmp( postParameter + 5, "=Off", 4) == 0 ) {
      //    digitalWrite(pin, 0);
      //  }
      //}

      // send back a response
      if (!strcmp(uri, "/") || !strcmp(uri, "/index.htm")   ) // the homepage
        sendPage(message);
      else if (!strcmp(uri, "/favicon.ico"))                  // a favicon
        send204(message);                                     // if you don't have a favicon, send a defined empty response
      else                                                    // if the page is unknown, send HTTP response code 404
        send404(message);
      client.stop();
    }
  }
}

void send404(BufferedPrint &message)
{
  // Serial.println("[server] response 404 file not found");
  message.println(F("HTTP/1.0 404 Not Found\r\n"
                   "Content-Type: text/plain\r\n"              // we will send a simple plain text without html tags
                   "\r\n"
                   "File Fot Found"));
  message.flush();
}

void send204(BufferedPrint &message)
{
  // Serial.println("[server] response 204 no content");
  message.println(F("HTTP/1.0 204 no content\r\n"));  // no content
  message.flush();
}

und ein Tab server_user mit der neuen page:

/* *******************************************************************
   Webserver User functions
   here you define your "pages" 
   ***************************************************************** */
void sendPage(BufferedPrint &message)
{
  // Serial.println("[server] 200 response send");
  message.print  (F("HTTP/1.0 200 OK\r\n"                 // \r\n Header Fields are terminated by a carriage return (CR) and line feed (LF) character sequence.
                    "Content-Type: text/html\r\n"         // The Media type of the body of the request (used with POST and PUT requests).
                    "\r\n"                                // a blank line to split HTTP header and HTTP body
                    "<!doctype html>\n"                   // the HTTP Page itself
                    "<html lang='de'>\n"
                    "<head>\n"
                    "<meta charset='utf-8'>\n"
                    "<meta name='viewport' content='width=device-width'>\n"
                    "<link rel=icon type=image/vnd.microsoft.icon href=http://IP.DES.NAS/Gfp.png>"
                    "<title>Gewächshausbewässerung</title>\n"
                    "<style>\n"
                    "body {background-image: url('http://IP.DES.NAS/Gewächshaus.png');"
                    "background-repeat: no-repeat;"
                    "background-attachment: fixed;"
                    "background-position: center;}\n"
                    "body {font-family:Helvetica, sans-serif; font-size:1.3em}\n"
                    "main{text-align:center; margin-left:auto; margin-right:auto; display:table}\n"
                    "h1 {font-size:1.7em}\n"
                    "table, td {border-style:solid}\n"
                    "td {text-align:right}\n"
                    ".left {text-align:left}\n"
                    "</style>\n"
                    "</head>\n"
                    "<body>\n"));  // a minimum style to avoid serifs
  // Ausgabe Anfang
  message.print  (F("<main>\n"
                    "<h1>Gewächshausbewässerung</h1>\n"
                    "<p>LDR: "));
  message.print(analogRead(ldrPin));
  message.print  (F("</p>\n"
                    "<p>Tageszeit: "));
  message.print(isDay() ? "Tag" : "Nacht");
  message.print  (F("</p>\n"
                    "<p>Schalter1 (Alle Ventile geöffnet): "));
  message.print(digitalRead(hauptschalterPin) ? "Aus" : "Ein");
  message.print  (F("</p>\n"));

  message.print  (F("<p>\n"
                    "<table>\n"
                    "<tr>\n"
                    "<td class='left'>Kreislauf</td>\n"
                    "<td>0</td>\n"
                    "<td>1</td>\n"
                    "<td>2</td>\n"
                    "<td>3</td>\n"
                    "</tr>\n"));

  // ein Code für alle Kreisläufe !
  message.print  (F("<tr>\n"
                    "<td class='left'>Kreislauf aktiv</td>\n"));
  for (auto &i : circuit)
  {
    message.print(F("<td>"));
    message.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
    message.print(F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                    "<tr>\n"
                    "<td class='left'>Feuchtigkeit in %</td>\n"));
  for (auto &i : circuit)
  {
    message.print(F("<td>"));
    message.print(i.prozent);
    message.print(F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                   "<tr>\n"
                   "<td class='left'>Relaisstatus</td>\n"));
  for (auto &i : circuit)
  {
    message.print  (F("<td>"));
    message.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
    message.print  (F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                    "<tr>\n"
                    "<td class='left'>Relaissperre</td>"));
  for (auto &i : circuit)
  {
    message.print  (F("<td>"));
    message.print(i.previousMillis / 1000); // das muss man noch umbauen
    message.print  (F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                    "</table>\n"
                    //"</p>\n" //der Validator meint, das ist zu viel, ich bin der Meinung, das gehört hin.
                    ));

  // Ausgabe Ende
  message.print (F("</main>\n</body>\n</html>"));
  message.flush();
}

10 mal schneller als mit F-Makro

Ich habe es gerade mal getestet, wewnn der Schalter, der am Digital Pin 2 ist geschaltet wird flackern die Relais an und aus...
Hier mal die Ausgabe:

16:31:14.873 -> Kreis 0
16:31:14.873 -> Feuchtigkeitssensor: 562
16:31:14.873 -> Prozent: 91.37
16:31:14.873 -> Schalter: 1
16:31:14.873 -> Relais: 0
16:31:14.926 -> letzte Schaltzeit: 4293463
16:31:14.926 -> Kreis 1
16:31:14.926 -> Feuchtigkeitssensor: 635
16:31:14.926 -> Prozent: 75.69
16:31:14.926 -> Schalter: 1
16:31:14.926 -> Relais: 0
16:31:14.926 -> letzte Schaltzeit: 4293463
16:31:14.926 -> Kreis 2
16:31:14.926 -> Feuchtigkeitssensor: 899
16:31:14.926 -> Prozent: 24.31
16:31:14.926 -> Schalter: 1
16:31:14.926 -> Relais: 0
16:31:14.926 -> letzte Schaltzeit: 4293463
16:31:14.926 -> Kreis 3
16:31:14.926 -> Feuchtigkeitssensor: 657
16:31:14.926 -> Prozent: 71.57
16:31:14.926 -> Schalter: 1
16:31:14.926 -> Relais: 1
16:31:14.973 -> letzte Schaltzeit: 4293463
16:31:14.973 -> 
16:31:14.973 -> aktuelle Zeit: 18
16:31:14.973 -> Hauptschalter: 0
16:31:14.973 -> LDR: 963
16:31:14.973 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:14.973 -> Zeitablauf --> Kreis AUS für i=0
16:31:14.973 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:14.973 -> Zeitablauf --> Kreis AUS für i=1
16:31:14.973 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.026 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.026 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.026 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.026 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.026 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.026 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.026 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.026 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.073 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.073 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.073 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.073 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.073 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.073 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.073 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.073 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.127 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.127 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.127 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.127 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.127 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.127 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.127 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.127 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.174 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.174 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.174 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.174 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.174 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.174 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.174 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.227 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.227 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.227 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.227 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.227 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.227 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.227 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.227 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.274 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.274 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.274 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.274 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.274 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.274 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.274 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.274 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.328 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.328 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.328 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.328 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.328 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.328 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.328 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.328 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.374 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.374 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.374 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.374 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.374 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.374 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.374 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.428 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.428 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.428 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.428 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.428 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.428 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.428 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.428 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.475 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.475 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.475 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.475 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.475 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.475 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.475 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.528 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.528 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.528 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.528 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.528 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.528 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.528 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.528 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.575 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.575 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.575 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.575 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.575 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.575 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.575 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.629 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.629 -> Hauptschalter LOW  --> Kreis EIN für i=0
16:31:15.629 -> Zeitablauf --> Kreis AUS für i=0
16:31:15.629 -> Hauptschalter LOW  --> Kreis EIN für i=1
16:31:15.629 -> Zeitablauf --> Kreis AUS für i=1
16:31:15.629 -> Hauptschalter LOW  --> Kreis EIN für i=2
16:31:15.629 -> Zeitablauf --> Kreis AUS für i=2
16:31:15.629 -> Zeitablauf --> Kreis AUS für i=3
16:31:15.629 -> Hauptschalter LOW  --> Kreis 

Mal für die Übersicht:
Digital Pin 2 = Schalter zum Öffnen aller Ventile
Digital Pin 3 = Schalter zum Deaktivieren des Kreislauf 1
Digital Pin 4 = Schalter zum Deaktivieren des Kreislauf 2
Digital Pin 5 = Schalter zum Deaktivieren des Kreislauf 3
Digital Pin 6 = Schalter zum Deaktivieren des Kreislauf 4
Digital Pin 7 = Relais 1 für den Kreislauf 1
Digital Pin 8 = Relais 2 für den Kreislauf 2
Digital Pin 9 = Relais 3 für den Kreislauf 3
Digital Pin 10 - 13 = Ethernet Shield
Digital Pin 19 = Relais 4 für den Kreislauf 4 (Pin A5)

Analog Pin 0 = LDR
Analog Pin 1 = Feuchtigkeitssensor1
Analog Pin 2 = Feuchtigkeitssensor2
Analog Pin 3 = Feuchtigkeitssensor3
Analog Pin 4 = Feuchtigkeitssensor4

man sieht dass der Hauptschalter == LOW und daher einschaltet

Aber es kommt zum Zeitablauf.
Darf nicht sein.

vermutlicher Fehler dort wo auf Tag und Hauptschalter abgefragt wird

Ändern auf Nicht gedrückten Hauptschalter:

  if (isDay() && digitalRead(hauptschalterPin) == HIGH) // wenn Tag und nicht ohnehin über Hauptschalter eingeschaltet...

dann soll mal das geklappere aufhören.

Hat sich erledigt, habe jetzt alles hinzugefügt und die alte HTML Page gelöscht!

// Gewaechshauswebif v3.3 vom 28.06.2022
// https://forum.arduino.cc/t/gewachshausbewasserung-sperre-immer-wieder-aktiv/1007214/9

#include <StreamLib.h>           // download from library manager

constexpr byte ldrPin = A0; // LDR am Pin
constexpr int MaximaleFeuchte = 515; // Maximaler Feuchtigkeitswert (kleinster Wert)
constexpr int MinimaleFeuchte = 1025; // Minimaler Feuchtigkeitswert (größter Wert)
constexpr int Trocken = 25; // Wert ab dem Bewässert werden soll

constexpr byte hauptschalterPin = 2;
float Spanne; // Zum Berechnen der Spanne

constexpr int TAG = 20; // Wert ab dem der Tag beginnt
constexpr uint32_t timeActive {3800UL}; // Bewässerungszeit 3,8s
constexpr uint32_t timeBlock {10000UL}; // "Sperrzeit" später anpassen auf

struct Circuit {
  const byte sensorPin;      // Anschluss des Feuchtigkeitssensor
  const byte schalterPin;    // Schalter zum Deaktivieren des Kreislauf
  const byte relaisPin;      // Relais für den Kreislauf
  uint32_t previousMillis = 0 - timeActive - timeBlock; // letzte Einschaltzeit // damit die Zeit bei Start "abgelaufen" ist
  float prozent = 0;            // Zum Berechnen der Feuchtigkeit in Prozent vom Feuchtigkeitssensor

  Circuit(const byte sensorPin, const byte schalterPin, const byte relaisPin) : sensorPin(sensorPin), schalterPin(schalterPin), relaisPin(relaisPin)
  {}
};

Circuit circuit[] {
  {A1, 3, 7},
  {A2, 4, 8},
  {A3, 5, 9},
  {A4, 6, 10}
};

//Ethernet Shield
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x00, 0x10, 0xFA, 0x6E, 0x28, 0x4A }; // MAC Adresse des Arduino Boards

EthernetServer server(80); // Port Einstellung (Standard für HTML : 80)

void setup ()
{
  Serial.begin(57600); // Serielle Schnittstelle initialisieren

  // Ethernet Shield
  Ethernet.begin(mac); // Ethernet initialisieren
  server.begin(); // Auf Clients warten
  Serial.print(F("Arduino's IP Address: "));
  Serial.println(Ethernet.localIP());

  Serial.print(F("DNS Server's IP Address: "));
  Serial.println(Ethernet.dnsServerIP());

  Serial.print(F("Gateway's IP Address: "));
  Serial.println(Ethernet.gatewayIP());

  Serial.print(F("Network's Subnet Mask: "));
  Serial.println(Ethernet.subnetMask());

  for (auto &i : circuit)
  {
    pinMode(i.sensorPin, INPUT);
    pinMode(i.schalterPin, INPUT_PULLUP);  // Schalter zum Öffnen aller Ventile
    pinMode(i.relaisPin, OUTPUT); // Pin Modus definieren, Ausgang
  }
  pinMode(hauptschalterPin, INPUT_PULLUP); // ex Schalter1

  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
}

void loop ()
{
  for (size_t i = 0; i < sizeof(circuit) / sizeof(circuit[0]); i++)
    circuitRun(i);
  //clientRun();  // alter hässlicher Server
  checkForClient(); // auf Basis von https://werner.rothschopf.net/microcontroller/202011_arduino_webserver_optimieren.htm
  debugOutputRun();
}

bool isDay()
{
  int ldrRaw = analogRead (0) * 1.0; // LDR1
  if (ldrRaw >= TAG)
    return true;
  else
    return false;
}

// hier die ganze Logik für EINEN Kreis
void circuitRun(const byte i)
{
  // Feuchtigkeit in Prozent umrechnen
  Spanne = MinimaleFeuchte - MaximaleFeuchte; // Zum Berechnen der Spanne
  circuit[i].prozent = (100 / Spanne) * (MinimaleFeuchte - analogRead(circuit[i].sensorPin)); // Prozent berechnen für den Feuchtigkeitssensor

  // Hauptschalter behandeln
  if (digitalRead(hauptschalterPin) == LOW && digitalRead(circuit[i].relaisPin) == LOW)
  {
    Serial.print(F("Hauptschalter LOW  --> Kreis EIN für i=")); Serial.println(i);
    digitalWrite(circuit[i].relaisPin, HIGH); // Relais aktivieren
  }

  if (isDay() && digitalRead(hauptschalterPin) == HIGH && digitalRead(circuit[i].relaisPin) == HIGH)
  {
    Serial.print(F("Tag, Hauptschalter HIGH --> Kreis AUS für i=")); Serial.println(i);
    digitalWrite(circuit[i].relaisPin, LOW); // Relais deaktivieren
  }

    if (isDay() && digitalRead(hauptschalterPin) == HIGH) // wenn Tag und nicht ohnehin über Hauptschalter eingeschaltet...
  {
    if ( digitalRead(circuit[i].relaisPin) == LOW)
    {
      // Feuchte testen und gegebenfalls einschalten vorrausgesetzt Sperrzeit ist abgelaufen
      if (circuit[i].prozent  <= Trocken && millis() - circuit[i].previousMillis > (timeActive + timeBlock))
      {
        Serial.print(F("Zu trocken --> Kreis EIN für i=")); Serial.println(i);
        digitalWrite(circuit[i].relaisPin, HIGH); // Relais aktivieren
      }
    }
    // auf Zeitablauf prüfen:
    if (digitalRead(circuit[i].relaisPin) == HIGH && millis() - circuit[i].previousMillis > 3800)
    {
      Serial.print(F("Zeitablauf --> Kreis AUS für i=")); Serial.println(i);
      digitalWrite(circuit[i].relaisPin, LOW); // Relais deaktivieren
    }
  }
}

void debugOutputRun()
{
  // Die Werte des Feuchtigkeitssensor Ausgeben
  static unsigned long lastprint;
  if (millis() - lastprint > 3000) // Testausgabe alle 3s
  {
    lastprint = millis();
    int j = 0;
    for (auto &i : circuit)
    {
      Serial.print(F("Kreis ")); Serial.println(j);
      Serial.print(F("Feuchtigkeitssensor: ")); Serial.println(analogRead(i.sensorPin));
      Serial.print(F("Prozent: ")); Serial.println(i.prozent);
      Serial.print(F("Schalter: ")); Serial.println(digitalRead(i.schalterPin));
      Serial.print(F("Relais: ")); Serial.println(digitalRead(i.relaisPin));
      Serial.print(F("letzte Schaltzeit: ")); Serial.println(i.previousMillis / 1000);
      j++;
    }
    Serial.println();
    Serial.print(F("aktuelle Zeit: ")); Serial.println(millis() / 1000);
    Serial.print(F("Hauptschalter: ")); Serial.println(digitalRead(hauptschalterPin));
    Serial.print(F("LDR: ")); Serial.println(analogRead(ldrPin)); // LDR oben links
  }
}

void checkForClient()
{
  EthernetClient client = server.available();

  if (client) {
    Serial.println(F("\n[server] client connected"));
    uint8_t i = 0;                                // index / current read position
    const uint16_t buffersize = 100;              // size of read buffer (reads a complete line) (if larger than 255, modify i also!
    const uint16_t smallbuffersize = 30;          // a smaller buffer for results
    char lineBuffer[buffersize] {'\0'};           // buffer for incomming data
    char method[8];                               // largest one 7+1. HTTP request methods in RFC7231 + RFC5789: GET HEAD POST PUT DELETE CONNECT OPTONS TRACE PATCH
    char uri[smallbuffersize];                    // the requestet page, shorter than smallbuffersize - method
    char requestParameter[smallbuffersize];       // parameter appended to the URI after a ?
    char postParameter[smallbuffersize];          // parameter transmitted in the body / by POST
    enum class Status {REQUEST, CONTENT_LENGTH, EMPTY_LINE, BODY};
    Status status = Status::REQUEST;

    const size_t MESSAGE_BUFFER_SIZE = 64;                  // size of buffer
    char buffer[MESSAGE_BUFFER_SIZE];                       // a buffer for the StreamLib
    BufferedPrint message(client, buffer, sizeof(buffer));  // the StreamLib object to replace client print

    while (client.connected()) {
      while (client.available()) {
        char c = client.read();
        Serial.print(c);  // Debug print received characters to Serial monitor
        if ( c == '\n' )
        {
          if (status == Status::REQUEST)               // read the first line
          {
            //Serial.print(F("lineBuffer="));Serial.println(lineBuffer);
            // now split the input
            char *ptr;
            ptr = strtok(lineBuffer, " ");  // strtok willdestroy the newRequest
            strlcpy(method, ptr, smallbuffersize);
            Serial.print(F("method=")); Serial.println(method);
            ptr = strtok(NULL, " ");
            strlcpy(uri, ptr, smallbuffersize);   // enthält noch evtl. parameter
            if (strchr(uri, '?') != NULL)
            {
              ptr = strtok(uri, "?");  // split URI from parameters
              strcpy(uri, ptr);
              ptr = strtok(NULL, " ");
              strcpy(requestParameter, ptr);
              Serial.print(F("requestParameter=")); Serial.println(requestParameter);
            }
            Serial.print(F("uri=")); Serial.println(uri);
            status = Status::EMPTY_LINE;                          // jump to next status
          }
          else if (status == Status::CONTENT_LENGTH)              // MISSING check for Content-Length
          {
            status = Status::EMPTY_LINE;
          }
          else if (status > Status::REQUEST && i < 2)            // check if we have an empty line
          {
            status = Status::BODY;
          }
          else if (status == Status::BODY)
          {
            strlcpy(postParameter, lineBuffer, smallbuffersize);
            break; // we have received one line payload and break out
          }
          i = 0;
          strcpy(lineBuffer, "");
        }
        else
        {
          if (i < buffersize)
          {
            lineBuffer[i] = c;
            i++;
            lineBuffer[i] = '\0';
          }
          // MISSING wenn status 3 und content-length --> abbrechen.
        }
      }
      if (status == Status::BODY) // status 3 could end without linefeed, therefore we takeover here also
      {
        strlcpy(postParameter, lineBuffer, smallbuffersize);
      }
      Serial.print(F("postParameter=")); Serial.println(postParameter);
      // Simple evaluation of postParameter from body
      // Example Post data looks like pinD2=On
      //if ( strncmp( postParameter, "pinD", 4) == 0 ) {
      //  byte pin = postParameter[4] - 48; // Convert ascii to int
      // // Serial.println(pin);
      //  if ( strncmp( postParameter + 5, "=On", 3) == 0 ) {
      //    digitalWrite(pin, 1);
      //  }
      //  else if ( strncmp( postParameter + 5, "=Off", 4) == 0 ) {
      //    digitalWrite(pin, 0);
      //  }
      //}

      // send back a response
      if (!strcmp(uri, "/") || !strcmp(uri, "/index.htm")   ) // the homepage
        sendPage(message);
      else if (!strcmp(uri, "/favicon.ico"))                  // a favicon
        send204(message);                                     // if you don't have a favicon, send a defined empty response
      else                                                    // if the page is unknown, send HTTP response code 404
        send404(message);
      client.stop();
    }
  }
}

void send404(BufferedPrint &message)
{
  // Serial.println("[server] response 404 file not found");
  message.println(F("HTTP/1.0 404 Not Found\r\n"
                   "Content-Type: text/plain\r\n"              // we will send a simple plain text without html tags
                   "\r\n"
                   "File Fot Found"));
  message.flush();
}

void send204(BufferedPrint &message)
{
  // Serial.println("[server] response 204 no content");
  message.println(F("HTTP/1.0 204 no content\r\n"));  // no content
  message.flush();
}

void sendPage(BufferedPrint &message)
{
  // Serial.println("[server] 200 response send");
  message.print  (F("HTTP/1.0 200 OK\r\n"                 // \r\n Header Fields are terminated by a carriage return (CR) and line feed (LF) character sequence.
                    "Content-Type: text/html\r\n"         // The Media type of the body of the request (used with POST and PUT requests).
                    "\r\n"                                // a blank line to split HTTP header and HTTP body
                    "<!doctype html>\n"                   // the HTTP Page itself
                    "<html lang='de'>\n"
                    "<head>\n"
                    "<meta charset='utf-8'>\n"
                    "<meta name='viewport' content='width=device-width'>\n"
                    "<link rel=icon type=image/vnd.microsoft.icon href=http://IP.DES.NAS/Gfp.png>"
                    "<title>Gewächshausbewässerung</title>\n"
                    "<style>\n"
                    "body {background-image: url('http://IP.DES.NAS/Gewächshaus.png');"
                    "background-repeat: no-repeat;"
                    "background-attachment: fixed;"
                    "background-position: center;}\n"
                    "body {font-family:Helvetica, sans-serif; font-size:1.3em}\n"
                    "main{text-align:center; margin-left:auto; margin-right:auto; display:table}\n"
                    "h1 {font-size:1.7em}\n"
                    "table, td {border-style:solid}\n"
                    "td {text-align:right}\n"
                    ".left {text-align:left}\n"
                    "</style>\n"
                    "</head>\n"
                    "<body>\n"));  // a minimum style to avoid serifs
  // Ausgabe Anfang
  message.print  (F("<main>\n"
                    "<h1>Gewächshausbewässerung</h1>\n"
                    "<p>LDR: "));
  message.print(analogRead(ldrPin));
  message.print  (F("</p>\n"
                    "<p>Tageszeit: "));
  message.print(isDay() ? "Tag" : "Nacht");
  message.print  (F("</p>\n"
                    "<p>Schalter1 (Alle Ventile geöffnet): "));
  message.print(digitalRead(hauptschalterPin) ? "Aus" : "Ein");
  message.print  (F("</p>\n"));

  message.print  (F("<p>\n"
                    "<table>\n"
                    "<tr>\n"
                    "<td class='left'>Kreislauf</td>\n"
                    "<td>0</td>\n"
                    "<td>1</td>\n"
                    "<td>2</td>\n"
                    "<td>3</td>\n"
                    "</tr>\n"));

  // ein Code für alle Kreisläufe !
  message.print  (F("<tr>\n"
                    "<td class='left'>Kreislauf aktiv</td>\n"));
  for (auto &i : circuit)
  {
    message.print(F("<td>"));
    message.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
    message.print(F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                    "<tr>\n"
                    "<td class='left'>Feuchtigkeit in %</td>\n"));
  for (auto &i : circuit)
  {
    message.print(F("<td>"));
    message.print(i.prozent);
    message.print(F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                   "<tr>\n"
                   "<td class='left'>Relaisstatus</td>\n"));
  for (auto &i : circuit)
  {
    message.print  (F("<td>"));
    message.print(digitalRead(i.relaisPin) ? "Ein" : "Aus");
    message.print  (F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                    "<tr>\n"
                    "<td class='left'>Relaissperre</td>"));
  for (auto &i : circuit)
  {
    message.print  (F("<td>"));
    message.print(i.previousMillis / 1000); // das muss man noch umbauen
    message.print  (F("</td>\n"));
  }
  message.print  (F("</tr>\n"
                    "</table>\n"
                    //"</p>\n" //der Validator meint, das ist zu viel, ich bin der Meinung, das gehört hin.
                    ));

  // Ausgabe Ende
  message.print (F("</main>\n</body>\n</html>"));
  message.flush();
}

und die zwei zusätzlichen Tabs hast auch ergänzt?
Wenn ja und es kompiliert trotzdem nicht , möchte ich bitte einen Screenshot von deiner IDE sehen.

1 Like

Das Klackern ist noch da, dieses mal am Relais 3, das Aufrufen der HTML Seite geht nicht und es kommt auch keine Serielle Ausgabe

Serial musst selber suchen. Verstehe ich nicht.

was ein Problem ist, der Pin 10.
10, 11,12,13 gehören dem Ethernet

im Struct gibts in der letzten Zeile eine 10
--> Anderen Pin für 10 suchen.
--> lt. deinem ersten Post A5 ... ändere auf A5

aufpassen, hat dein Modul eh keine SD-Karte?

bitte sprich ab jetzt bei Kreisen so wie auch der Microcontroller in Kreis 0, 1,2,3

Nein, es hat kein SD Anschluss. Ist ein W5500 Mini:

PIN 10 habe ich doch garnicht in Benutzung nur für das Ethernet Modul
Habe extra alles Aufgelistet:

tja, auch ich darf falsch abtippen.
Korrigiere das struct und berichte

Circuit circuit[] {
  {A1, 3, 7},
  {A2, 4, 8},
  {A3, 5, 9},
  {A4, 6, A5}
};
1 Like

Alles gut, kann immer passieren!

Leider bleibt das Problem mit dem Flackern vom Microcontroller Kreis 2. Sprich Relais 3
Hier der Log:

17:26:03.378 -> Kreis 0
17:26:03.378 -> Feuchtigkeitssensor: 561
17:26:03.378 -> Prozent: 90.39
17:26:03.378 -> Schalter: 1
17:26:03.378 -> Relais: 0
17:26:03.378 -> letzte Schaltzeit: 4294953
17:26:03.378 -> Kreis 1
17:26:03.378 -> Feuchtigkeitssensor: 635
17:26:03.378 -> Prozent: 76.27
17:26:03.378 -> Schalter: 1
17:26:03.378 -> Relais: 0
17:26:03.425 -> letzte Schaltzeit: 4294953
17:26:03.425 -> Kreis 2
17:26:03.425 -> Feuchtigkeitssensor: 899
17:26:03.425 -> Prozent: 25.29
17:26:03.425 -> Schalter: 1
17:26:03.425 -> Relais: 0
17:26:03.425 -> letzte Schaltzeit: 4294953
17:26:03.425 -> Kreis 3
17:26:03.425 -> Feuchtigkeitssensor: 645
17:26:03.425 -> Prozent: 74.12
17:26:03.425 -> Schalter: 1
17:26:03.425 -> Relais: 0
17:26:03.425 -> letzte Schaltzeit: 4294953
17:26:03.425 -> 
17:26:03.425 -> aktuelle Zeit: 9
17:26:03.425 -> Hauptschalter: 1
17:26:03.478 -> LDR: 959
17:26:03.478 -> Zu trocken --> Kreis EIN für i=2
17:26:03.478 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.478 -> Zu trocken --> Kreis EIN für i=2
17:26:03.478 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.478 -> Zu trocken --> Kreis EIN für i=2
17:26:03.478 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.478 -> Zu trocken --> Kreis EIN für i=2
17:26:03.478 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.478 -> Zu trocken --> Kreis EIN für i=2
17:26:03.525 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.525 -> Zu trocken --> Kreis EIN für i=2
17:26:03.525 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.525 -> Zu trocken --> Kreis EIN für i=2
17:26:03.525 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.525 -> Zu trocken --> Kreis EIN für i=2
17:26:03.525 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.525 -> Zu trocken --> Kreis EIN für i=2
17:26:03.579 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.579 -> Zu trocken --> Kreis EIN für i=2
17:26:03.579 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.579 -> Zu trocken --> Kreis EIN für i=2
17:26:03.579 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.579 -> Zu trocken --> Kreis EIN für i=2
17:26:03.579 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.579 -> Zu trocken --> Kreis EIN für i=2
17:26:03.579 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.626 -> Zu trocken --> Kreis EIN für i=2
17:26:03.626 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.626 -> Zu trocken --> Kreis EIN für i=2
17:26:03.626 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.626 -> Zu trocken --> Kreis EIN für i=2
17:26:03.679 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.679 -> Zu trocken --> Kreis EIN für i=2
17:26:03.679 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.679 -> Zu trocken --> Kreis EIN für i=2
17:26:03.679 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.679 -> Zu trocken --> Kreis EIN für i=2
17:26:03.679 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.679 -> Zu trocken --> Kreis EIN für i=2
17:26:03.679 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.726 -> Zu trocken --> Kreis EIN für i=2
17:26:03.726 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.726 -> Zu trocken --> Kreis EIN für i=2
17:26:03.726 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.726 -> Zu trocken --> Kreis EIN für i=2
17:26:03.726 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.726 -> Zu trocken --> Kreis EIN für i=2
17:26:03.726 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.779 -> Zu trocken --> Kreis EIN für i=2
17:26:03.779 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.779 -> Zu trocken --> Kreis EIN für i=2
17:26:03.779 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.779 -> Zu trocken --> Kreis EIN für i=2
17:26:03.779 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.779 -> Zu trocken --> Kreis EIN für i=2
17:26:03.779 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.779 -> Zu trocken --> Kreis EIN für i=2
17:26:03.826 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.826 -> Zu trocken --> Kreis EIN für i=2
17:26:03.826 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.826 -> Zu trocken --> Kreis EIN für i=2
17:26:03.826 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.826 -> Zu trocken --> Kreis EIN für i=2
17:26:03.826 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.826 -> Zu trocken --> Kreis EIN für i=2
17:26:03.880 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.880 -> Zu trocken --> Kreis EIN für i=2
17:26:03.880 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.880 -> Zu trocken --> Kreis EIN für i=2
17:26:03.880 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.880 -> Zu trocken --> Kreis EIN für i=2
17:26:03.880 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.880 -> Zu trocken --> Kreis EIN für i=2
17:26:03.880 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.927 -> Zu trocken --> Kreis EIN für i=2
17:26:03.927 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.927 -> Zu trocken --> Kreis EIN für i=2
17:26:03.927 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.927 -> Zu trocken --> Kreis EIN für i=2
17:26:03.927 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.927 -> Zu trocken --> Kreis EIN für i=2
17:26:03.927 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.980 -> Zu trocken --> Kreis EIN für i=2
17:26:03.980 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.980 -> Zu trocken --> Kreis EIN für i=2
17:26:03.980 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.980 -> Zu trocken --> Kreis EIN für i=2
17:26:03.980 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.980 -> Zu trocken --> Kreis EIN für i=2
17:26:03.980 -> Zeitablauf --> Kreis AUS für i=2
17:26:03.980 -> Zu trocken --> Kreis EIN für i=2
17:26:04.027 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.027 -> Zu trocken --> Kreis EIN für i=2
17:26:04.027 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.027 -> Zu trocken --> Kreis EIN für i=2
17:26:04.027 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.027 -> Zu trocken --> Kreis EIN für i=2
17:26:04.027 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.081 -> Zu trocken --> Kreis EIN für i=2
17:26:04.081 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.081 -> Zu trocken --> Kreis EIN für i=2
17:26:04.081 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.081 -> Zu trocken --> Kreis EIN für i=2
17:26:04.081 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.081 -> Zu trocken --> Kreis EIN für i=2
17:26:04.081 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.081 -> Zu trocken --> Kreis EIN für i=2
17:26:04.127 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.127 -> Zu trocken --> Kreis EIN für i=2
17:26:04.127 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.127 -> Zu trocken --> Kreis EIN für i=2
17:26:04.127 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.127 -> Zu trocken --> Kreis EIN für i=2
17:26:04.127 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.127 -> Zu trocken --> Kreis EIN für i=2
17:26:04.127 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.181 -> Zu trocken --> Kreis EIN für i=2
17:26:04.181 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.181 -> Zu trocken --> Kreis EIN für i=2
17:26:04.181 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.181 -> Zu trocken --> Kreis EIN für i=2
17:26:04.181 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.181 -> Zu trocken --> Kreis EIN für i=2
17:26:04.181 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.181 -> Zu trocken --> Kreis EIN für i=2
17:26:04.228 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.228 -> Zu trocken --> Kreis EIN für i=2
17:26:04.228 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.228 -> Zu trocken --> Kreis EIN für i=2
17:26:04.228 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.228 -> Zu trocken --> Kreis EIN für i=2
17:26:04.228 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.281 -> Zu trocken --> Kreis EIN für i=2
17:26:04.281 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.281 -> Zu trocken --> Kreis EIN für i=2
17:26:04.281 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.281 -> Zu trocken --> Kreis EIN für i=2
17:26:04.281 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.281 -> Zu trocken --> Kreis EIN für i=2
17:26:04.281 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.281 -> Zu trocken --> Kreis EIN für i=2
17:26:04.328 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.328 -> Zu trocken --> Kreis EIN für i=2
17:26:04.328 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.328 -> Zu trocken --> Kreis EIN für i=2
17:26:04.328 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.328 -> Zu trocken --> Kreis EIN für i=2
17:26:04.328 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.328 -> Zu trocken --> Kreis EIN für i=2
17:26:04.382 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.382 -> Zu trocken --> Kreis EIN für i=2
17:26:04.382 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.382 -> Zu trocken --> Kreis EIN für i=2
17:26:04.382 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.382 -> Zu trocken --> Kreis EIN für i=2
17:26:04.382 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.382 -> Zu trocken --> Kreis EIN für i=2
17:26:04.382 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.429 -> Zu trocken --> Kreis EIN für i=2
17:26:04.429 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.429 -> Zu trocken --> Kreis EIN für i=2
17:26:04.429 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.429 -> Zu trocken --> Kreis EIN für i=2
17:26:04.429 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.429 -> Zu trocken --> Kreis EIN für i=2
17:26:04.429 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.482 -> Zu trocken --> Kreis EIN für i=2
17:26:04.482 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.482 -> Zu trocken --> Kreis EIN für i=2
17:26:04.482 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.482 -> Zu trocken --> Kreis EIN für i=2
17:26:04.482 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.482 -> Zu trocken --> Kreis EIN für i=2
17:26:04.482 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.482 -> Zu trocken --> Kreis EIN für i=2
17:26:04.529 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.529 -> Zu trocken --> Kreis EIN für i=2
17:26:04.529 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.529 -> Zu trocken --> Kreis EIN für i=2
17:26:04.529 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.529 -> Zu trocken --> Kreis EIN für i=2
17:26:04.529 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.529 -> Zu trocken --> Kreis EIN für i=2
17:26:04.583 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.583 -> Zu trocken --> Kreis EIN für i=2
17:26:04.583 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.583 -> Zu trocken --> Kreis EIN für i=2
17:26:04.583 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.583 -> Zu trocken --> Kreis EIN für i=2
17:26:04.583 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.583 -> Zu trocken --> Kreis EIN für i=2
17:26:04.583 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.629 -> Zu trocken --> Kreis EIN für i=2
17:26:04.629 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.629 -> Zu trocken --> Kreis EIN für i=2
17:26:04.629 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.629 -> Zu trocken --> Kreis EIN für i=2
17:26:04.629 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.629 -> Zu trocken --> Kreis EIN für i=2
17:26:04.629 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.683 -> Zu trocken --> Kreis EIN für i=2
17:26:04.683 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.683 -> Zu trocken --> Kreis EIN für i=2
17:26:04.683 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.683 -> Zu trocken --> Kreis EIN für i=2
17:26:04.683 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.683 -> Zu trocken --> Kreis EIN für i=2
17:26:04.683 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.683 -> Zu trocken --> Kreis EIN für i=2
17:26:04.730 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.730 -> Zu trocken --> Kreis EIN für i=2
17:26:04.730 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.730 -> Zu trocken --> Kreis EIN für i=2
17:26:04.730 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.730 -> Zu trocken --> Kreis EIN für i=2
17:26:04.730 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.730 -> Zu trocken --> Kreis EIN für i=2
17:26:04.783 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.783 -> Zu trocken --> Kreis EIN für i=2
17:26:04.783 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.783 -> Zu trocken --> Kreis EIN für i=2
17:26:04.783 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.783 -> Zu trocken --> Kreis EIN für i=2
17:26:04.783 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.783 -> Zu trocken --> Kreis EIN für i=2
17:26:04.783 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.830 -> Zu trocken --> Kreis EIN für i=2
17:26:04.830 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.830 -> Zu trocken --> Kreis EIN für i=2
17:26:04.830 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.830 -> Zu trocken --> Kreis EIN für i=2
17:26:04.830 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.830 -> Zu trocken --> Kreis EIN für i=2
17:26:04.884 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.884 -> Zu trocken --> Kreis EIN für i=2
17:26:04.884 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.884 -> Zu trocken --> Kreis EIN für i=2
17:26:04.884 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.884 -> Zu trocken --> Kreis EIN für i=2
17:26:04.884 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.884 -> Zu trocken --> Kreis EIN für i=2
17:26:04.884 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.930 -> Zu trocken --> Kreis EIN für i=2
17:26:04.930 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.930 -> Zu trocken --> Kreis EIN für i=2
17:26:04.930 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.930 -> Zu trocken --> Kreis EIN für i=2
17:26:04.930 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.930 -> Zu trocken --> Kreis EIN für i=2
17:26:04.930 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.984 -> Zu trocken --> Kreis EIN für i=2
17:26:04.984 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.984 -> Zu trocken --> Kreis EIN für i=2
17:26:04.984 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.984 -> Zu trocken --> Kreis EIN für i=2
17:26:04.984 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.984 -> Zu trocken --> Kreis EIN für i=2
17:26:04.984 -> Zeitablauf --> Kreis AUS für i=2
17:26:04.984 -> Zu trocken --> Kreis EIN für i=2
17:26:05.031 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.031 -> Zu trocken --> Kreis EIN für i=2
17:26:05.031 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.031 -> Zu trocken --> Kreis EIN für i=2
17:26:05.031 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.031 -> Zu trocken --> Kreis EIN für i=2
17:26:05.031 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.031 -> Zu trocken --> Kreis EIN für i=2
17:26:05.084 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.084 -> Zu trocken --> Kreis EIN für i=2
17:26:05.084 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.084 -> Zu trocken --> Kreis EIN für i=2
17:26:05.084 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.084 -> Zu trocken --> Kreis EIN für i=2
17:26:05.084 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.084 -> Zu trocken --> Kreis EIN für i=2
17:26:05.084 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.131 -> Zu trocken --> Kreis EIN für i=2
17:26:05.131 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.131 -> Zu trocken --> Kreis EIN für i=2
17:26:05.131 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.131 -> Zu trocken --> Kreis EIN für i=2
17:26:05.131 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.131 -> Zu trocken --> Kreis EIN für i=2
17:26:05.131 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.184 -> Zu trocken --> Kreis EIN für i=2
17:26:05.184 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.184 -> Zu trocken --> Kreis EIN für i=2
17:26:05.184 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.184 -> Zu trocken --> Kreis EIN für i=2
17:26:05.184 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.184 -> Zu trocken --> Kreis EIN für i=2
17:26:05.184 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.184 -> Zu trocken --> Kreis EIN für i=2
17:26:05.231 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.231 -> Zu trocken --> Kreis EIN für i=2
17:26:05.231 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.231 -> Zu trocken --> Kreis EIN für i=2
17:26:05.231 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.231 -> Zu trocken --> Kreis EIN für i=2
17:26:05.231 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.231 -> Zu trocken --> Kreis EIN für i=2
17:26:05.285 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.285 -> Zu trocken --> Kreis EIN für i=2
17:26:05.285 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.285 -> Zu trocken --> Kreis EIN für i=2
17:26:05.285 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.285 -> Zu trocken --> Kreis EIN für i=2
17:26:05.285 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.285 -> Zu trocken --> Kreis EIN für i=2
17:26:05.285 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.331 -> Zu trocken --> Kreis EIN für i=2
17:26:05.331 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.331 -> Zu trocken --> Kreis EIN für i=2
17:26:05.331 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.331 -> Zu trocken --> Kreis EIN für i=2
17:26:05.331 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.331 -> Zu trocken --> Kreis EIN für i=2
17:26:05.331 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.384 -> Zu trocken --> Kreis EIN für i=2
17:26:05.384 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.384 -> Zu trocken --> Kreis EIN für i=2
17:26:05.384 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.384 -> Zu trocken --> Kreis EIN für i=2
17:26:05.384 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.384 -> Zu trocken --> Kreis EIN für i=2
17:26:05.384 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.431 -> Zu trocken --> Kreis EIN für i=2
17:26:05.431 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.431 -> Zu trocken --> Kreis EIN für i=2
17:26:05.431 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.431 -> Zu trocken --> Kreis EIN für i=2
17:26:05.431 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.431 -> Zu trocken --> Kreis EIN für i=2
17:26:05.431 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.485 -> Zu trocken --> Kreis EIN für i=2
17:26:05.485 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.485 -> Zu trocken --> Kreis EIN für i=2
17:26:05.485 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.485 -> Zu trocken --> Kreis EIN für i=2
17:26:05.485 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.485 -> Zu trocken --> Kreis EIN für i=2
17:26:05.485 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.485 -> Zu trocken --> Kreis EIN für i=2
17:26:05.532 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.532 -> Zu trocken --> Kreis EIN für i=2
17:26:05.532 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.532 -> Zu trocken --> Kreis EIN für i=2
17:26:05.532 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.532 -> Zu trocken --> Kreis EIN für i=2
17:26:05.532 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.532 -> Zu trocken --> Kreis EIN für i=2
17:26:05.585 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.585 -> Zu trocken --> Kreis EIN für i=2
17:26:05.585 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.585 -> Zu trocken --> Kreis EIN für i=2
17:26:05.585 -> Zeitablauf --> Kreis AUS für i=2
17:26:05.585 -> Zu trocken --> Kreis EIN für i=2
17:26:05.585 -> Zeitablauf --> Kreis AUS für i=2

Für mich sieht es so aus, das er die Bedingung hat zu Gießen, also Relais 3 an, aber er es dann nicht die 3800ms an lässt, sonder es immer wieder an und aus setzt und die Sperrzeit wird auch ignoriert. Sonst wäre es ja sofort aus und dann erst nach ablauf der Zeit wieder an.

siehste,
mit vernünftigen debug Ausgaben kann man debuggen.

beim einschalten fehlt das merken der Uhrzeit

      // Feuchte testen und gegebenfalls einschalten vorrausgesetzt Sperrzeit ist abgelaufen
      if (circuit[i].prozent  <= Trocken && millis() - circuit[i].previousMillis > (timeActive + timeBlock))
      {
        circuit[i].previousMillis = millis(); // wäre schon gut das zu setzen ...
        Serial.print(F("Zu trocken --> Kreis EIN für i=")); Serial.println(i);
        digitalWrite(circuit[i].relaisPin, HIGH); // Relais aktivieren
      }

ohne kommentar :wink:

ich gehe davon aus, dass die Weboberfläche nun läuft?

1 Like

Dann mal den von mir entwickelten.
Hinweis: Ich habe einige Kleinigkeiten verändert.
Dazu gehört, das ich die SperrFunktion nicht brauche und auch die dazugehörige Variable nicht.

Die Berechnung des Messwertes Feuchte ist ebenfalls etwas anders.
Und dann gibt es ganz viel schicke Sachen... Ich habe einiges kommentiert.
Da Du schon mit mir zusammen einige Dinge entwickelt hast, solltest Du dich zurecht finden.

// Gewaechshauswebif v4_beta_1 vom 28.06.2022
// basiert auf https://forum.arduino.cc/t/gewachshausbewasserung-sperre-immer-wieder-aktiv/1007214

//Ethernet Shield
  #include <SPI.h>
  #include <Ethernet.h>
  byte mac[] = { 0x00, 0x10, 0xFA, 0x6E, 0x28, 0x4A }; // MAC Adresse des Arduino Boards
  EthernetServer server(80); // Port Einstellung (Standard für HTML : 80)

struct Sensor
{
  const uint8_t sensorPin;
  const uint8_t schalterPin;
  const uint8_t relaisPin;
  uint32_t sperreStart = 0;
  uint16_t sensorWert = 0; // der kann später raus - nur zum debuggen!
  float feuchte = 255.00;
  Sensor(const uint8_t sensorPin, const uint8_t schalterPin, const uint8_t relaisPin) : sensorPin(sensorPin), schalterPin(schalterPin), relaisPin(relaisPin)
  {}
};

Sensor sensors[]
{
  {A1, 3, 7}, // Anschluss, Schalter, Relais
  {A2, 4, 8},
  {A3, 5, 9},
  {A4, 6, A5},
};
const uint8_t sensorsNum = sizeof(sensors) /
                           sizeof(Sensor);  // Merker, wieviele Sensorkreise
const byte schalterAllPin = 2;              // Schalter zum Öffnen aller Ventile , am Pin D2
const uint8_t ldrPin = A0;                  // LDR am Pin A0
unsigned int ldrWert;

const uint16_t minimalFeuchte = 1024;       // Minimaler Feuchtigkeitswert (kleinster Wert)
const uint16_t maximalFeuchte = 320;        // Maximaler Feuchtigkeitswert (größter Wert)

const uint8_t trocken = 25;                 // Wert in % ab dem Bewässert werden soll
const uint8_t tag = 20;                     // Wert ab dem der Tag beginnt
const uint32_t pumpTime = 15000;             // Bewässerungszeit in ms
const uint32_t sperrZeit = 1000UL * 90;// SperrZeit in ms
enum relaisZustand {an, aus};

void setup ()
{
  delay(500);
  Serial.begin(115200); // Serielle Schnittstelle initialisieren
  Ethernet.begin(mac); // Ethernet initialisieren
  server.begin(); // Auf Clients warten
  Serial.print(F("Arduino's IP Address: "));
  Serial.println(Ethernet.localIP());
  Serial.print(F("DNS Server's IP Address: "));
  Serial.println(Ethernet.dnsServerIP());
  Serial.print(F("Gateway's IP Address: "));
  Serial.println(Ethernet.gatewayIP());
  Serial.print(F("Network's Subnet Mask: "));
  Serial.println(Ethernet.subnetMask());
  for (byte b = 0; b < sensorsNum; b++)
  {
    pinMode(sensors[b].sensorPin, INPUT_PULLUP);
    pinMode(sensors[b].schalterPin, INPUT_PULLUP);
    pinMode(sensors[b].relaisPin, OUTPUT);
    digitalWrite(sensors[b].relaisPin, relaisZustand::aus);
    sensorSchaltung(b);
  }
  pinMode(schalterAllPin, INPUT_PULLUP);
}
//
void loop ()
{
  sensorAbfrage();
  webPage();
  serialDebug();
}
//
void sensorAbfrage()
{
  if (!digitalRead(schalterAllPin))                              // Wenn der aktiv ist ...
  {
    for (byte b = 0; b < sensorsNum; b++)
      digitalWrite(sensors[b].relaisPin, relaisZustand::an);     // ... dann alle ...
    return;                                                      // ... und Ende
  }
  ldrWert = analogRead (ldrPin);                                 // Licht abfragen
  for (byte b = 0; b < sensorsNum; b++)                          // alle Sensoren durchzaehlen
  {
    feuchteErmitteln(b);                                         // erstmal den Boden befragen
    if ((ldrWert < tag) ||                                       // es ist Nacht ...
        (millis() - sensors[b].sperreStart > pumpTime))          // ... oder Pumpzeit abgelaufen ...
      digitalWrite(sensors[b].relaisPin, relaisZustand::aus);    // ... Ausgang löschen
    if (ldrWert >= tag)                                          // es ist Tag ...
    {
      if (!digitalRead(sensors[b].schalterPin))                  // ... Zwangsschaltung aktiv?
        digitalWrite(sensors[b].relaisPin, relaisZustand::an);
      else                                                       // Keine Zwangsschaltung...
      {
        if (millis() - sensors[b].sperreStart > sperrZeit)       // Sperrtzeit abgelaufen? ...
          sensorSchaltung(b);                                    // ...dann auf Sensor reagieren
      }
    }
  }
}
//
void feuchteErmitteln(const byte sensNum)
{
  sensors[sensNum].sensorWert = analogRead(sensors[sensNum].sensorPin);
  sensors[sensNum].sensorWert >= maximalFeuchte ?
  sensors[sensNum].feuchte = ((sensors[sensNum].sensorWert - maximalFeuchte) * 100.0) / (minimalFeuchte - maximalFeuchte) :
                             sensors[sensNum].feuchte = 0.0;
}
//
void sensorSchaltung(const byte sensNum)
{
  if (sensors[sensNum].feuchte <= trocken)
  {
    sensors[sensNum].sperreStart = millis();
    digitalWrite(sensors[sensNum].relaisPin, relaisZustand::an);
  }
}
//
void webPage()
{
  EthernetClient client = server.available(); //Prüfen, ob Client Seite aufruft
  if (client)
  {
    // Seitenaufruf durch User
    // Ausgabe in HTML
    server.print("HTTP/1.1 200 OK\r\nServer: Arduino UNO R3\r\nContent-Type: text/html\r\nRefresh: 10\r\n\r\n"
                 "<!DOCTYPE html>"
                 "<html lang='de'>"
                 "<head>"
                 "<meta charset='utf-8'>"
                 "<link rel=icon type=image/vnd.microsoft.icon href=http://IP DES NAS/Gfp.png>"
                 "<title>Gewächshausbewässerung</title>"
                 "</head>"
                 "<style>"
                 "body { background-image: url('http://IP DES NAS/Gewächshaus.png');"
                 "background-repeat: no-repeat;"
                 "background-attachment: fixed;"
                 "background-position: center;"
                 "</style>"
                 "<center><img src=http://IP DES NAS/Gfp2.png width=390 height=54><BR/>"
                 "<font size=7><strong>Gewächshausbewässerung</strong></font size><BR/><BR/>"
                 "<font size=5>LDR: ");
    server.print(ldrWert);
    server.print("<BR/>Bewässerung: ");
    server.print(ldrWert >= tag ? "Ein" : "Aus");
    server.print("<BR/>Schalter1 (Alle Ventile geöffnet): ");
    server.print(digitalRead(schalterAllPin) ? "AUS" : "EIN");
    server.print("<BR/><BR/>"
                 "<table>"
                 "<table border=1>"
                 "<tr>"
                 "<td>Kreislauf</td>");
    for (byte b = 1; b <= sensorsNum; b++)
    {
      server.print("<td align=right>");
      server.print(b);
      server.print("</td>");
    }
    server.print("</tr>"
                 "<tr>"
                 "<td>manuell aktiv</td>");
    for (byte b = 0; b < sensorsNum; b++)
    {
      server.print("<td align = right>");
      server.print(digitalRead(sensors[b].schalterPin) ? "AUS" : "EIN");
    }
    server.print("</tr>"
                 "<tr>"
                 "<td>Feuchtigkeit in %</td>");
    for (byte b = 0; b < sensorsNum; b++)
    {
      server.print("<td align = right>");
      server.print(sensors[b].feuchte);
    }
    server.print("</tr>"
                 "<tr>"
                 "<td>Relaisstatus</td>");
    for (byte b = 0; b < sensorsNum; b++)
    {
      server.print("<td align = right>");
      server.print(digitalRead(sensors[b].relaisPin) == relaisZustand::aus ? "Aus" : "Ein");
    }
    server.print("</tr>"
                 "<tr>"
                 "<td>Relaissperre</td>");
    for (byte b = 0; b < sensorsNum; b++)
    {
      server.print("<td align = right>");
      server.print((millis() - sensors[b].sperreStart < pumpTime) ? "Aktiv" : "Inaktiv");
    }
    server.print("</tr>"
                 "<tr>"
                 "<td>Rest Sperrzeit in Min.</td>");
    for (byte b = 0; b < sensorsNum; b++)
    {
      server.print("<td align = right>");
      uint32_t warteZeit = 0;
      if (millis() - sensors[b].sperreStart < sperrZeit)
      {
        warteZeit = sperrZeit - (millis() - sensors[b].sperreStart);
        server.print(warteZeit / 60000); server.print(':'); // Minuten
        if (warteZeit % 60000 / 1000 < 10) server.print('0');
        server.print((warteZeit % 60000) / 1000);
      }
      else server.print(warteZeit);
    }
    server.print("</tr>"
                 "</table>"
                 "</font size>"
                 "</center>"
                 "</body>"
                 "</html>");
    delay(10); // Kurzer Delay, um Daten zu senden
    client.stop(); // Verbindung mit dem Client trennen
  }
}
//
void serialDebug()
{
  static unsigned long lastprint;
  if (millis() - lastprint > 3000) // Testausgabe alle 3s
  {
    lastprint = millis();
    Serial.print(F("LDR: ")); Serial.println(ldrWert);                                               // LDR oben links
    Serial.print(F("Hauptschalter: ")); Serial.println(digitalRead(schalterAllPin) ? "AUS" : "AN");  // Abfrage Manuell
    Serial.println(F("Sensor\tFeuchte\t\tSchalter\tRelais\tP-Sperre\tnextSensorRead"));              // TabellenKopf
    for (byte b = 0; b < sensorsNum; b++)
    {
      Serial.print(b + 1); Serial.print('\t');
      Serial.print(analogRead(sensors[b].sensorPin)); Serial.print(' '); Serial.print(sensors[b].feuchte); Serial.print(F("%\t"));
      Serial.print(digitalRead(sensors[b].schalterPin) ? "AUS" : "AN");                    Serial.print("\t\t"); // Zustand Schalter
      Serial.print(digitalRead(sensors[b].relaisPin) == relaisZustand::an ? "AN" : "AUS"); Serial.print('\t');   // Zustand Relais
      Serial.print((millis() - sensors[b].sperreStart < pumpTime) ? "AN" : "AUS");         Serial.print("\t\t"); // pumpen Sperre
      uint32_t warteZeit = 0;
      if (millis() - sensors[b].sperreStart < sperrZeit)                                                         // Restzeit Abfrageintervall
        warteZeit = sperrZeit - (millis() - sensors[b].sperreStart) ;
      Serial.println(warteZeit);
    }
  }
}

Ich wollte nicht unbedingt auf weniger Zeilen Code kommen, aber insgesamt werden es nur 242 :slight_smile:

17:40:44.333 -> LDR: 727
17:40:44.333 -> Hauptschalter: AUS
17:40:44.333 -> Sensor	Feuchte		Schalter	Relais	P-Sperre	nextSensorRead
17:40:44.333 -> 1	1022 99.57%	AUS		AUS	AUS		0
17:40:44.333 -> 2	1021 99.57%	AN		AN	AUS		0
17:40:44.333 -> 3	14 0.00%	AUS		AUS	AUS		41800
17:40:44.333 -> 4	1022 99.57%	AUS		AUS	AUS		0