"Blink Without Delay" - zwei unabhängige Zeitrelais, zwei Schaltereingänge

Seit einigen Tagen beschäftigt mich ein scheinbar einfaches Projekt. Intelligente Badezimmerentlüftung.
Nachdem ich die Hürde Blink Without Delay gemeistert hatte, stellte sich das Problem, dass der Duschlüfter, sofort nach der Inbetriebnahme der Schaltung, erst einmal 40 Minuten läuft, erst nach Ablauf dieser Zeit funktionierte die Schaltung einwandfrei. Ich habe also versucht diesen unbeabsichtigten Erststart zu vermeiden. Herausgekommen ist folgende Lösung, bei der ich das Ein und Ausschalten der Lüfter, mittels getrennter Abfragen realisiere. (Tatsächlich ist mein Badezimmerlüfter nur ein Lüfter, der durch zwei Wicklungen unterschiedlichen Geschwindigkeiten kennt. Daher war es mir wichtig, dass jeweils nur eine der beiden Relais Stufe I oder Stufe II aktiv ist)

// Bad und WC Entlüftung, zwei unabhängige Lüfterlaufzeiten, 4 bzw. 40 Minuten.
// Funktion: WC Entlüftung Gebläse langsam, 4 Minuten Laufdauer. Die Duschentlüftungsfunktion beendet eine eventuell bereits gestartete WC Lüftung vorzeitig, schaltet den Lüfter auf hohe Drehzahl, hier 40 Minuten Nachlauf. Der Duschlüfter hat Priorität vor der WC-Entlüftung.

// Variable
unsigned long WCStart = 0; // Zeitmerker für den Zeitpunkt WC-Lüfter läuft an.
unsigned long DuscheStart = 0; // Zeitmerker für den Zeitpunkt Duschlüfter läuft an.

// Zeiten
const long wcLuefterDauer = 240 * 1000UL ; //Lüfter WC Laufdauer 4 Minuten.
const long duschLuefterDauer = 2400 * 1000UL; //Lüfter Dusche Laufdauer 40 Minuten.
// const long wcLuefterDauer = 6 * 1000UL ; //Test WC 6 Sekunden.
// const long duschLuefterDauer = 12 * 1000UL; //Test Dusche 12 Sekunden.

// Ein und Ausgänge
const int WC = 2;     // Sensor im WC Deckel
const int Duschlicht = 3;      // Signal von der Duschbeleuchtung
const int Luefter =  11;      // Lüfter Relais Langsam (WC-Entlüftung)
const int LuefterII =  12;     // Lüfter Relais Schnell (Duschbetrieb)

// Festlegung der Arduino Ein und Ausgänge
void setup() {
  pinMode(Luefter, OUTPUT);
  pinMode(LuefterII, OUTPUT); 
  pinMode(Duschlicht, INPUT);
  pinMode(WC, INPUT);
}

// -----------Schleife beginnt ab hier ------------------
void loop() {

// Schalterzustandabfrage WC Reed Relais. Duschlicht, gebrauchtes 5 Volt Handy- Netzteil liefert Signal Licht an.

if ((digitalRead(WC) == HIGH) & (digitalRead(LuefterII) == LOW)) { WCStart = millis(); digitalWrite(Luefter, HIGH);} //WC Lüftung Ein. Überprüft ob nicht bereits die Duschabsaugung läuft. Setzt temporäre Zeitmarke.
if (digitalRead(Duschlicht) == HIGH) {DuscheStart = millis(); digitalWrite(LuefterII, HIGH); digitalWrite(Luefter, LOW);} //Dusche Lüftung Ein. Setzt temporäre Zeitmarke2.

// Laufzeit Ende WC und Dusche

if (millis() - WCStart >= wcLuefterDauer) { digitalWrite(Luefter, LOW);} //Laufzeitende Lüfter WC.
if (millis() - DuscheStart >= duschLuefterDauer) { digitalWrite(LuefterII, LOW);} //Laufzeitende Lüfter Dusche.

 // Loop Ende
}

Und was ist jetzt deine Frage?

Gruß

Scherheinz

(deleted)

elektronenstrudel:
... stellte sich das Problem, dass der Duschlüfter, sofort nach der Inbetriebnahme der Schaltung, erst einmal 40 Minuten läuft, erst nach Ablauf dieser Zeit funktionierte die Schaltung einwandfrei.

Das ist eine bekannte Schwäche, die bei etwas Geblinke mit kurzen Zeiten nicht auffällt.

Da millis() bei 0 anfängt und auch die Start-Variablen mit 0 initialisiert werden, kommt es zu diesem Effekt.

Abhilfe 1 (vergleichbar dem millis-Überlauf nach 49 Tagen):

unsigned long WCStart = -wcLuefterDauer; // Zeitmerker für den Zeitpunkt WC-Lüfter läuft an.
unsigned long DuscheStart = -duschLuefterDauer; // Zeitmerker für den Zeitpunkt Duschlüfter läuft an.

Abhilfe 2:

   WCStart = millis() + wcLuefterDauer; //WC Lüftung Ein. Überprüft ob nicht bereits die Duschabsaugung läuft. Setzt temporäre Zeitmarke.
...
    DuscheStart = millis() + duschLuefterDauer; //Dusche Lüftung Ein. Setzt temporäre Zeitmarke2.
...
  if (millis() >= WCStart) {
...
  if (millis() >= DuscheStart) {
...

Deine Lösung scheint aber auch zu funktionieren, oder?

Damit willkommen im Forum!

Hi

Mein Weg wäre, daß ich mir 'nur' merke, ob der Lüfter gestartet wurde.
Wenn NICHT, ist mir die vergangene Zeit völlig egal.
Erst das Starten des Lüfter (Person erkannt, Start, Startzeit setzen) lässt den Lüfter anspringen und auf das Ende der Laufdauer warten.
(Also ich schalte den Lüfter AN bei Erkennung - Das hat NICHTS mit der Zeit zu tun. Auch schalte ich den Lüfter AUS, wenn die Laufzeit vorbei ist (und der Lüfter läuft)).
Wenn hier das Start-Ereignis erneut zuschlägt, gerne auch Mal länger - aber frühestens NACH dem ersten Start.

Der WorkArround mit den Negativ-Startzeiten ist aber genau so praktikabel und wurde ebenfalls schon genutzt - vll. im Kommentar, WAS man Da treibt, dann ist's auch so ok.

MfG

Die Aufgabenstellung "schwebt" zwischen einfachem Blinken und einer Schrittkette mit switch/case. Ich finde so eine Schrittkette einfacher zu verstehen, als eine "endlose" Abfolge von Bedingungen. Mit der Schrittkette kann man dann auch elegant die beschriebene Anfangsproblematik umgehen. Das wäre dann Abhilfe 3.

Habe da mal schnell was zusammengedengelt.
Wenn ich das richtig verstanden habe....

Duschlicht macht schnellen Lüfter
WC Licht macht langsam.

Entweder schnell oder langsam.
Schnell überläd langsam.

Und das alles natürlich nach den "Blink Without Delay" Regeln.
Nix negative Zeiten.
Nix viele if() und so weiter.
Mit kleiner Einschaltverzögerung, als Entpreller, oder Störunterdrückung

#include <CombiePin.h>

#include <CombieTimer.h>
using namespace Combie::Timer;

#include <CombieTypeMangling.h>
using namespace Combie::Millis;

using WcLicht         = Combie::Pin::InputPin<4>;
using DuscheLicht     = Combie::Pin::InputPin<5>;
using LuefterSchnell  = Combie::Pin::RelaisINV<2>;
using LuefterLangsam  = Combie::Pin::RelaisINV<3>;

class ZeitRelaisErsatzstoff: protected TriggerableBool
{
  protected:
  RisingEdgeTimer  ton ;
  FallingEdgeTimer toff;

  public:
  explicit ZeitRelaisErsatzstoff(const Millis einZeit, const Millis ausZeit): ton{einZeit}, toff{ausZeit}{}
  using TriggerableBool::operator();
  using TriggerableBool::operator=;
  using TriggerableBool::operator bool;

  virtual bool doTrigger(const bool trigger) override
  {
     return state = toff = ton = trigger;
  }
};

ZeitRelaisErsatzstoff wcZeitRelaisErsatzstoff     {10_ms, 4_min};
ZeitRelaisErsatzstoff duscheZeitRelaisErsatzstoff {10_ms,40_min};

void setup()
{
  WcLicht().init();
  DuscheLicht().init();
  LuefterSchnell().init();
  LuefterLangsam().init();
}


void loop()
{
  bool langsam = wcZeitRelaisErsatzstoff     = WcLicht();
  bool schnell = duscheZeitRelaisErsatzstoff = DuscheLicht();
 
  LuefterSchnell() =  schnell;
  LuefterLangsam() =  langsam && not schnell;
}

EDIT:
Die Klasse nochmal etwas "hübscher" gemacht.


Oha...
Das Programm enthält einen schweren Fehler:
Bitte denkt euch überall, wo ein WcLicht steht, einen WcSitzSchalter hin.

CombieLib.zip (80.6 KB)