Logik/Code-Problem bei einem etwas komplexeren Sketch

Guten Abend,

als Erstes bedanke ich mich für euer Engergement, euren Tatendrang und eure Hilfestelungen. Ich lese nun schon ein paar Tage hier im Forum. Leider habe ich noch keine passende Lösung für meinen Sketch finden können. Ich habe das Problem, dass ich das was ich realisieren möchte noch nicht programmieren kann.

So sieht die Hardware aus:

Die Anlage besteht aus 8 Relais (Relaisplatine) wovon 7 angesteuert werden, davon werden 5 Ventile, eine LED (24V im Taster integriert) und eine Pumpe geschaltet. Außerdem soll ein Niederdruckschalter beim Öffnen die Relais 3, 4, 5, 6, 7 und 8 ausschalten und ein Hochdruckschalter beim Öffnen das Relais 8 ausschalten. Der Zeitsteuerung soll über ein Uhr-Modul (RTC DS3231) funktionieren. Die Uhr ist im Moment noch nicht eingebunden, da sie heute erst mit der Post kam.

So könnte ein systematischer Sketchaufbau aussehen.

Funktion A

Schalte Relais 6, 3, 4, und 5 mit LOW an.
Die Relais sollen 30 Sekunden an sein.
Schalte Relais 6 und 3 mit LOW an.
Die Relais sollen 30 Sekunden an sein.
Während Funktion A läuft soll keine Eingabe über den Taster möglich sein und die LED_1 leuchten. Die Funktion A soll sich alle 6 Stunden wiederholen.

Funktion B

Wenn der Taster x (Betätigungsart) gedrückt wird,
schalte Relais 6, 3 und 8 mit LOW an.
Relais sollen 15 Sekunden an sein.
Schalte Relais 6, 7 und 8 mit LOW an.
Sollen die Relais Zeit y an sein.
Während die Funktion B läuft soll keine Eingabe über den Taster möglich sein (außer mit Funktion …) und die LED_1 soll im Intervall z blinken.
Für die Werte:
x: x1 = kurz, x2 = doppelt, x3 = dreifach, x4 = lange (2s), x5 = sehr lange (4s)
y: y1 = 30 s, y2 = 60 s, y3 = 120 s, y4 = 480 s, y5 = ALLES AUS,
z: z1 = 500ms, z2 = 1s, z3 = 2s, z4 = 4s,

Funktion C

Wenn der Eingang NDS öffnet wird sollen die Relais 3, 4, 5, 6, 7, 8, ausgeschaltet werden und die LED_1 blinken (250ms). Die Funktionen C, D und E haben Vorrang.
Funktion D
Wenn der Eingang HDS öffnet wird soll das Relais 8 ausgeschaltet werden. Die Funktionen C, D und E haben Vorrang.

Funktion E
Wenn der Eingang WS schließt sollen die Relais 3, 4, 5, 6, 7, 8 ausgeschaltet werden, die LED_1 blinken (250ms) und ein Ton über den Buzzer ertönen. Die Funktionen C, D und E haben Vorrang.

Leider sieht mein bisheriger Sketch etwas anders aus und funktioniert wenig gut.
Bitte mich nicht verbal erhängen. Ich weis es im Moment noch nicht besser und möchte gerne von euch Hilfe erhalten. Viele Code-Brocken habe ich hier aus dem Forum.
Ab dem void benchmark funktioniert der Code nicht.

Ich wollte jetzt hier mein Code einfügen, aber irgendwie geht das nicht.

Vielen Dank schon mal für eure Hilfe.

der_anfaenger:
Ich wollte jetzt hier mein Code einfügen, aber irgendwie geht das nicht.

„irgendwie geht das nicht“ ist ein bisschen unspezifisch. Funktioniert copy&paste nicht, wie es das sonst tut?

Gruß

Gregor

Soweit ich weiss sind beim ersten Post keine Anhänge und Bilder erlaubt.

Versuch es einfach noch mal.

Hallo,
hier nun hoffentlich der Code

alles was ausgekommentiert ist, hat nicht oder nur begrenzt funktioniert.
Die LED ist im Code nicht eingebunden.

Ich musste den Code aufgrund der Länge (>9000 Zeichen) in 2 Teile trennen

Teil 1

//*** Konstantendefinition 

// Relais Ausgänge

#define relaisPin_P 9    // Relais 8
#define relaisPin_7 8    // Relais 7
#define relaisPin_6 7    // Relais 6
#define relaisPin_5 6    // Relais 5
#define relaisPin_4 5    // Relais 4
#define relaisPin_3 4    // Relais 3
#define relaisPin_2 3    // Relais für LED

// Eingänge

#define NDS_Pin     2    // Eingang Niederdruckschalter
#define HDS_Pin     1    // Eingang Hochdruckschalter
#define tasterPin  12    // Eingang Tastereingang 

// Zeitkonstaten

const long zeit6H = 1800000;     // 6 h    21600000
const long intervalvmsp = 30000 + zeit6H;   // Intervall vMSpühlung + Zeit6H
const long interval_LED_schnell = 250;    // Intervall LED_schnell
const long interval_LED_normal = 500;    // Intervall LED_normal
const long interval_LED_langsam = 750;   // Intervall LED_langsam

//******** Variablendefinition 

unsigned long currentMillis;   // Zeit beim Starten des Programs

//********* Tasterprogramm 

// Tastenerkennung SHORTCLICK, DOUBLECLICK, LONGCLICK
#define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
#define PRELLZEIT 5             // Prellzeit in Millisekunden
#define SHORTCLICKTIME 250      // Längste Zeit für einen SHORTCLICK
#define DOUBLECLICKTIME 400     // Längste Zeit für den zweiten Klick beim DOUBLECLICK
#define LONGCLICKTIME 800       // Mindestzeit für einen LONGGLICK
byte buttonPins[] = {12, A3}; // Arduino Pins (mehrere als Array möglich)
#define NUMBUTTONS sizeof(buttonPins)
byte buttonState[NUMBUTTONS];  // Aktueller Status des Buttons HIGH/LOW
enum {NONE, FIRSTDOWN, FIRSTUP, SHORTCLICK, DOUBLECLICK, LONGCLICK};
byte buttonResult[NUMBUTTONS]; // Aktueller Klickstatus der Buttons NONE/SHORTCLICK/LONGCLICK

//******* Pinbelegung 

void setup() {


 // serielle Ausgabe zur Kontrolle

 Serial.begin(9600);     // serielle Verbingung herstellen

 Serial.println("Button test");
 for (int i = 0; i < NUMBUTTONS; i++) pinMode(buttonPins[i], INPUTMODE);

 // Relais aus!!!
 digitalWrite(relaisPin_P, HIGH);    
 digitalWrite(relaisPin_7, HIGH);
 digitalWrite(relaisPin_6, HIGH);    
 digitalWrite(relaisPin_5, HIGH);    
 digitalWrite(relaisPin_4, HIGH);    
 digitalWrite(relaisPin_3, HIGH);    
 digitalWrite(relaisPin_2, HIGH);

 // Relais als Ausgänge definieren

 pinMode(relaisPin_P, OUTPUT);   // Den PWM PIN "relaisPin_P" als Ausgang gesetz. ...
 pinMode(relaisPin_7, OUTPUT);   // Den PWM PIN "relaisPin_7" als Ausgang gesetzen. ...
 pinMode(relaisPin_6, OUTPUT);   // Den PWM PIN "relaisPin_6" als Ausgang gesetzen. 
 pinMode(relaisPin_5, OUTPUT);   // Den PWM PIN "relaisPin_5" als Ausgang gesetzen. 
 pinMode(relaisPin_4, OUTPUT);   // Den PWM PIN "relaisPin_4" als Ausgang gesetzen. ...
 pinMode(relaisPin_3, OUTPUT);   // Den PWM PIN "relaisPin_3" als Ausgang gesetzen. ...
 pinMode(relaisPin_2, OUTPUT);   // Den PWM PIN "relaisPin_2" als Ausgang gesetzen. ...
 




void loop()

{
 
 //***** Programm ****

 currentMillis = millis();
 MSpuel();
 if (eingabe())
 {
   verarbeitung();
   ausgabe();
 }
 benchmark();
}


//******* MSpuel 

void MSpuel()
{
 static unsigned long zeitVmSp = 0;   // Zeit beim Starten des Programs

 if (currentMillis - zeitVmSp >= zeit6H)
 {
   Serial.println("  Test  ");
   Serial.println(currentMillis);
   digitalWrite(relaisPin_3, LOW);      // Relais 3
   digitalWrite(relaisPin_5, LOW);      // Relais 5
   digitalWrite(relaisPin_4, LOW);      // Relais 4
   digitalWrite(relaisPin_6, LOW);      // Relais 6
   //digitalWrite(relaisPin_P, LOW);    // Relais 8

   delay (20000);

   zeitVmSp = currentMillis;

   //digitalWrite(relaisPin_P, HIGH);    // Relais 8
   digitalWrite(relaisPin_6, HIGH);      // Relais 6
   digitalWrite(relaisPin_5, HIGH);      // Relais 5
   digitalWrite(relaisPin_4, HIGH);      // Relais 4
   digitalWrite(relaisPin_3, HIGH);      // Relais 3

   delay (10);

   digitalWrite(relaisPin_3, LOW);      // Relais 3
   digitalWrite(relaisPin_6, LOW);      // Relais 6
   //digitalWrite(relaisPin_P, LOW);    // Relais 8

   delay (15000);

   //digitalWrite(relaisPin_P, HIGH);    // Relais 8
   digitalWrite(relaisPin_6, HIGH);      // Relais 6
   digitalWrite(relaisPin_3, HIGH);      // Relais 3

 }
}

boolean eingabe()
// Rückgabewert false ==> Prellzeit läuft, Taster wurden nicht abgefragt
// Rückgabewert true ==> Taster wurden abgefragt und Status gesetzt
{
 static unsigned long lastRunTime;
 static unsigned long buttonDownTime[NUMBUTTONS];
 unsigned long now = millis();
 if (now - lastRunTime < PRELLZEIT) return false; // Prellzeit läuft noch
 lastRunTime = now;
 for (int i = 0; i < NUMBUTTONS; i++)
 {
   byte curState = digitalRead(buttonPins[i]);
   if (INPUTMODE == INPUT_PULLUP) curState = !curState; // Vertauschte Logik bei INPPUT_PULLUP
   if (buttonResult[i] >= SHORTCLICK) buttonResult[i] = NONE; // Letztes buttonResult löschen
   if (curState != buttonState[i]) // Flankenwechsel am Button festgestellt
   {
     if (curState)   // Taster wird gedrückt, Zeit merken
     {
       if (buttonResult[i] == FIRSTUP && now - buttonDownTime[i] < DOUBLECLICKTIME)
         buttonResult[i] = DOUBLECLICK;
       else
       {
         buttonDownTime[i] = now;
         buttonResult[i] = FIRSTDOWN;
       }
     }
     else  // Taster wird losgelassen
     {
       if (buttonResult[i] == FIRSTDOWN) buttonResult[i] = FIRSTUP;
       if (now - buttonDownTime[i] >= LONGCLICKTIME) buttonResult[i] = LONGCLICK;
     }
   }
   else // kein Flankenwechsel, Up/Down Status ist unverändert
   {
     if (buttonResult[i] == FIRSTUP && now - buttonDownTime[i] > DOUBLECLICKTIME)
       buttonResult[i] = SHORTCLICK;
   }
   buttonState[i] = curState;
 } // for
 return true;
}

Teil 2 (dauert etwas, weil ich nur alle 5 min posten darf):

void verarbeitung()
{
// dummy function
}


void ausgabe()
// Klickstatus von geklickten Buttons mit Zeitstempel ausgeben
{
for (int i = 0; i < NUMBUTTONS; i++)
{
  if (buttonResult >= SHORTCLICK) // Ein Button wurde geklickt
  {
    Serial.print(millis() / 1000.0, 3); // Zeitstempel mit Millisekundenauflösung
    Serial.print("\tPin-"); Serial.print(buttonPins);
    if (buttonResult == SHORTCLICK)
    {
      Serial.println("  300 ml");
      //digitalWrite(relaisPin_3, LOW);    // Relais 3
      //digitalWrite(relaisPin_6, LOW);    // Relais 6
      //digitalWrite(relaisPin_P, LOW);    // Relais 8

      //delay (15000);

      //digitalWrite(relaisPin_P, HIGH);    // Relais 8
      //digitalWrite(relaisPin_6, HIGH);    // Relais 6
      //digitalWrite(relaisPin_3, HIGH);    // Relais 3

      //delay (500);

      digitalWrite(relaisPin_7, LOW);    // Relais 7
      digitalWrite(relaisPin_6, LOW);    // Relais 6
      digitalWrite(relaisPin_P, LOW);    // Relais 8
      delay (20000);

      digitalWrite(relaisPin_P, HIGH);     // Relais 8
      digitalWrite(relaisPin_6, HIGH);     // Relais 6
      digitalWrite(relaisPin_7, HIGH);     // Relais 7
    }
    else if (buttonResult == DOUBLECLICK)
    {
      Serial.println("  1000 ml");
     
      //digitalWrite(relaisPin_3, LOW);    // Relais 3
      //digitalWrite(relaisPin_6, LOW);    // Relais 6
      //digitalWrite(relaisPin_P, LOW);    // Relais 8

      //delay (15000);

      //digitalWrite(relaisPin_P, HIGH);    // Relais 8
      //digitalWrite(relaisPin_6, HIGH);    // Relais 6
      //digitalWrite(relaisPin_3, HIGH);    // Relais 3

      //delay (500);
     
      digitalWrite(relaisPin_7, LOW);    // Relais 7
      digitalWrite(relaisPin_6, LOW);    // Relais 6
      digitalWrite(relaisPin_P, LOW);    // Relais 8
      delay (60000);

      digitalWrite(relaisPin_P, HIGH);    // Relais 8
      digitalWrite(relaisPin_6, HIGH);    // Relais 6
      digitalWrite(relaisPin_7, HIGH);    // Relais 7
    }
    else if (buttonResult == LONGCLICK)
    {
      Serial.println("  8000 ml");

      //digitalWrite(relaisPin_3, LOW);    // Relais 3
      //digitalWrite(relaisPin_6, LOW);    // Relais 6
      //digitalWrite(relaisPin_P, LOW);    // Relais 8

      //delay (15000);

      //digitalWrite(relaisPin_P, HIGH);    // Relais 8
      //digitalWrite(relaisPin_6, HIGH);    // Relais 6
      //digitalWrite(relaisPin_3, HIGH);    // Relais 3

      //delay (500);
     
      digitalWrite(relaisPin_7, LOW);      // Relais 7
      digitalWrite(relaisPin_6, LOW);      // Relais 6
      digitalWrite(relaisPin_P, LOW);      // Relais 8
      delay (480000);

      digitalWrite(relaisPin_P, HIGH);     // Relais 8
      digitalWrite(relaisPin_6, HIGH);     // Relais 6
      digitalWrite(relaisPin_7, HIGH);     // Relais 7
    }
  }
}
}

void benchmark() {
static unsigned long counterStartTime;
static unsigned long counter;
counter++;
if (counter >= 1000000L)
{
  Serial.print("Average Time per loop(): ");
  Serial.print((micros() - counterStartTime) / 1000000.0);
  Serial.println(" microseconds");
  counter = 0;
  counterStartTime = micros();
}
}



/*
//************************** Niederdruckschalter ***************************

void NDS()
{
if (NDS_Pin == LOW)                // wenn der Niederdruckschalter aktiv ist
  digitalWrite(relaisPin_6, LOW);   // Ventil zu
LED_schnell();
}

//************************** Hochdruckschalter ***************************
void HDS()
{
if (hds_Pin == LOW)                // wenn der Hochdruckschalter aktiv ist
digitalWrite(relaisPin_P, LOW);   // Pumpe aus
}

//************************* LED- Signale ****************************

void LED_schnell()
{
//unsigned long currentMillis_LED_s = 0;
//unsigned long previousMillis_s = 0;
//int ledState_s = LOW;

if (NDS_Pin == LOW)
unsigned long currentMillis_LED_s = millis();

if (currentMillis_LED_s - previousMillis_s >= interval_LED_schnell) {
// save the last time you blinked the LED
previousMillis_s = currentMillis_LED_s;

// if the LED is off turn it on and vice-versa:
if (ledState_s == LOW) {
ledState_s = HIGH;
} else {
ledState_s = LOW;
}

// set the LED with the ledState of the variable:
digitalWrite(relaisPin_2, ledState_s);
}
}


void LED_normal()
{
//if (millis() - zeitVmSp >= zeit6H)
unsigned long currentMillis_LED_n = millis();

if (currentMillis_LED_n - previousMillis_n >= interval_LED_normal) {
// save the last time you blinked the LED
previousMillis_n = currentMillis_LED_n;

// if the LED is off turn it on and vice-versa:
if (ledState_n == LOW) {
ledState_n = HIGH;
} else {
ledState_n = LOW;
}

// set the LED ) )
digitalWrite(relaisPin_2, ledState_n);
}
}

void LED_langsam()
{

if (millis() - zeitVmSp >= zeit6H)
unsigned long currentMillis_LED_n = millis();

if (currentMillis_LED_l - previousMillis_l >= interval_LED_normal) {
// save the last time you blinked the LED
previousMillis_l = currentMillis_LED_l;

// if the LED is off turn it on and vice-versa:
if (ledState_l == LOW) {
ledState_l = HIGH;
} else {
ledState_l = LOW;
}

// set the LED with the ledState of the variable:
digitalWrite(relaisPin_2, ledState_l);


}
}
*/

Setze deinen Code bitte in Code-Tags. Dann ist er leichter lesbar, und Du verhinderst, dass Teile des Codes als Formatierungszeichen interpretiert werden. Das geht auch nachträglich indem Du deinen Post editierst, den Code markierst und dann oben links auf </> klickst.

Danke für den Hinweis MicroBahner.

Könntet Ihr mir bitte bei meinem Vorhaben den Code so umzusetzen, wie er oben als Text formuliert steht, so umzusetzen.

Oder ist das Blödsinn den Code wie im ersten Beitrag geschrieben steht zu programmieren.

Könntet Ihr mir bitte ein paar Code-Fetzen zukommen lassen, damit ich diese anpassen und zu einem Ganzen zusammenfügen kann.

Ich freue mich auf eure Hilfe.

LG der_Anfaenger

der_anfaenger:
Oder ist das Blödsinn den Code wie im ersten Beitrag geschrieben steht zu programmieren.

Ja, ist es.

Die Kombination aus millis() und delay() funktioniert definitiv nicht.
Du wirst um einen vernünftige Ablaufsteuerung nicht herumkommen.

Musst du die Relais wirklich in umgekehrter Reihenfolge wieder deaktivieren?

#define HDS_Pin     1    // Eingang Hochdruckschalter

Du benutzt einen Leonardo oder Micro?
Wenn nicht kollidiert das mit der Seriellen Schnittstelle.

Der Kode ist angefüllt mit inhaltslosen Kommentaren, dafür gibt es nur wenige sinnvolle Namen.

Beispiel:

//*** Konstantendefinition

// Relais Ausgänge

#define relaisPin_P 9    // Relais 8
#define relaisPin_7 8    // Relais 7
#define relaisPin_6 7    // Relais 6
#define relaisPin_5 6    // Relais 5
#define relaisPin_4 5    // Relais 4
#define relaisPin_3 4    // Relais 3
#define relaisPin_2 3    // Relais für LED

Warum behandelst du zwei verschiedene Tasten, wenn die doch das Gleiche machen?
Da ist es doch einfacher die Tasten anders zu verdrahten (beide an den gleichen Pin).

Eigene Tasten die einfach nur auf Betätigung reagieren wären mir sympathischer,
diese würde ich mit einer Library auswerten.

Hallo Whandall,

es gibt einen Betätigungstaster mit integrierter LED mit dem die Anlage angeschaltet und ausgeschaltet werden soll.

Die Schalter HDS (Hochdruckschalter) und NDS (Niederdruckschalter) sind Schalter die im System integriert sind und werden vom Wasserdruck geschaltet und dienen in erster Linie als Schutz der Komponenten. Diese Schalter sind Bauartbeding jeweils Öffner.
Der Schalter WS (Wasserstop) ist ein Schließer und soll schalten, wenn Wasser aus der Anlage austritt.

Nein, es ist nicht notwendig die Relais (Ventile) in umgekehrter Reihenfolge an und aus zu schalten. Dies habe ich am Anfang aus Unwissenheit programmiert und noch nicht geändert.

Ich habe auch schon festgestellt, dass es zu Problemen zwischen millis und delay kommt, doch leider reicht mein Verständnis noch nicht, dies in richtiger Weise zu programmieren. Ich bevorzuge millis.

Ist denn überhaupt klar was ich umsetzen möchte?

Mein Problem ist, ich weis nicht was am Code voller Blödsinn ist und was brauchbar ist und an welcher Baustelle ich als erste anfangen sollte.

Whandall könntest du mir bitte ein Beispiel für eine vernünftige Ablaufsteuerung geben?

Was meinst du damit?

Du benutzt einen Leonardo oder Micro?
Wenn nicht kollidiert das mit der Seriellen Schnittstelle.

Whandall:

#define HDS_Pin     1    // Eingang Hochdruckschalter

Du benutzt einen Leonardo oder Micro?
Wenn nicht kollidiert das mit der Seriellen Schnittstelle.

der_anfaenger:
Was meinst du damit?

Pin 1 ist der RX Pin der seriellen Schnittstelle (auf Unos, Megas, Pro Minis).

der_anfaenger:
an welcher Baustelle ich als erste anfangen sollte.

Beim Implementieren von Tastendruck "ganz lang" und "Dreifach-Klick".

Du hast immer noch nicht beantwortet warum du mehrere Tasten benutzt, die alle das Gleiche machen.

der_anfaenger:
Der Schalter WS (Wasserstop) ist ein Schließer und soll schalten, wenn Wasser aus der Anlage austritt.

Der Schalter kommt in deinem Kode nicht vor.

Whandall:
Pin 1 ist der RX Pin der seriellen Schnittstelle (auf Unos, Megas, Pro Minis).

Ok, danke für die Info. Dann muss ich an der Stelle einen anderen Pin verwenden.

Whandall:
Beim Implementieren von Tastendruck "ganz lang" und "Dreifach-Klick".

Das habe ich bereits versucht, aber ich bin mit dem Code an der Stelle nicht zurecht gekommen.

Setze mich nachher (heute Abend) gleich mal ran und werde den geänderten Code hier hochladen.

Whandall:
Du hast immer noch nicht beantwortet warum du mehrere Tasten benutzt, die alle das Gleiche machen.

Das ist ein kopierter Code den ich nur teilweise verstehe.

Whandall:
Der Schalter kommt in deinem Kode nicht vor.

Ja, aber er soll noch hinzugefügt werden.

Danke, dass du meinen Code zerlegst und mir hilfst mir selbst zu helfen.

Außerdem ist die Uhr noch nicht implementiert. Dies soll auch noch passieren.

Kannst du mir diesbezüglich Tipps geben?

Die Uhr einzubauen ist nicht schwer,
du solltest zunächst mal festlegen was genau wann passieren soll.

Die anderen Abläufe (alle 6 Stunden, die Relasizeiten) brauchen keine Uhr.

Was ist mit den auskommentierten (Anfangs-)Sequenzen?
War das nur ein Experiment, oder sollte das nachher enthalten sein?

Die Sensoren habe ich noch auskommentiert.

#define relaisPin_8 9    // Relais 8
#define relaisPin_7 8    // Relais 7
#define relaisPin_6 7    // Relais 6
#define relaisPin_5 6    // Relais 5
#define relaisPin_4 5    // Relais 4
#define relaisPin_3 4    // Relais 3
#define relaisPin_2 3    // Relais für LED

#define NDS_Pin     2    // Eingang Niederdruckschalter
#define HDS_Pin     11    // Eingang Hochdruckschalter
#define tasterPin   12    // Eingang Tastereingang
#define wasAuchImmer  A3  // Eingang Tastereingang

const unsigned long zeit6H = 21600000UL;

typedef enum eLedZustand { Aus, Schnell, Normal, Langsam } ledZustand;

ledZustand lSignal = Aus;
unsigned long ledInterval = 0;
unsigned long lastLedAction;

const byte relayPins[] = { relaisPin_2, relaisPin_3, relaisPin_4, relaisPin_5, relaisPin_6, relaisPin_7, relaisPin_8 };

unsigned long currentMillis;

// Tastenerkennung
enum {NONE, FIRSTDOWN, FIRSTUP, SHORTCLICK, DOUBLECLICK, LONGCLICK};
#define INPUTMODE INPUT_PULLUP
#define PRELLZEIT 5             // Millisekunden
#define SHORTCLICKTIME 250      // Längste Zeit für einen SHORTCLICK
#define DOUBLECLICKTIME 400     // Längste Zeit für den zweiten Klick beim DOUBLECLICK
#define LONGCLICKTIME 800       // Mindestzeit für einen LONGGLICK
byte buttonPins[] = {tasterPin, wasAuchImmer};
#define NUMBUTTONS sizeof(buttonPins)
byte buttonState[NUMBUTTONS];  // HIGH/LOW
byte buttonResult[NUMBUTTONS]; // NONE/SHORTCLICK/LONGCLICK

enum Zustaende {
  unbeschaeftigt,
  spuelStart,   spuelPhase1,  spuelPause,  spuelPhase2,
  shortStart,   shortPhase1,  shortPause,  shortPhase2,
  doubleStart,  doublePhase1, doublePause, doublePhase2,
  longStart,    longPhase1,   longPause,   longPhase2,
};

byte aktuellerZustand = unbeschaeftigt;
unsigned long lastTransition;
unsigned long letzteSpuelung;
void setup() {
  Serial.begin(250000);
  Serial.println("Button test");
  for (byte i = 0; i < NUMBUTTONS; i++) {
    pinMode(buttonPins[i], INPUTMODE);
  }
  for (byte i = 0; i < sizeof(relayPins); i++) {
    digitalWrite(relayPins[i], HIGH);
    pinMode(relayPins[i], OUTPUT);
  }
}

void loop() {
  currentMillis = millis();
  //  if (digitalRead(NDS_Pin) == LOW) {  // wenn der Niederdruckschalter aktiv ist
  //    digitalWrite(relaisPin_6, HIGH);  // Ventil zu
  //    setLedState(Schnell);
  //  }
  //  if (digitalRead(HDS_Pin) == LOW) {  // wenn der Hochdruckschalter aktiv ist
  //    digitalWrite(relaisPin_8, HIGH);  // Pumpe aus
  //    setLedState(Langsam);
  //  }
  if (aktuellerZustand == unbeschaeftigt) {
    if (eingabe()) {
      ausgabe();
    } else if (currentMillis - letzteSpuelung >= zeit6H) {
      letzteSpuelung = currentMillis;
      aktuellerZustand = spuelStart;
    }
  } else {
    switch (aktuellerZustand) {
      case spuelStart:
        setupAndGoToState(0b0001111, true, spuelPhase1, PSTR("Test")); // mit 8 0b0101111
        break;
      case spuelPhase1:
        switchAndNewStateAfter(0b0001111, false, spuelPause, 20000);  // mit 8 0b0101111
        break;
      case spuelPause:
        switchAndNewStateAfter(0b0001001, true, spuelPhase2, 10);  // mit 8 0b0101001
        break;
      case spuelPhase2:
        switchAndNewStateAfter(0b0001001, false, unbeschaeftigt, 15000);  // mit 8 0b0101001
        break;

      case shortStart:
        setupAndGoToState(0b0101001, true, shortPhase1, PSTR("300 ml"));
        break;
      case shortPhase1:
        switchAndNewStateAfter(0b0101001, false, shortPause, 15000);
        break;
      case shortPause:
        switchAndNewStateAfter(0b0111000, true, shortPhase2, 500);
        break;
      case shortPhase2:
        switchAndNewStateAfter(0b0111000, false, unbeschaeftigt, 20000);
        break;

      case doubleStart:
        setupAndGoToState(0b0101001, true, doublePhase1, PSTR("1000 ml"));
        break;
      case doublePhase1:
        switchAndNewStateAfter(0b0101001, false, doublePause, 15000);
        break;
      case doublePause:
        switchAndNewStateAfter(0b0111000, true, shortPhase2, 500);
        break;
      case doublePhase2:
        switchAndNewStateAfter(0b0111000, false, unbeschaeftigt, 60000UL);
        break;

      case longStart:
        setupAndGoToState(0b0101001, true, longPhase1, PSTR("8000 ml"));
        break;
      case longPhase1:
        switchAndNewStateAfter(0b0101001, false, longPause, 15000);
        break;
      case longPause:
        switchAndNewStateAfter(0b0111000, true, longPhase2, 500);
        break;
      case longPhase2:
        switchAndNewStateAfter(0b0111000, false, unbeschaeftigt, 480000UL);
        break;
    }
  }
  if (lSignal != Aus && currentMillis - lastLedAction >= ledInterval) {
    lastLedAction = currentMillis;
    digitalWrite(relaisPin_2, !digitalRead(relaisPin_2));
  }
  benchmark();
}

void setLedState(ledZustand lz) {
  if (lz == Aus) {
    ledInterval = 0;
    digitalWrite(relaisPin_2, HIGH);
  } else if (lz != lSignal) {
    digitalWrite(relaisPin_2, LOW);
    ledInterval = lz * 250;
    lastLedAction = currentMillis;
  }
  lSignal = lz;
}

void switchRelaisSet(byte mask, bool toWhat) {
  for (byte idx = 1; mask; idx++, mask >>= 1) {
    if (mask & 1) {
      digitalWrite(relayPins[idx], toWhat ? LOW : HIGH);
    }
  }
}

void setupAndGoToState(byte mask, bool toWhat, byte newState, char* txt) {
  switchRelaisSet(mask, toWhat);
  lastTransition = currentMillis;
  aktuellerZustand = newState;
  Serial.print(F("  "));
  Serial.print((__FlashStringHelper*)txt);
  Serial.write(' ');
  Serial.println(millis());
}

void switchAndNewStateAfter(byte mask, bool toWhat, byte newState, unsigned long duration) {
  if (currentMillis - lastTransition >= duration) {
    switchRelaisSet(mask, toWhat);
    Serial.print(F("  "));
    Serial.print(aktuellerZustand);
    Serial.print(F(" -> "));
    Serial.print(newState);
    Serial.print(F(" nach "));
    Serial.print(currentMillis - lastTransition);
    Serial.println();
    lastTransition = currentMillis;
    aktuellerZustand = newState;
  }
}

boolean eingabe()
// Rückgabewert false ==> Prellzeit läuft, Taster wurden nicht abgefragt
// Rückgabewert true ==> Taster wurden abgefragt und Status gesetzt
{
  static unsigned long lastRunTime;
  static unsigned long buttonDownTime[NUMBUTTONS];
  unsigned long now = millis();
  if (now - lastRunTime < PRELLZEIT) return false; // Prellzeit läuft noch
  lastRunTime = now;
  for (byte i = 0; i < NUMBUTTONS; i++)
  {
    byte curState = digitalRead(buttonPins[i]);
    if (INPUTMODE == INPUT_PULLUP) curState = !curState; // Vertauschte Logik bei INPPUT_PULLUP
    if (buttonResult[i] >= SHORTCLICK) buttonResult[i] = NONE; // Letztes buttonResult löschen
    if (curState != buttonState[i]) // Flankenwechsel am Button festgestellt
    {
      if (curState)   // Taster wird gedrückt, Zeit merken
      {
        if (buttonResult[i] == FIRSTUP && now - buttonDownTime[i] < DOUBLECLICKTIME)
          buttonResult[i] = DOUBLECLICK;
        else
        {
          buttonDownTime[i] = now;
          buttonResult[i] = FIRSTDOWN;
        }
      }
      else  // Taster wird losgelassen
      {
        if (buttonResult[i] == FIRSTDOWN) buttonResult[i] = FIRSTUP;
        if (now - buttonDownTime[i] >= LONGCLICKTIME) buttonResult[i] = LONGCLICK;
      }
    }
    else // kein Flankenwechsel, Up/Down Status ist unverändert
    {
      if (buttonResult[i] == FIRSTUP && now - buttonDownTime[i] > DOUBLECLICKTIME)
        buttonResult[i] = SHORTCLICK;
    }
    buttonState[i] = curState;
  } // for
  return true;
}


void ausgabe()
// Klickstatus von geklickten Buttons mit Zeitstempel ausgeben
{
  for (byte i = 0; i < NUMBUTTONS; i++)
  {
    if (buttonResult[i] >= SHORTCLICK) {
      Serial.print(millis() / 1000.0, 3); // Zeitstempel mit Millisekundenauflösung
      Serial.print("\tPin-");
      Serial.println(buttonPins[i]);
      if (buttonResult[i] == SHORTCLICK) {
        aktuellerZustand = shortStart;
      } else if (buttonResult[i] == DOUBLECLICK) {
        aktuellerZustand = doubleStart;
      } else if (buttonResult[i] == LONGCLICK) {
        aktuellerZustand = longStart;
      }
    }
  }
}

void benchmark() {
  static unsigned long counterStartTime;
  static unsigned long counter;
  if (++counter >= 1000000L) {
    Serial.print("Average Time per loop(): ");
    Serial.print((micros() - counterStartTime) / 1000000.0);
    Serial.println(" microseconds");
    counter = 0;
    counterStartTime = micros();
  }
}

Whandall:
Die Uhr einzubauen ist nicht schwer,
du solltest zunächst mal festlegen was genau wann passieren soll.

Die anderen Abläufe (alle 6 Stunden, die Relasizeiten) brauchen keine Uhr.

Ein Bekannter sagte mir, wenn die Anlage über mehrere Wochen laufen soll, ist eine Uhr nur sinnvoll.

Whandall:
Was ist mit den auskommentierten (Anfangs-)Sequenzen?
War das nur ein Experiment, oder sollte das nachher enthalten sein?

Die Auskommentierten Anfangssequenzen sollten als einmal auszuführende Anfangsspülung beim Start des Programms dienen.

Die anderen Sequenzen waren Experimente / Versuch, welche nicht funktionierten und später zum Teil in den Code mit aufgenommen werden sollen / sollten.

Vielen vielen Dank für den umfangreichen Code.

Als erstes werde ich versuchen diesen zu verstehen.

Danke.

Ich muss leider eine Reihe Anfängerfragen stellen um den Code zu verstehen.

Was passiert in dem folgenden Codeabschnitt?

enum Zustaende {
  unbeschaeftigt,
  spuelStart,   spuelPhase1,  spuelPause,  spuelPhase2,
  shortStart,   shortPhase1,  shortPause,  shortPhase2,
  doubleStart,  doublePhase1, doublePause, doublePhase2,
  longStart,    longPhase1,   longPause,   longPhase2,
}

Was passiert in dem folgenden Codeabschnitt? Ich glaube das ganze ist ein switch in dem verschiedene Fälle abgefragt werden, doch leider begreife ich nicht was da genau passiert.

case spuelStart:
        setupAndGoToState(0b0001111, true, spuelPhase1, PSTR("Test")); // mit 8 0b0101111
        break;

Ich bin heute zu müde und werde hoffentlich mit deiner Antwort morgen eventuell mehr verstehen und dann weitere Fragen stellen.

Vielen Dank schon mal und gute Nacht

der_anfaenger:
Was passiert in dem folgenden Codeabschnitt?

enum Zustaende {

unbeschaeftigt,
  spuelStart,  spuelPhase1,  spuelPause,  spuelPhase2,
  shortStart,  shortPhase1,  shortPause,  shortPhase2,
  doubleStart,  doublePhase1, doublePause, doublePhase2,
  longStart,    longPhase1,  longPause,  longPhase2,
}

Nichts. Da werden nur die verschieden Betriebszustände aufgelistet.

...Start Relais werden auf den 1. Zustand gebracht, weiter in Phase1
...Phase1 gewünschte Dauer abwarten, dann Relais wieder deaktivieren, weiter in Pause
...Pause Pause zwischen den Relais Zuständen, danach Aktivierung des 2. Zustands, weiter in Phase2
...Phase2 gewünschte Dauer abwarten, dann Relais wieder deaktivieren und beenden der Sequenz

der_anfaenger:
Was passiert in dem folgenden Codeabschnitt? Ich glaube das ganze ist ein switch in dem verschiedene Fälle abgefragt werden, doch leider begreife ich nicht was da genau passiert.

case spuelStart:

setupAndGoToState(0b0001111, true, spuelPhase1, PSTR("Test")); // mit 8 0b0101111
        break;

Die Routine fasst die immer wieder auftretende Anfangssequenz zusammen,
Setzen des Relaiszustands (ausgewählt durch die Bits im 1. Parameter) auf aktiv (2. Parameter),
weiter im Zustand spuelPhase1 (3. Parameter), Debugausgabe "Test" (4. Parameter).

PSTR() entspricht dem F() d.h. ermöglicht String Konstanten (aussschließlich) im Flash.

switch case erkläre ich nicht, schau in die Dokumentation.

Guten Tag,

vielen Dank für eure Hilfe.
Leider lag dieses Projekt sehr lange brach. Nun ist es endlich so weit, dass ich es weiterbearbeiten kann.
Ich habe einen großen Teil des Codes verstanden, aber ich besitze noch nicht die Fähigkeit den Code anzupassen.

Es hat sich außerdem an der Hardware einiges geändert:
Es gibt nun vier Taster.
Aufgaben der Taster:
Taster 1 Stop der Anlage
Taster 2 Start der Anlage für 15 Sekunden
Taster 3 Start der Anlage für 60 Sekunden
Taster 4 Start der Anlage für 600 Sekunden

Die Bedienung der Anlage mit nur einem Taster hat sich als unpassend herausgestellt.

Es kommen Taster mit integrierten LED‘s zum Einsatz. Die LED’s in den Tastern sollen jeweils leuchten, wenn das „Programm“ hinter dem Taster aktiv ist. Dazu soll jeweils die LED im Stop-Taster leuchten (Taster 1).
Die LED über ein Relais leuchten zu lassen ist nun nicht mehr nötig, weil die neuen Taster keine integrierten Vorwiderstände haben und direkt über den Elegoo gesteuert werden können.

Der Elegoo UNO wurde durch ein Elegoo Mega ersetzt um für spätere Erweiterungen gerüstet zu sein. Ob dies sinnvoll oder unsinnig war sei mal dahingestellt.
Die Öffner Hochdruckschalter (HDS), Niederdruckschalter (NDS) und Wasserstop (WS) werden wohl elektrisch ausgeführt. Trotzdem wäre eine Meldung über den Elegoo über ein Leuchtsignal der LED schön, hat aber Zeit.
Die Anlage soll 365 Tage im Jahr in Bereitschaft / im Betrieb sein. Ist es sinnvoll die Anlage sich einmal am Tag zum Beispiel um 01:00 neu starten zu lassen um Laufzeitfehler auszuschließen?

Der Code stammt größtenteils von Whandall und wurde von mir nur leicht verändert.
Dieser Code funktioniert so nicht mehr.

Bitte helft mir den Eingabe-Teil zu überarbeiten.
Außerdem muss die LED-Ansteuerung noch überarbeitet werden.
Den Code poste im nächsten Beitrag.

Nun folgt der Code.

Muss leider 5 min warten.

#define relaisPin_P 8    // Pumpe Relais 8
#define relaisPin_7 7    // Reinstwasser Relais 7
#define relaisPin_6 6    // Leitungswaser Relais 6
#define relaisPin_5 5    // Spühlwasser v.M. Membran Relais 5
#define relaisPin_4 4    // Spühlwasser v.M. Membran Relais 4
#define relaisPin_3 3    // Spühlwasser v.M. Membran Relais 3
#define relaisPin_2 2    // Spühlwasser n.M. Membran Relais 2

// LED Ausgänge

#define LED_rot_aus     28   
#define LED_gelb_wenig  29    
#define LED_gruen_mittel 30 
#define LED_blau_viel   31  

// Taster Eingänge

#define tasterPin1     24    // Eingang Taster abbruch / Stop
#define tasterPin2     25    // Eingang Taster wenig
#define tasterPin3     26    // Eingang Taster mittel 
#define tasterPin4     27    // Eingang Taster viel

// Sensor Eingäng

#define NDS_Pin      10    // Eingang Niederdruckschalter
#define HDS_Pin      11    // Eingang Hochdruckschalter
#define wasser_stop  12    // Eingang Wasserstop 




const unsigned long zeit6H = 21600000UL;

typedef enum eLedZustand { Aus, Schnell, Normal, Langsam } ledZustand; 

ledZustand lSignal = Aus;
unsigned long ledInterval = 0;
unsigned long lastLedAction;

const byte relayPins[] = { relaisPin_2, relaisPin_3, relaisPin_4, relaisPin_5, relaisPin_6, relaisPin_7, relaisPin_P };

unsigned long currentMillis;

// Tastenerkennung /* Diese Tastenerkennung kann mit der Hardwareveränderung wesentlich vereinfacht werden. Oder?*/
enum {NONE, FIRSTDOWN, FIRSTUP, SHORTCLICK, DOUBLECLICK, LONGCLICK};
#define INPUTMODE INPUT_PULLUP
#define PRELLZEIT 5             // Millisekunden
#define SHORTCLICKTIME 250      // Längste Zeit für einen SHORTCLICK
#define DOUBLECLICKTIME 400     // Längste Zeit für den zweiten Klick beim DOUBLECLICK
#define LONGCLICKTIME 800       // Mindestzeit für einen LONGGLICK
byte buttonPins[] = {tasterPin1, tasterPin2, tasterPin3, tasterPin4};
#define NUMBUTTONS sizeof(buttonPins)
byte buttonState[NUMBUTTONS];  // HIGH/LOW
byte buttonResult[NUMBUTTONS]; // NONE/SHORTCLICK/LONGCLICK

enum Zustaende {
  unbeschaeftigt,
  spuelStart,   spuelPhase1,  spuelPause,  spuelPhase2,
  shortStart,   shortPhase1,  shortPause,  shortPhase2,
  doubleStart,  doublePhase1, doublePause, doublePhase2,
  longStart,    longPhase1,   longPause,   longPhase2,
};

byte aktuellerZustand = unbeschaeftigt;
unsigned long lastTransition;
unsigned long letzteSpuelung;
void setup() {
  Serial.begin(250000);
  Serial.println("Button test");
  for (byte i = 0; i < NUMBUTTONS; i++) {
    pinMode(buttonPins[i], INPUTMODE);
  }
  for (byte i = 0; i < sizeof(relayPins); i++) {
    digitalWrite(relayPins[i], HIGH);
    pinMode(relayPins[i], OUTPUT);
  }
}

void loop() {
  currentMillis = millis();
    if (aktuellerZustand == unbeschaeftigt) {
    if (eingabe()) {
      ausgabe();
    } else if (currentMillis - letzteSpuelung >= zeit6H) {
      letzteSpuelung = currentMillis;
      aktuellerZustand = spuelStart;
    }
  } else {
    switch (aktuellerZustand) {
      case spuelStart:
        setupAndGoToState(0b0001111, true, spuelPhase1, PSTR("Test")); // mit 8 0b0101111
        break;
      case spuelPhase1:
        switchAndNewStateAfter(0b0001111, false, spuelPause, 20000);  // mit 8 0b0101111
        break;
      case spuelPause:
        switchAndNewStateAfter(0b0001001, true, spuelPhase2, 10);  // mit 8 0b0101001
        break;
      case spuelPhase2:
        switchAndNewStateAfter(0b0001001, false, unbeschaeftigt, 15000);  // mit 8 0b0101001
        break;

      case shortStart:
        setupAndGoToState(0b0101001, true, shortPhase1, PSTR("300 ml"));
        break;
      case shortPhase1:
        switchAndNewStateAfter(0b0101001, false, shortPause, 15000);
        break;
      case shortPause:
        switchAndNewStateAfter(0b0111000, true, shortPhase2, 500);
        break;
      case shortPhase2:
        switchAndNewStateAfter(0b0111000, false, unbeschaeftigt, 20000);
        break;

      case doubleStart:
        setupAndGoToState(0b0101001, true, doublePhase1, PSTR("1000 ml"));
        break;
      case doublePhase1:
        switchAndNewStateAfter(0b0101001, false, doublePause, 15000);
        break;
      case doublePause:
        switchAndNewStateAfter(0b0111000, true, shortPhase2, 500);
        break;
      case doublePhase2:
        switchAndNewStateAfter(0b0111000, false, unbeschaeftigt, 60000UL);
        break;

      case longStart:
        setupAndGoToState(0b0101001, true, longPhase1, PSTR("8000 ml"));
        break;
      case longPhase1:
        switchAndNewStateAfter(0b0101001, false, longPause, 15000);
        break;
      case longPause:
        switchAndNewStateAfter(0b0111000, true, longPhase2, 500);
        break;
      case longPhase2:
        switchAndNewStateAfter(0b0111000, false, unbeschaeftigt, 480000UL);
        break;
    }
  }
  if (lSignal != Aus && currentMillis - lastLedAction >= ledInterval) {
    lastLedAction = currentMillis;
    digitalWrite(relaisPin_2, !digitalRead(relaisPin_2));
  }
  benchmark();
}

void setLedState(ledZustand lz) {
  if (lz == Aus) {
    ledInterval = 0;
    digitalWrite(relaisPin_2, HIGH);
  } else if (lz != lSignal) {
    digitalWrite(relaisPin_2, LOW);
    ledInterval = lz * 250;
    lastLedAction = currentMillis;
  }
  lSignal = lz;
}

void switchRelaisSet(byte mask, bool toWhat) {
  for (byte idx = 1; mask; idx++, mask >>= 1) {
    if (mask & 1) {
      digitalWrite(relayPins[idx], toWhat ? LOW : HIGH);
    }
  }
}

void setupAndGoToState(byte mask, bool toWhat, byte newState, char* txt) {
  switchRelaisSet(mask, toWhat);
  lastTransition = currentMillis;
  aktuellerZustand = newState;
  Serial.print(F("  "));
  Serial.print((__FlashStringHelper*)txt);
  Serial.write(' ');
  Serial.println(millis());
}

void switchAndNewStateAfter(byte mask, bool toWhat, byte newState, unsigned long duration) {
  if (currentMillis - lastTransition >= duration) {
    switchRelaisSet(mask, toWhat);
    Serial.print(F("  "));
    Serial.print(aktuellerZustand);
    Serial.print(F(" -> "));
    Serial.print(newState);
    Serial.print(F(" nach "));
    Serial.print(currentMillis - lastTransition);
    Serial.println();
    lastTransition = currentMillis;
    aktuellerZustand = newState;
  }
}

boolean eingabe() 
// Rückgabewert false ==> Prellzeit läuft, Taster wurden nicht abgefragt
// Rückgabewert true ==> Taster wurden abgefragt und Status gesetzt
{
  static unsigned long lastRunTime;
  static unsigned long buttonDownTime[NUMBUTTONS];
  unsigned long now = millis();
  if (now - lastRunTime < PRELLZEIT) return false; // Prellzeit läuft noch
  lastRunTime = now;
  for (byte i = 0; i < NUMBUTTONS; i++)
  {
    byte curState = digitalRead(buttonPins[i]);
    if (INPUTMODE == INPUT_PULLUP) curState = !curState; // Vertauschte Logik bei INPPUT_PULLUP
    if (buttonResult[i] >= SHORTCLICK) buttonResult[i] = NONE; // Letztes buttonResult löschen
    if (curState != buttonState[i]) // Flankenwechsel am Button festgestellt
    {
      if (curState)   // Taster wird gedrückt, Zeit merken
      {
        if (buttonResult[i] == FIRSTUP && now - buttonDownTime[i] < DOUBLECLICKTIME)
          buttonResult[i] = DOUBLECLICK;
        else
        {
          buttonDownTime[i] = now;
          buttonResult[i] = FIRSTDOWN;
        }
      }
      else  // Taster wird losgelassen
      {
        if (buttonResult[i] == FIRSTDOWN) buttonResult[i] = FIRSTUP;
        if (now - buttonDownTime[i] >= LONGCLICKTIME) buttonResult[i] = LONGCLICK;
      }
    }
    else // kein Flankenwechsel, Up/Down Status ist unverändert
    {
      if (buttonResult[i] == FIRSTUP && now - buttonDownTime[i] > DOUBLECLICKTIME)
        buttonResult[i] = SHORTCLICK;
    }
    buttonState[i] = curState;
  } // for
  return true;
}


void ausgabe()
// Klickstatus von geklickten Buttons mit Zeitstempel ausgeben
{
  for (byte i = 0; i < NUMBUTTONS; i++)
  {
    if (buttonResult[i] >= SHORTCLICK) {
      Serial.print(millis() / 1000.0, 3); // Zeitstempel mit Millisekundenauflösung
      Serial.print("\tPin-");
      Serial.println(buttonPins[i]);
      if (buttonResult[i] == SHORTCLICK) {
        aktuellerZustand = shortStart;
      } else if (buttonResult[i] == DOUBLECLICK) {
        aktuellerZustand = doubleStart;
      } else if (buttonResult[i] == LONGCLICK) {
        aktuellerZustand = longStart;
      }
    }
  }
}

void benchmark() {
  static unsigned long counterStartTime;
  static unsigned long counter;
  if (++counter >= 1000000L) {
    Serial.print("Average Time per loop(): ");
    Serial.print((micros() - counterStartTime) / 1000000.0);
    Serial.println(" microseconds");
    counter = 0;
    counterStartTime = micros();
  }
}