Relais schalten, wenn andere Relais Pins aktiv sind + weiteres Problem

Hallo zusammen!

Ich bin derzeit an einem kleinen Projekt. Übersichthalber erkläre ich kurz die Funktion und die verwendeten Bauteile. Die aktuellen Fortschritt werde ich hier im ersten Post farblich markieren und aktualisieren, damit jeder sieht wo es noch hapert. Im Anhang ist zusätzlich eine kleine Excel-Skizze. Vielleicht wird es hier sehr viel deutlicher :slight_smile:

Meine Idee ist / war es, jede Abschnitt separat zu betrachten:

  1. Sensor 1 erkennt → Relais 1 wird für Laufzeit 1 betätigt
  2. Sensor 2 erkennt → Relais 2 wird für Laufzeit 2 betätigt
  3. Während Relais 1 / 2 oder beide zusammen aktiv → Relais 3 betätigen
  4. Nach der Laufzeit von Relais 1 / 2 oder beiden → Relais 4 kurz betätigen

Das Projekt besteht aus:

1x Pumpe
3x 2-Wege-Ventile
Arduino UNO
4er Relais Board (LOW-Aktiv)
2x Sensor

Einfachheitshalber breche ich alles auf das Relaisboard runter, weil dies im Prinzip für die Gesamtfunktion ausschlaggebend ist.

→ Also Arduino, Sensoren und Relaisboard.

Pin-Belegung:
2 = Sensor 1
3 = Sensor 2
4 = Relais 1
5 = Relais 2
6 = Relais 3
7 = Relais 4

Die Relais sollen zu bestimmten Ereignissen geschaltet werden.

Relais 1 und 2 lösen jeweils mit einem der beiden Sensoren aus. (Bsp.: Wenn Sensor 1 aktiv, dann Relais 1 aktiv für eine bestimmte Laufzeit). → Funktioniert so wie es soll, auch schön unabhängig voneinander

Knackpunkt1 : Relais 3 soll immer dann betätigt werden, WÄHREND Relais 1 oder 2 oder beide zusammen aktiv sind.
Das Problem hier ist, dass ich zwar das Relais 3 mit dem Sensor geschaltet bekomme, allerdings nur für die Laufzeit des ersten aktiven Relais. Danach geht das Relais 3 aus, obwohl das andere durch den zweiten Sensor geschaltete Relais noch aktiv ist.

Knackpunkt 2 : Relais 4 soll für kurze Zeit betätigt werden, NACHDEM

  • die Laufzeit von Ventil 1 vorbei ist
  • oder die Laufzeit von Ventil 2 vorbei ist
  • oder WENN während der Laufzeit von einem der beiden Relais das andere Relais geschaltet wird.

Also:
Sensor 1 aktiv → Relais 1 aktiv → Relais 1 betätigt für bestimmte Laufzeit → Relais 4 schaltet am Ende der Laufzeit für eine bestimmte Zeit.

Sensor 2 aktiv → Relais 2 aktiv → Relais 2 betätigt für bestimmte Laufzeit → Relais 4 schaltet am Ende der Laufzeit für eine bestimmte Zeit

Sensor 1 aktiv → Relais 1 aktiv → Relais 1 betätigt für bestimmte Laufzeit → Während der Laufzeit von Relais 1 wird Relais 2 über Sensor 2 betätigt → Relais 4 schaltet erst dann für eine bestimmte Zeit, nachdem die Laufzeit von beiden Relais beendet ist

Ich hoffe, dass ich mich soweit für jeden verständlich und nachvollziehbar ausgedrückt habe. ansonsten fragt mich bitte einfach :slight_smile:

Hier der Code den ich soweit habe:

#include <CooperativeTask.h>

/*-------------------------Relais 1----------------------------*/
class Relais1: public Task
{
  protected:
    const byte sensor1Pin;
    const byte relais1Pin;
    const unsigned long laufzeit1;
   
  public:

    Relais1(const byte sensor1Pin, const byte relais1Pin, const unsigned long laufzeit1):
      sensor1Pin(sensor1Pin),
      relais1Pin(relais1Pin),
      laufzeit1(laufzeit1)
    {
    }

    virtual void init() override
    {
      pinMode(sensor1Pin, INPUT_PULLUP);
      pinMode(relais1Pin, OUTPUT);
      digitalWrite(relais1Pin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
        taskWaitFor(not digitalRead(sensor1Pin));
        digitalWrite(relais1Pin, LOW);
        taskPause(laufzeit1);
        digitalWrite(relais1Pin, HIGH);
      }
    }
};

Relais1 aktiv1[] { 
  // {sensor1Pin,relais1Pin,laufzeit1}
     {2,4,1000}
};

/*-------------------------Relais 2----------------------------*/
class Relais2: public Task
{
  protected:
    const byte sensor2Pin;
    const byte relais2Pin;
    const unsigned long laufzeit2;

  public:
    Relais2(const byte sensor2Pin, const byte relais2Pin, const unsigned long laufzeit2):
      sensor2Pin(sensor2Pin),
      relais2Pin(relais2Pin),
      laufzeit2(laufzeit2)
    {
    }

    virtual void init() override
    {
      pinMode(sensor2Pin, INPUT_PULLUP);
      pinMode(relais2Pin, OUTPUT);
      digitalWrite(relais2Pin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
        taskWaitFor(not digitalRead(sensor2Pin));
        digitalWrite(relais2Pin, LOW);
        taskPause(laufzeit2);
        digitalWrite(relais2Pin, HIGH);
      }
    }
};

Relais2 aktiv2[] { 
  // {sensor1Pin,relais1Pin,laufzeit1}
     {3,5,1000}
};

/*------------------------- Relais 3 ----------------------------*/
class Relais3: public Task
{
  protected:
    const byte relais3Pin;

  public:
    Relais3(const byte relais3Pin):
      relais3Pin(relais3Pin)
    {
    }

    virtual void init() override
    {
      pinMode(relais3Pin, OUTPUT);
      digitalWrite(relais3Pin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
        taskWaitFor((not digitalRead(4))||(not digitalRead(5)))
        if ((not digitalRead(4))||(not digitalRead(5)))
        {
          digitalWrite(relais3Pin, LOW);
        }
        else 
        {
          digitalWrite(relais3Pin, HIGH);
        }
      }
    }
};

Relais3 aktiv3[] { 
  // {relais3Pin}
     {6}
};

/*------------------------- Relais 4 ----------------------------*/
class Relais4: public Task
{
  protected:
    const byte relais4Pin;

  public:
    Relais4(const byte relais4Pin):
      relais4Pin(relais4Pin)
    {
    }

    virtual void init() override
    {
      pinMode(relais4Pin, OUTPUT);
      digitalWrite(relais4Pin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
          /* Noch keine Lösung 
          - schalte, wenn die Laufzeit von Relais 1 vorbei
          - schalte, wenn die Laufzeit von Relais 2 vorbei
          - oder schalte erst dann, wenn die Laufzeit von Relais 1 und Relais 2 vorbei ist. Solange noch eines der beiden Relais aktiv ist, darf nicht geschaltet werden */
      }
    }
};

Relais4 aktiv4[] { 
  // {relais3Pin}
     {7}
};

/*------------------------------------------------*/
void setup()
{
  Scheduler::instance().init();
}

void loop()
{
  Scheduler::instance().run();
}

erstens bekommst ein Karma für die Erklärung. So kann man sich was drunter vorstellen.

Zweitens: in der Theorie: du musst ja nur das Relais 3 über sowohl mit Sensor 1 wie auch mit Sensor 2 antriggern lassen. Und Relais4 reagiert einfach auf "alle 3 sind nun aus" - implizit ist es "Relais 3" ist ausgegangen

In der Sache kann ich dir nicht helfen, weil ich diese CooperativeTask nicht kennen - aber der Ersteller wird sich schon melden.

noiasca: In der Sache kann ich dir nicht helfen, weil ich diese CooperativeTask nicht kennen - aber der Ersteller wird sich schon melden.

Ich wäre auch dankbar für jegliche Lösungsalternativen. Bin da offen für alles. Die andere (mir bekannte) Alternative wäre mit millis() zu arbeiten.

Ich kenne mich mit den Klassen (oder generell was die Programmierung angeht) nicht sonderlich gut aus :D

noiasca: erstens bekommst ein Karma für die Erklärung. So kann man sich was drunter vorstellen.

Danke!

noiasca: Zweitens: in der Theorie: du musst ja nur das Relais 3 über sowohl mit Sensor 1 wie auch mit Sensor 2 antriggern lassen. Und Relais4 reagiert einfach auf "alle 3 sind nun aus" - implizit ist es "Relais 3" ist ausgegangen

Darüber habe ich auch schon nachgedacht. Da das Relais 3 ja immer bis Laufzeitende dabei ist.

noiasca: ... weil ich diese CooperativeTask nicht kennen ...

Wenn Du es ändern möchtest: CooperativeTask

noiasca: OT: falsch formuliert: kennen ja. Können nein. Ändern wollen auch nein. Bin eher der "millis im Sketch" Typ.

Ich auch ::)

noiasca: Bin eher der "millis im Sketch" Typ.

Ich auch, aber in Form des MoToTimers aus meinen MobaTools ;) :D. Für jedes Relais, das zeitgesteuert abgeschaltet werden soll legst Du einen Timer an. Immer wenn Du das Relais einschaltest, startest Du auch den Timer('setTime'). Das kann auch während der Laufzeit sein ( der Timer ist retriggerbar ). Läuft der Timer ab ('expired') schaltest Du das Relais wieder ab.

Für so eine Aufgabe eine Task Scheduler herzunehmen finde ich mit Kanonen auf Spatzen geschossen. Aber wenn's funktioniert ... auch OK 8)

Bei mehr Öffentlichkeit ginge dies:

 digitalWrite(aktiv3[0].relais3Pin, digitalRead(aktiv1[0].relais1Pin) || digitalRead(aktiv2[0].relais2Pin));

Aber besser wären Methoden, die den Wert des Ausgangs zurückgeben.

EDIT: Wegen aktiv LOW ODER in UND geändert.

aber der Ersteller wird sich schon melden.

Anwesend!

Also... Eigentlich wurden die Cooperativen Task von mir erfunden, um die Multitasking Makros OOP kompatible zu machen. Nicht nur um millis() in den Hintergrund zu drängen. Aber auch dafür. Und um das durchnummerieren von Variablen/Funktionen und Codedupplizierungen usw vermeiden zu können. Zu dem Zweck kann man die Tasks auch in Arrays zwängen. Kann, nicht muss.

Viele von diesen Zielsetzungen sind hier konterkariert.

Euren Hang weiter mit millis() zu arbeiten kann ich verstehen, denn warum sollte man etwas aufgeben, wo man viel Arbeit investiert hat um es zu kapieren. Und was den Quellcode so schön unübersichtlich macht. Dass bei den Tasks, das gleiche Verfahren(millis()) verwendet wird, muss bei dem Beharrungsvermögen ja nicht stören. (auch wenn ich es für irrational halte)

Hier gilt es wohl die verschiedenen Task miteinander kommunizieren zu lassen. Eine Abstaktionshürde, wie mir scheint.

Auch ist mir nicht ganz klar, was passieren soll, wenn Relais4 gerade aktiv ist, und einer der beiden Sensoren zuschlägt. Da fehlt meiner Ansicht nach noch ein Stück in der Logik.

Moin, für Knackpunkt1 hätte ich eine Idee (sofern ich den Code richtig verstanden habe). In Relais3 Zeile 107/108:

        taskWaitFor((not digitalRead(4))||(not digitalRead(5)))
        if ((not digitalRead(4))||(not digitalRead(5)))
  1. Während Relais 1 / 2 oder beide zusammen aktiv -> Relais 3 betätigen

Sollte dann nicht darauf gewartet werden, dass beide (Relais1 an Pin 4 und Relais2 an Pin 5) nicht mehr aktiv sind?

        taskWaitFor((not digitalRead(4))&&(not digitalRead(5)))
        if ((not digitalRead(4))&&(not digitalRead(5)))

Vielleicht wäre eine positive Logik einfacher zu erfassen:

        taskWaitFor((digitalRead(4))||(digitalRead(5)))
        if ((digitalRead(4))||(digitalRead(5)))
        {
          digitalWrite(relais3Pin, HIGH);
        }
        else
        {
          digitalWrite(relais3Pin, LOW);
        }

Nebenbei bemerkt: Ich bin noch nicht sicher, ob es das "if" mit derselben Bedingung direkt nach dem taskWaitFor() wirklich braucht...

Gruß Walter

Erstmal danke für die ersten Rückmeldungen.

wno158: Sollte dann nicht darauf gewartet werden, dass beide (Relais1 an Pin 4 und Relais2 an Pin 5) nicht mehr aktiv sind?

        taskWaitFor((not digitalRead(4))&&(not digitalRead(5)))
        if ((not digitalRead(4))&&(not digitalRead(5)))

Vielleicht wäre eine positive Logik einfacher zu erfassen:

        taskWaitFor((digitalRead(4))||(digitalRead(5)))
        if ((digitalRead(4))||(digitalRead(5)))
        {
          digitalWrite(relais3Pin, HIGH);
        }
        else
        {
          digitalWrite(relais3Pin, LOW);
        }

Gruß Walter

Das Relais 3 soll ja in abnhängigkeit von Relais 1 und Relais 2 geschaltet werden. Entweder ist eines der beiden Relais geschaltet oder aber eben beide gleichzeitig überschneidend. Wenn ich hier mit UND arbeite, wird Relais 3 ja nur geschaltet, wenn BEIDE Relais (1 und 2) aktiv sind. Oder irre ich mich an dieser Stelle?

Zur positven Logik: Klar, kann man machen. Bringt mich im Endeffekt aber nicht näher an meine Lösung :)

noiasca: Die Abstraktionshürde ... ja ist für mich auch anfänglich auch knifflig, aber ich habe vermutlich einen Ansatz. Will aber den TO nicht verwirren und die Rest-Mittagspause reicht nicht mehr. Zeig ihm bitte deinen Weg.

Kurz und knapp. Überfordert mich bitte nicht :D Die Lösung sollte simpel und für mich weiterhin nachvollziehbar bleiben :)

agmue: Bei mehr Öffentlichkeit ginge dies:

  digitalWrite(aktiv3[0].relais3Pin, digitalRead(aktiv1[0].relais1Pin) || digitalRead(aktiv2[0].relais2Pin));

Aber besser wären Methoden, die den Wert des Ausgangs zurückgeben.

Die Syntax ist mir leider komplett fremd und neu. Wird hier der Pin über die Klassen ausgelesen / geschrieben?

Danke! :)

Hallo,

hab ich das jetzt übersehen ? Was passiert wenn einer/beide Sensoren aktiv bleiben ? Werden die Zeiten jeweils von den Flanken der Sensoren gestartet.

was wenn ein Sensor erneut betätigt wird wenn eine zugehörige Zeit noch läuft. Startet die Zeit dann neu oder läuft sie weiter.

Heinz

(deleted)

@kahmui: Vielleicht nochmals die Bedingungen genau anschauen. Bei dir ist not digitalRead(Relais 1 bzw.2) erfüllt, wenn das Relais aus ist. Es sollen aber beide aus sein um #3 abzuschalten. Deshalb &&. Bei || reicht es, wenn eins der beiden aus ist - das ist das, was du auch beobachtet hast.

kahmui:
Die Syntax ist mir leider komplett fremd und neu. Wird hier der Pin über die Klassen ausgelesen / geschrieben?

Nur weil Du nachgefragt hast, hier meine Variante:

#include <CooperativeTask.h>

/*-------------------------Relais 1----------------------------*/
class Relais1: public Task
{
  public:
    const byte sensor1Pin;
    const byte relais1Pin;
    const unsigned long laufzeit1;

    Relais1(const byte sensor1Pin, const byte relais1Pin, const unsigned long laufzeit1):
      sensor1Pin(sensor1Pin),
      relais1Pin(relais1Pin),
      laufzeit1(laufzeit1)
    {
    }

    virtual void init() override
    {
      pinMode(sensor1Pin, INPUT_PULLUP);
      pinMode(relais1Pin, OUTPUT);
      digitalWrite(relais1Pin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
        taskWaitFor(not digitalRead(sensor1Pin));
        digitalWrite(relais1Pin, LOW);
        taskPause(laufzeit1);
        digitalWrite(relais1Pin, HIGH);
      }
    }
};

Relais1 aktiv1 
  // {sensor1Pin,relais1Pin,laufzeit1}
  {2, 4, 1000}
;

/*-------------------------Relais 2----------------------------*/
class Relais2: public Task
{
  public:
    const byte sensor2Pin;
    const byte relais2Pin;
    const unsigned long laufzeit2;

    Relais2(const byte sensor2Pin, const byte relais2Pin, const unsigned long laufzeit2):
      sensor2Pin(sensor2Pin),
      relais2Pin(relais2Pin),
      laufzeit2(laufzeit2)
    {
    }

    virtual void init() override
    {
      pinMode(sensor2Pin, INPUT_PULLUP);
      pinMode(relais2Pin, OUTPUT);
      digitalWrite(relais2Pin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
        taskWaitFor(not digitalRead(sensor2Pin));
        digitalWrite(relais2Pin, LOW);
        taskPause(laufzeit2);
        digitalWrite(relais2Pin, HIGH);
      }
    }
};

Relais2 aktiv2 
  // {sensor1Pin,relais1Pin,laufzeit1}
  {3, 5, 1000}
;

const byte sensor3Pin = 6;
/*------------------------------------------------*/
void setup()
{
  Scheduler::instance().init();
  pinMode(sensor3Pin, OUTPUT);
}

void loop()
{
  Scheduler::instance().run();
  digitalWrite(sensor3Pin, digitalRead(aktiv1.relais1Pin) && digitalRead(aktiv2.relais2Pin));
}

Diesen Mischmasch solltest Du aber besser nicht übernehmen :smiling_imp:

Eine verbesserte Variante:

#include <CooperativeTask.h>

class Relais: public Task
{
  public:
    const byte sensorPin;
    const byte relaisPin;
    const unsigned long laufzeit;

    Relais(const byte sensorPin, const byte relaisPin, const unsigned long laufzeit):
      sensorPin(sensorPin),
      relaisPin(relaisPin),
      laufzeit(laufzeit)
    {}

    virtual void init() override
    {
      pinMode(sensorPin, INPUT_PULLUP);
      pinMode(relaisPin, OUTPUT);
      digitalWrite(relaisPin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
        taskWaitFor(not digitalRead(sensorPin));
        digitalWrite(relaisPin, LOW);
        taskPause(laufzeit);
        digitalWrite(relaisPin, HIGH);
      }
    }

    bool status()
    {
      return digitalRead(relaisPin);
    }
};

Relais aktiv[]
{ // {sensorPin,relaisPin,laufzeit1}
  {2, 4, 1000}, // Relais 1
  {3, 5, 1000}  // Relais 2
};

enum {Relais1, Relais2};
const byte sensor3Pin = 6;

void setup()
{
  Scheduler::instance().init();
  pinMode(sensor3Pin, OUTPUT);
}

void loop()
{
  Scheduler::instance().run();
  digitalWrite(sensor3Pin, aktiv[Relais1].status() && aktiv[Relais2].status());
}

OK, combie war schneller 8)

So jetzt mal kurz die Zeit gefunden um den Code etwas aufzuräumen…
Relais3 habe ich da mal abgeändert(die umhüllende Klasse entfernt) um die Kommunikation flach zu halten.

#include <CooperativeTask.h>

class Relais: public Task
{
  protected:
    const byte sensorPin;
    const byte relaisPin;
    const unsigned long laufzeit;
  
  public:

    Relais(const byte sensorPin, const byte relaisPin, const unsigned long laufzeit):
      sensorPin(sensorPin),
      relaisPin(relaisPin),
      laufzeit(laufzeit)
    {
    }

    virtual void init() override
    {
      pinMode(sensorPin, INPUT_PULLUP);
      digitalWrite(relaisPin, HIGH);
      pinMode(relaisPin, OUTPUT);
    }

    virtual void run() override
    {
      TaskBlock
      {
        taskWaitFor(not digitalRead(sensorPin));
        digitalWrite(relaisPin, LOW);
        taskPause(laufzeit);
        digitalWrite(relaisPin, HIGH);
      }
    }
    
    bool aktiv()
    {
      return not digitalRead(relaisPin);  
    }
};


Relais relais[] 
{
  // {sensorPin,relaisPin,laufzeit}
     {2,4,1000},
     {3,5,1000}
};

const byte relais3Pin {6};



/*------------------------- Relais 4 ----------------------------*/
class Relais4: public Task
{
  protected:
    const byte relais4Pin;

  public:
    Relais4(const byte relais4Pin):
      relais4Pin(relais4Pin)
    {
    }

    virtual void init() override
    {
      pinMode(relais4Pin, OUTPUT);
      digitalWrite(relais4Pin, HIGH);
    }

    virtual void run() override
    {
      TaskBlock
      {
          /* Noch keine Lösung
          - schalte, wenn die Laufzeit von Relais 1 vorbei
          - schalte, wenn die Laufzeit von Relais 2 vorbei
          - oder schalte erst dann, wenn die Laufzeit von Relais 1 und Relais 2 vorbei ist. Solange noch eines der beiden Relais aktiv ist, darf nicht geschaltet werden */
      }
    }
};


Relais4 aktiv4[] {
  // {relais3Pin}
     {7}
};

/*------------------------------------------------*/

void setup()
{
  digitalWrite(relais3Pin,HIGH);
  pinMode(relais3Pin,OUTPUT);
  Scheduler::instance().init();
}

void loop()
{
  Scheduler::instance().run();

  bool aktiv {false};
  for(Relais &r:relais) aktiv = aktiv || r.aktiv();
  digitalWrite(relais3Pin,not aktiv);
  
}

Zu Relais4 kann ich noch nichts sagen da meine vorherige Frage nicht beantwortet wurde.

Auch ist mir nicht ganz klar, was passieren soll, wenn Relais4 gerade aktiv ist, und einer der beiden Sensoren zuschlägt. Da fehlt meiner Ansicht nach noch ein Stück in der Logik.

Soooo und nochmals vielen Dank für die tollen Informationen und Hilfestellungen! :slight_smile:

wno158:
Vielleicht nochmals die Bedingungen genau anschauen.
Bei dir ist not digitalRead(Relais 1 bzw.2) erfüllt, wenn das Relais aus ist. Es sollen aber beide aus sein um #3 abzuschalten.
Deshalb &&.
Bei || reicht es, wenn eins der beiden aus ist - das ist das, was du auch beobachtet hast.

Macht Sinn und ist auch vollkommen richtig. Mein Fehler. Danke dir!
Also nicht negiert und angepasst.

taskWaitFor((digitalRead(4)) && (digitalRead(5)))
        if ((digitalRead(4)) && (digitalRead(5)))
        {
          digitalWrite(relais3Pin, HIGH);
        }
        else  
        {
          digitalWrite(relais3Pin, LOW);
        }

Danke @agmue für die Erläuterung. Jetzt leuchtet mir das ganze schon ein wenig mehr ein :slight_smile:

combie:
Zu Relais4 kann ich noch nichts sagen da meine vorherige Frage nicht beantwortet wurde.

Angenommen die Zeiten überschneiden sich erneut ist es so, dass sich dass Relais 4 immer wieder hinten angliedern soll. Siehe Anhang erweitert :slight_smile:

Wenn ich so recht darüber nachdenke, kann ich die Betätigung von Relais 4 an das Ende der Laufzeit von Relais 3 koppeln… Ohweia! Manchmal sollte man doch vorab eine kleine Skizze machen um somit rein grafisch schon Lösungen zu erkennen! :smiley:

kahmui: Danke @agmue für die Erläuterung. Jetzt leuchtet mir das ganze schon ein wenig mehr ein :)

Bitte gerne, immer schön, wenn es leuchtet ;)

Ach Combie. Um nochmal auf die Frage zurückzukommen was passieren soll wenn Relais 4 aktiv ist und einer der Sensoren während dieser Zeitspanne was erkennt: generell unrelevant, da Relais 4 für sage und schreibe 150 ms nach Relais 3 aktiv ist. (Ja das Relais ist so schnell :wink: )
Ansonsten wäre die logische Vorgehensweise, den Betätigungszeitraum von Relais 4 abzubrechen und Relais 1 / 2 wie gehabt zu schalten.

Siehe Anhang ^^

meine Variante basiert auf folgender Annahme:


(sorry für meine Klaue, aber das ist halt so)

Dann die Überlegung, dass die 4 Relais retriggerbare Nachlaufrelais sind, die sich selber um das Zeitmanagement kümmern. Ist halt aus meinem Muster von der Homepage.

Ich will damit nur zeigen, dass es auch “zu Fuß” gar nicht so lang sein muss (100 Zeilen), und im Source auch nur zwei Millis-Zugriffe stehen. Für mich ist das noch übersichtlich:

/*
  Relais in Abhängigkeit starten
  by noiasca
  https://forum.arduino.cc/index.php?topic=707341
  Ausgangsbasis: https://werner.rothschopf.net/202003_arduino_retriggerbares_nachlaufrelais_oop.htm
*/

class RetriggerOffDelay                      // a class for a retriggerable off delay
{
  private:
    const byte outputPin;                    // GPIO für die LED die nachläuft
    const byte interval;                     // Nachlaufzeit in Sekunden
    uint32_t previousMillis = 0;             // Zeitstempel, wann die LED das letzte mal eingeschaltet wurde
    using CallBack = void (*)(void);
    CallBack funcPtr;

  public:
    RetriggerOffDelay(byte outputPin, byte interval) :  // ein Constructor für unsere Klasse mit Initialisierungsliste
      outputPin(outputPin),
      interval(interval),
      funcPtr(nullptr)
    {}

    void setCallBack(const CallBack funcPtr)
    {
      (*this).funcPtr = funcPtr;
    }

    void begin() {                            // a method which should be called in setup()
      digitalWrite(outputPin, LOW);
      pinMode(outputPin, OUTPUT);
    }

    void trigger()                           // Relais einschalten
    {
      previousMillis = millis();
      digitalWrite(outputPin, HIGH);
      Serial.print("on  "); Serial.println(outputPin);
    }

    void tick() {                             // a method which should be called permanently in loop()
      if (millis() - previousMillis > interval * 1000UL && digitalRead(outputPin) == HIGH)  // wenn Zeit abgelaufen und die LED leuchtet
      {
        digitalWrite(outputPin, LOW);
        Serial.print("off "); Serial.println(outputPin);
        if (funcPtr) funcPtr();
      }
    }
};

RetriggerOffDelay relais[] {
  { 2, 7},   // first objekt,  index 0: buttonPin, Zeit
  { 3, 8},   // second objekt, index 1:
  { 4, 8},   // third objekt,  index 2:
  { 5, 1},   // 4th objekt,    index 3:
};

struct Eingabe                // ein Button startet zwei Relais
{
  const byte buttonPin;
  const byte relaisA;
  const byte relaisB;
};

Eingabe eingabe[] {          // du willst zwei EingabeButtons
  {A4, 0, 2},  // Pin, Relais A to trigger, Relais B to trigger
  {A5, 1, 2}
};

void readButton() {
  for (auto & i : eingabe)
  {
    if (digitalRead(i.buttonPin) == LOW) {
      Serial.print("pressed "); Serial.println(i.buttonPin);
      relais[i.relaisA].trigger();
      relais[i.relaisB].trigger();
    }
  }
}

void triggerD()
{
  relais[3].trigger();
}

void setup() {
  Serial.begin(115200);
  for (auto & i : relais)  // range based for iterates through array of objects and "i" gets a reference onto the object
    i.begin();
  for (auto & i : eingabe)
    pinMode(i.buttonPin, INPUT_PULLUP);

  relais[2].setCallBack(triggerD); // wenn das Relais abfällt, soll es eine Callback Funktion aufrufen
}

void loop() {
  readButton();
  for (auto & i : relais)
    i.tick();
}

Das “4te Relais” einschalten ist eine optionale Callback Funktion. Sozusagen ein Event “onSwitchOff”. Klappt zwar, aber hat mich nun länger genervt.

Kritiken: Ginge sicher schöner. Ursprünglich wollte ich dem Objekt optional eine Referenz auf das nächste Relais geben. Die Eingabe-Buttons könnte man auch auch als Objekte betrachten, aber weils schon leuchtet und blinkt lass ich das mal.

20201002_124548m.jpg

edit. Hab gesehen, dass ich noch einen Logikfehler drinnen hatte. Also wenn ich es nun richtig verstanden habe, ziehen die Relais immer gleich lang an. Daher habe ich die Zeit nun zum Sensor auslesen gegeben.

Eine Variante unter verzicht auf die Arrays, soll man nur machen wenn man sicher weis, dass da NIE weitere Sensoren oder Relais hinzukommen. Ist aber vermutlich besser lesbar.

/*
  Relais in Abhängigkeit starten
  by noiasca
  https://forum.arduino.cc/index.php?topic=707341
  Ausgangsbasis: https://werner.rothschopf.net/202003_arduino_retriggerbares_nachlaufrelais_oop.htm
  derived class, no arrays for objects
*/

class RetriggerOffDelay                      // a class for a retriggerable off delay
{
  protected:
    const uint8_t outputPin;                 // GPIO für die LED die nachläuft
    uint32_t previousMillis = 0;             // Zeitstempel, wann die LED das letzte mal eingeschaltet wurde
    byte interval;                           // Nachlaufzeit in Sekunden

  public:
    RetriggerOffDelay(uint8_t outputPin) :  // ein Constructor für unsere Klasse mit Initialisierungsliste
      outputPin(outputPin)
    {}

    void begin() {                            // a method which should be called in setup()
      digitalWrite(outputPin, LOW);
      pinMode(outputPin, OUTPUT);
    }

    void trigger(byte newInterval)            // Relais einschalten
    {
      previousMillis = millis();
      digitalWrite(outputPin, HIGH);
      interval = newInterval;
      Serial.print("on  "); Serial.println(outputPin);
    }

    void tick() {                             // a method which should be called permanently in loop()
      if (millis() - previousMillis > interval * 1000UL && digitalRead(outputPin) == HIGH)  // wenn Zeit abgelaufen und die LED leuchtet
      {
        digitalWrite(outputPin, LOW);
        Serial.print("off "); Serial.println(outputPin);
      }
    }
};

class RetriggerOffDelayMaster  : public RetriggerOffDelay
{
  private:
    RetriggerOffDelay &obj;

  public:
    RetriggerOffDelayMaster(byte outputPin, RetriggerOffDelay &obj) :
      RetriggerOffDelay(outputPin),
      obj(obj)
    {}

    void tick() {                             // a method which should be called permanently in loop()
      if (millis() - previousMillis > interval * 1000UL && digitalRead(outputPin) == HIGH)  // wenn Zeit abgelaufen und die LED leuchtet
      {
        digitalWrite(outputPin, LOW);
        Serial.print("off "); Serial.println(outputPin);
        obj.trigger(1);  // andere aufrufen
      }
    }
};

RetriggerOffDelay relaisA(2);
RetriggerOffDelay relaisB(3);
RetriggerOffDelay relaisD(5);
RetriggerOffDelayMaster relaisC(4, relaisD);

void readButton() {
  if (digitalRead(A4) == LOW) {
    Serial.println("pressed A4");
    relaisA.trigger(4);
    relaisC.trigger(4);
  }
  if (digitalRead(A5) == LOW) {
    Serial.println("pressed A5");
    relaisA.trigger(3);
    relaisC.trigger(3);
  }
}

void setup() {
  Serial.begin(115200);
  relaisA.begin();
  relaisB.begin();
  relaisC.begin();
  relaisD.begin();
  pinMode(A4, INPUT_PULLUP);
  pinMode(A5, INPUT_PULLUP);
}

void loop() {
  readButton();
  relaisA.tick();
  relaisB.tick();
  relaisC.tick();
  relaisD.tick();
}

oder eine Variante mit einem pointer auf das zweite Objekt, dass nach dem Ausschalten getriggert werden soll.

/*
  Relais in Abhängigkeit starten
  by noiasca
  https://forum.arduino.cc/index.php?topic=707341
  Ausgangsbasis: https://werner.rothschopf.net/202003_arduino_retriggerbares_nachlaufrelais_oop.htm
  Pointer to second relais
*/

class RetriggerOffDelay                      // a class for a retriggerable off delay
{
  protected:
    const uint8_t outputPin;                 // GPIO für die LED die nachläuft
    uint32_t previousMillis = 0;             // Zeitstempel, wann die LED das letzte mal eingeschaltet wurde
    RetriggerOffDelay *obj;                  // a pointer to the second relais (not a reference, because default will be a nullptr)
    byte interval;                           // Nachlaufzeit in Sekunden

  public:
    RetriggerOffDelay(uint8_t outputPin) :  // ein Constructor für unsere Klasse mit Initialisierungsliste
      outputPin(outputPin),
      obj(nullptr)
    {}

    void begin() {                            // a method which should be called in setup()
      digitalWrite(outputPin, LOW);
      pinMode(outputPin, OUTPUT);
    }

    void trigger(byte newInterval)                           // Relais einschalten
    {
      interval = newInterval;
      previousMillis = millis();
      digitalWrite(outputPin, HIGH);
      Serial.print("on  "); Serial.println(outputPin);
    }

    void setOnEventOff(RetriggerOffDelay *myObj)  // sets the second relay
    {
      obj = myObj;
    }

    void tick() {                             // a method which should be called permanently in loop()
      if (millis() - previousMillis > interval * 1000UL && digitalRead(outputPin) == HIGH)  // wenn Zeit abgelaufen und die LED leuchtet
      {
        digitalWrite(outputPin, LOW);
        Serial.print("off "); Serial.println(outputPin);
        if (obj) obj->trigger(1);             // if pointer to object is set, trigger the second relais
      }
    }
};

RetriggerOffDelay relais[] {2, 3, 4, 5};

struct Eingabe                   // ein Button startet zwei Relais
{
  const byte buttonPin;
  const byte interval;
  RetriggerOffDelay &one;
  RetriggerOffDelay &two;
};

Eingabe eingabe[] {               // du willst zwei Eingabe Buttons
  {A4, 4, relais[0], relais[2]},  // Pin, first relais to trigger, second relais to trigger
  {A5, 3, relais[1], relais[2]}
};

void readButton() {
  for (auto & i : eingabe)
  {
    if (digitalRead(i.buttonPin) == LOW) {
      Serial.print("pressed "); Serial.println(i.buttonPin);
      i.one.trigger(i.interval);
      i.two.trigger(i.interval);
    }
  }
}

void setup() {
  Serial.begin(115200);
  for (auto &i : relais)
    i.begin();
  for (auto &i : eingabe)
    pinMode(i.buttonPin, INPUT_PULLUP);

  RetriggerOffDelay * pointer = &relais[3];  // wtf: a pointer to a reference of a object (in an array)
  relais[2].setOnEventOff(pointer);
}

void loop() {
  readButton();
  for (auto & i : relais)
    i.tick();
}

so, jetzt langts...