Hilfe bei einer Ausschaltverzögerung

Bei mir läd er rein. Ohne Fehler.

DATEI - VOREINSTELLUNGEN
grafik

Nicht an der Farbe stören

Nochmal kompilieren und?
UPS!

Von diesem Begriff solltest du dich aber mal verabschieden. Der ist in dem von dir beschriebenen Zusammenhang falsch.
Bei einer Verzögerung läuft deine Pumpe länger, bis die Verzögerung abgelaufen ist.
Wie ich es verstehe, möchtest du eine "vorgezogene" also frühere Abschaltung.

Also Topf läuft über und dann wird der ganze Fußboden nass ist ja nicht ohne. Kannst du bitte mal beschreiben wie lange können deine Pflanzen "trockenen" Boden ertragen ohne Schaden zu nehmen?
Wie lange ist deine längste Abwesenheit während dessen deine Pflanzen bewässert werden sollen?

Willst du für 2 Wochen in den Urlaub fahren und in der Zwischenzeit sollen die Pflanzen bewässert werden?
Da muss deine Bewässerungssteuerung verdammt zuverlässig funktionieren. In diesem Falle würde ich mir passive Sicherheitsmaßnahmen überlegen. Damit meine Ich zum Bespiel:
alle Pflanzen in die Duschwanne / Badewanne stellen damit überlaufender Wasser gar keinen Schaden anrichten kann.

Noch eine Möglichkeit wäre das Vorratsvolumen bewusst auf ein kleines Volumen zu beschränken. Wenn deine Pflanzen auch mal 12 Stunden ohne Nachbewässerung gut zurecht kommen dann könntest du in deiner Abwesenheit nur aus dem kleinen Behälter Wasser zuführen. Wenn die Steuerung nicht rechtzeitig abschaltet ist nach 2-5 Litern der Tank leer.

Oder die Pflanzen stehen in einem Auffanggefäß mit doppelt ausgeführten Schwimmerschaltern.
Die beiden Schwimmerschalter sind in Serie geschaltet. Wenn einer aufschwimmt (bevor das Gefäß überläuft) dann wird der Pumpenstrom direkt = unter Umgehung des Microcontrollers unterbrochen.
Sicherheitabschaltungen werden immer ausgeführt als "nur wenn alles normal ist einschalten. Sonst sofort und direkt aus.
Durch diese Schaltungsweise wird auch bei einem Kabelbruch / fehlendem Kontakt sofort abgeschaltet. Das bedeutet dein Programm könnte noch so wild herumspinnen die Abschaltung funktioniert trotzdem weil der Schwimmerschalter den Strom direkt abschaltet.

So nun zur eigentlichen Zeitabschaltung:
Du brauchst so etwas ähnliches wie ein Mono-Flop mit Totzeit.
Damit ist folgendes gemeint:
Wenn ein Einschaltimpuls detektiert wird dann einen Ausgang für eine bestimmte Zeit einschalten und danach wieder ausschalten.
Zusätzlich direkt nach dem Ausschalten eine gewisse Zeit lang eintreffende Einschaltsignale ignorieren = Schaltung reagiert nicht auf das Einschaltsignal bis die sogenannte Totzeit vorbei ist.

Wie viel Zeit vergeht bei besonders heißem Wetter = Boden wird am schnellsten trocken.
vgs

Das ist der Logikplan für M1/M2.

1 Like

Das nennt sich in der Automatisierungswelt "Ausschaltverzögerung"

Also eine Laufzeitbegrenzung?

Sollte da nicht stehen
! digitalRead(S0)
?

Hallo vielen Dank für deinen "Roman". Wenn ich um Urlaub bin soll die Anlage, wie sonst auch zuverlässig funktionieren. Die läuft 24/7, also immer. Die Anlage ist relativ unhandlich und mit dem Wasser schwer. Sodass das bewegen ein Kraftakt ist. (Zwei Blech Schaltschränke verbaut). Der Schwimmschalter ist eine super Idee. Da muss ich nur mal schauen, wie ich das Umsetze. Daher wolte ich das zuerst Softwareseitig verriegeln. LG

Nein. Dann würde der Austaster S0 nicht mehr funktionieren. LG

Das passt absolut nicht.

Genau so sehe ich das auch.

Jetzt habe ich keine Fehler mehr. Ich habe Zwei Klammern vergessen

int S1 = 7;         //Start Taster
int S0 = 6;         //Stop Taster
int M1 = 2;         //Pumpe 1 (Relais K1)
int M2 = 3;         //Pumpe 2 (Relais K2)
int P1 = 4;         //LED_1:"Anlage läuft"
int P2 = 5;         //LED_2:"Bitte Wasser nachfüllen" | "Pegel n.I.O"
int Pegel = A0;     //Sensor für den Wasserstand (B3)
int Feuchte1 = A1;  //Sensor für die Bodenfeuchte (B1)
int Feuchte2 = A2;  //Sensor für die Bodenfeuchte (B2)
bool Start = false; //Variable für den Anlagenstart
int count_M1 = 0;   //Variable um die Einschaltzeit (s) der Pumpe "M1" zu zählen, wird nach erneutem Reinladen in den Arduino gelöscht.
int count_M2 = 0;   //Variable um die Einschaltzeit (s) der Pumpe "M2" zu zählen, wird nach erneutem Reinladen in den Arduino gelöscht.
uint32_t previousMillis = 0; //Variable für die Ausschaltzeit.
uint32_t previousMillis1 = 0; //Variable für die Zweite Ausschaltzeit.


void setup() {
    //Setup für die Eingänge:
  pinMode(S1, INPUT_PULLUP);
  pinMode(S0, INPUT_PULLUP);
    //Setup für die Ausgänge:
  pinMode(M1, OUTPUT);
  pinMode(M2, OUTPUT);
  pinMode(P1, OUTPUT);
  pinMode(P2, OUTPUT);
  digitalWrite(M1, LOW);
  digitalWrite(M2, LOW);
  digitalWrite(P1, LOW);
  digitalWrite(P2, LOW);
  Serial.begin(9600);
}
void loop() {
//Hier werden die Taster der Anlage abgefragt um die Variable "Start" zu schreiben. Die Led P1 "Anlage Eingeschaltet" wird hier geschaltet.
  if (!digitalRead(S1) == true){
    digitalWrite(P1, true);
    Start = true;  
  }
  if (digitalRead(S0) == true) {
    digitalWrite(P1, false);
    Start = false;
  }
  
  //Hier wird der Durchschnittswert vom Wasserstandssensor berechnet, um die Hysterese des Sensors möglichst klein zu halten.
  int average = 0;
  for (int i = 0; i < 8; i++) {  
    average = average + analogRead(Pegel);
    delay (50);
  }
  average = average / 8; // Den Durchschnittswert durch die Anzahl der Schleifen nehmen. (Durch "8").

// Hier werden die Werte für den Wasserstand ausgewertet. Dafür muss die Anlage Eingeschaltet sein. Der Wasserstand soll immer im optimalen Bereich liegen, damit die Pumpen nicht Trockenlaufen.
  if ((average < 418 ) && (Start == true)) {  // Bei niedrigem Füllstand leuchtet die Meldeleuchte "P2", "Bitte Wasser auffüllen"
    digitalWrite(P2, HIGH);
  
  } else if ((average > 418 ) || (Start == false)){ //Die Meldeleuchte kann auch über den Taster S0 ausgeschaltet werden.
    digitalWrite(P2, LOW);
  }

// Hier wird nun der Sensor "B1" ausgelesen und die Pumpe "M1" für die Pflanze eingeschaltet, sobald die Anlage eingeschaltet ist.
  if ((analogRead(Feuchte1) < 500 ) && (Start == true)) {
    digitalWrite(M1, HIGH);
    previousMillis = millis(); //Die Zeit speichern.
    count_M1 = count_M1 + 1;    //Anzahl der Sekunden, wie lange die Pumpe aktiv ist, wird hier gezählt.

  }if ((millis() - previousMillis > 3 * 1000UL && digitalRead(M1) == HIGH) || analogRead(Feuchte1) > 500  || Start == false) {
    digitalWrite(M1, LOW);

  }
// Hier wird nun der Sensor "B2" ausgelesen und die Pumpe "M2" für die Pflanze eingeschaltet, sobald die Anlage eingeschaltet ist.
  if ((analogRead(Feuchte2) < 500 ) && (Start == true)) {
    digitalWrite(M2, HIGH);
  
    count_M2 = count_M2 + 1;    //Anzahl der Sekunden, wie lange die Pumpe aktiv ist, wird hier gezählt.

  }if ((analogRead(Feuchte2) > 500 ) || (Start == false)) {
  
    digitalWrite(M2, LOW);
  }
  

  Serial.println("Sekundenzähler M1:"); 
  Serial.println(count_M1);
  Serial.println("Sekundenzähler M2:");
  Serial.println(count_M2);
  Serial.println(previousMillis);  
}

Ja richtig eine Laufzeitbegrenzung.
Lass uns nicht streiten. Es ist auch nur ein Begriff.

Das ist auch OK mit dem softwareseitig verriegeln, aber wenn die Software aus irgendwelchen Gründen spinnt oder das der Relaiskontakt der die Pumpe schaltet auf "eingeschaltet" kleben bleibt, dann sollte es eine Sicherheitsabschaltung geben. Da kommt es dann darauf an was dir lieber ist, im Falle des Falles vertrocknete Pflanzen oder abgesoffene Pflanzen und abgesoffene Wohnung.

Hier ein Demo-code in dem eine Einschaltverzeögerung und eine Ausschaltverzögerung und eine Wiedereinschaltverzögerung drin ist.

Die Variablen

unsigned long InitialWaitTime = 2000; // set to zero if you don't want an initial wait
unsigned long ActiveTime = 3000;
unsigned long LockedTime = 10000; // set to zero if you don't want a lock-time

steuern das Schaltverhalten.
Wenn du gar keine Einschaltverzögerung haben willst wird

unsigned long InitialWaitTime = 0; // set to zero if you don't want an initial wait
lock-time

auf Null gesetzt.

Wenn es keine Wiedereinschaltverzögerung geben soll dann wird

unsigned long LockedTime = 0; // set to zero if you don't want a lock-time

auf Null gesetzt.
.
.

unsigned long ActiveTime = 3000;

legt die Einschaltzeit fest.

Hier der gesamte Code

const byte triggerInputPin  = 3;
const byte triggerIsActive = LOW; // LOW or HIGH

const byte PinToSwitch = 12;
const byte OutPutOn  = HIGH;
// the attention-mark ! is the not-operator which inverts the value
// !true = false  !false = true     !HIGH = LOW    !LOW = HIGH
const byte OutPutOff = !OutPutOn; 

// set InitialWaitTime to 0 if you don't want a delay between triggering and switching output
unsigned long InitialWaitTime = 2000; // set to zero if you don't want an initial wait
unsigned long ActiveTime = 3000;
unsigned long LockedTime = 10000; // set to zero if you don't want a lock-time

unsigned long MonoFlopTimer; // variable used for non-blocking timing

// constants of the state-machine
const byte sm_Idling      = 0;
const byte sm_InitialWait = 1;
const byte sm_Activated   = 2;
const byte sm_Locked      = 3;

byte MonoFlopState = sm_Idling; // state-variable of the state-machine


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();
  pinMode(triggerInputPin,INPUT_PULLUP); // INPUT_PULLUP
  digitalWrite(PinToSwitch,OutPutOff);
  pinMode(PinToSwitch,OUTPUT);
}


void MonoFlopStateMachine() {

  switch (MonoFlopState) {

    case sm_Idling:
      // check if trigger-input reads triggering signal
      if (digitalRead(triggerInputPin) == triggerIsActive) {
        MonoFlopTimer = millis();       // make snapshot of time
        MonoFlopState = sm_InitialWait; // set next state of state-machine
        Serial.println( F("trigger-signal detected start inital wait") );
      }
      break; // IMMIDIATELY jump down to END OF SWITCH

    case sm_InitialWait:
      // wait until initial waittime has passed by
      if ( TimePeriodIsOver(MonoFlopTimer,InitialWaitTime) ) {
        // if waittime HAS passed by
        MonoFlopTimer = millis();           // make new snapshot of time
        digitalWrite(PinToSwitch,OutPutOn); // switch IO-Pin to activated state
        MonoFlopState = sm_Activated;       // set next state of state-machine
        Serial.println( F("InitialWaitTime is over switching output to ACTIVE") );
      }
      break; // IMMIDIATELY jump down to END OF SWITCH

    case sm_Activated:
      // check if time the IO-pin shall be active is over
      if ( TimePeriodIsOver(MonoFlopTimer,ActiveTime) ){
        // if activetime of IO-pin IS over
        MonoFlopTimer = millis();             // make new snapshot of time
        digitalWrite(PinToSwitch,OutPutOff);  // switch IO-pin to DE-activated state
        MonoFlopState = sm_Locked;            // set next state of state-machine
        Serial.println( F("Activetime is over switching output to IN-active") );
        Serial.println( F("and starting locktime") );
      }
      break; // IMMIDIATELY jump down to END OF SWITCH

    case sm_Locked:
      // check if time the logic shall be locked against too early re-triggering is over
      if ( TimePeriodIsOver(MonoFlopTimer,LockedTime) ){
        // if locked time IS over
        Serial.println( F("locktime is over change state to idling") );
        MonoFlopState = sm_Idling; // set state-machine to idle-mode
      }
      break; // IMMIDIATELY jump down to END OF SWITCH
  } // END OF SWITCH
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);

  // run down code of state-machine over and over again
  // all the logic for reading in sensor-signal and switching output ON/OFF 
  // is inside the function
  MonoFlopStateMachine(); 
}

soeben auf WOKWI getestet.

vgs

Damit es jeder versteht, was du bastelst, sollten schon die Begriffe richtig gewählt werden.
Da muss keiner drüber streiten.

Nein.
Du speicherst in count_M1 nichtmal die Millisekunden, sondern hast einen Zähler für jeden Umlauf
count_M1 läuft nach 32767 über und fängt dann bei -32768 erneut an zu zählen.

vermutlich wolltest du jemanden anderen antworten.

Vielen Dank für deinen Code.

Dann ist es eine Laufzeitüberwachung. Dass kann ich noch unterstützen

Hast Du die Taster wirklich so unterschiedlich verkabelt?
S1 mit ! und S0 ohne?

Der Zähler ist nur eine Spielerei. Der hat nichts mit der Laufzeitüberwachung zu tun. Der zählt in Sekunden.