Hilfe bei einer simplen Pumpensteuerung

Hallo, ich möchte mithilfe von 2 Schwimmschaltern ein Gefäß füllen.

Wenn beide Schwimmer nicht schwimmen soll die Pumpe angehen und füllen bis der obere Schwimmer auslöst.

Das funktioniert aber leider nicht immer. sehr häufig schaltet die Pumpe schon ab wenn nur der untere Schwimmer beim füllen auslöst.

Hat jemand eine Idee warum?
Hier der Code :

int su = 2; // schwimmer unten
int so = 3;// schwimmer oben
int pump = 8; // Pumpe 
int led = 9; //Ledpin
int storung = 0;// hilfsvariable 
long szeit= 900000;// Sicherheitszeit 15 min
void setup() 
{
 pinMode (su, INPUT_PULLUP); // Input und pullup
 pinMode (so, INPUT_PULLUP);// Input  und pullup

 pinMode (pump, OUTPUT);
 pinMode (led, OUTPUT);
Serial.begin (9600);
}

void loop()
{
delay(50);
 
  if (digitalRead (so)&& digitalRead (su)) //schwimmer abfragen
  {
    unsigned long zeit  = millis() + szeit; // "zeit setzen"
    while(digitalRead (so)) // so lange schwimmer oben nicht schwimmt
    {   

            if (zeit < millis() && digitalRead (so)) //zeit überschritten und schwimmer schwimmt noch nicht
                {
                  digitalWrite (pump,LOW); 
                  storung =1;
                   
                  digitalWrite (led,HIGH);
                  delay (1000);
                  
                }
                if (storung ==0) //wenn keine Störung vorliegt
                {
                 digitalWrite (pump,HIGH);
                }
  }
  }
  else //alles andere
  {
    digitalWrite (pump,LOW); 
    digitalWrite (led,LOW);
  }

}

Ich sehe im Momemt noch keinen Grund, warum du dafür (nach deiner Erklärung) zwei Schwimmer brauchst.

Und irgendwie stimmt die Logic nicht.
Du verwendest Pullup-Widerstände und fragst auch auf HIGH ab.
Der Eingang ist doch so immer HIGH.

Das mit den zwei Schwimmern ist schon klar: unten: einschalten, oben: ausschalten.

Aber dein Code .... :o

ist schon etwas chaotisch.
Ich denke, dein Problem ist nicht mal das Programmieren, sondern die Struktur.
Ich würde das grundsätzlich anders aufbauen:
ohne blockierende While-Schleifen.
Am Anfang der loop beide Schalter abfragen und das Ergebnis in Variable schreiben.
Dann in der loop deine ganzen Zustandsvariablen abfragen und entsprechend agieren.

aber, bevor du anfängst zu codieren, nimm ein Blatt Papier, zeichen erst mal auf, was genau dein Code wann und in welcher Reihenfolge machen soll.

So mal als ganz grobes Beispiel:

Die Logik mit den Pullups stimmt schon. Er basiert seine ganze Schaltung darauf dass der Eimer nicht voll ist und prüft immer nur das. Der volle Eimer muss irgendwie im else Fall stecken, aber der Code ist so verquer dass ich da auch nicht durch steige.
Der untere Schwimmer ist im gegebenen Code vollkommen überflüssig, das stimmt. Ich vermute mal dass dieser das einschalten der Pumpe steuern soll, tut er aber nicht.
Momentan wird die Pumpe nur dadurch eingeschaltet dass keine Störung vorliegt. Merkwürdige Logik.


Der Code sieht aus als hättest du mal drauf los programmiert ohne dir vorher Gedanken zu machen was du eigentlich steuern willst.

Mach dir erst einmal Gedanken bevor du anfängst zu programmieren. Welche Ereignisse treten ein, und welche Befehle sollen ausgegeben werden?

Ereignisse:

  • Eimer leer
  • Eimer voll
  • Pumpe läuft zu lange

Befehle:

  • Pumpe an
  • Pumpe aus
  • LED an
    (-Steuerung stoppen)

Du bringst dich schon damit durcheinander dass du sagst "Wenn beide Schwimmer nicht schwimmen". - Dass der obere Schwimmer nicht schwimmen kann wenn der untere es nicht tut gebietet die Physik, also reicht es den unteren auf nicht schwimmend zu prüfen.

Das muss dann in logische Zusammenhänge verknüpft werden.
Könnte z.b. so aussehen:

int su = 2; // schwimmer unten
int so = 3;// schwimmer oben
int pump = 8; // Pumpe 
int led = 9; //Ledpin
int storung = 0;// hilfsvariable 
long zeit = 0;
long szeit= 900000;// Sicherheitszeit 15 min
void setup() 
{
 pinMode (su, INPUT_PULLUP); // Input und pullup
 pinMode (so, INPUT_PULLUP);// Input  und pullup

 pinMode (pump, OUTPUT);
 pinMode (led, OUTPUT);
Serial.begin (9600);
}

void loop()
{
delay(50);
  //Pumpe
  if (digitalRead(su) && !digitalRead(pump)) { //Wenn Eimer bis unten leer und Pumpe aus
    digitalWrite(pump, HIGH); //Pumpe an
    zeit = millis();    //Einschlatzeitpunkt merken
  }
  if (!digitalRead(so) && digitalRead(pump)) //Wenn Eimer bis oben voll, schalte Pumpe aus
  
  
  if ((millis() - szeit ) && digitalRead(pump)) {  //Wenn nach abgelaufener Zeit Pumpe immer noch läuft
    digitalWrite(led, HIGH);  //Gibt Störung aus
    digitalWrite(pump, LOW);  //Schalte Pumpe ab
    while(1) {}; //Halte den Arduino endlos in der Störung fest bis die Reset Taste gedrückt wird.
  }

}

guntherb:
Das mit den zwei Schwimmern ist schon klar: unten: einschalten, oben: ausschalten.

Mir auch, aber nicht mit seiner Beschreibung.

Du hast im aber klar beschrieben, wie er es machen soll.

Ausser dem genannten Timeout würde ich schon auch den oberen Schalter abfragen, auch wenn der untere „nicht schwimmt“. Und den unteren auch, auch wenn der obere schwimmt.
Dadurch lassen sich Fehlfunktion der Hardware abfangen.

Weil mir gerade langweilig war:

Probiers mal mit einer Statemachine (=endlicher Automat).

hier überlegt man, in welchen Zuständen kann das System verharren, und mit welchen Bedingungen wird von einem in den anderen Zustand gewechselt.

Hier gibt es eigentlich 2 Zustände:
"Voll" (tut nix) und "Pumpen". Und man kann noch einen Zustand "Fehler" definieren.
Da könnte dann so aussehen:

Eingangszustand ist "Voll". Wenn der Schalter su aktiviert wird, springt er in "Pumpen".
Aus Pumpen springt er wieder zu "Voll", wenn Schalter "so" aktiviert ist, oder zu "Fehler" über den Timeout.

Als Code kann das dann in etwa so aussehen:

void loop() {
  bool su = digitalread(su_Pin);
  bool so = digitalread(so_Pin);
  static byte State = 0;  // status der Statemachine: Voll:0, Pumpen:1, Fehler:2

  switch (State){
    case 0:                         // Status Voll
      if (su){                        //  wenn unterer Pegel erreicht
        digitalWrite(Pumpenpin, HIGH);  // Pumpe ein
        StartZeit = millis();           // Zeit merken
        State = 1;                      // nächster State: Pumpen        
      }
    break;
    case 1:                         // Status Pumpen
      if (so){                        // wenn oberer Pegel erreicht
        digitalWrite(Pumpenpin, LOW);  // Pumpe aus
        State = 0;                      // nächster State: Voll                
      }
      if(millis()-Startzeit > timeout){ // Totzeit abgelaufen
        digitalWrite(Pumpenpin, LOW);    // Pumpe aus
        digitalWrite(Fehlerpin, HIGH);   // Fehler LED an
        State = 2;                       // nächster Status: Fehler        
      }
    break;
    case 2:                         // Status Fehler
      // hier könnte man z.B. eine Reset-Taste einlesen. 
     break;
     default:
  }// end switch
  
}// end loop

Nach wie vor kann ich allerdings diese Verwendung der "millis()" im Sketch nicht nachvollziehen.

Das solltest du noch mal überdenken oder besser erklären.

guntherb:
Weil mir gerade langweilig war:

So was gibt es tatsächlich ? :wink:

HotSystems:
Nach wie vor kann ich allerdings diese Verwendung der "millis()" im Sketch nicht nachvollziehen.

Das solltest du noch mal überdenken oder besser erklären.

Meinst du mich?
Einfach eine Totzeit, damit die Pumpe nicht endlos läuft.

guntherb:
Meinst du mich?
Einfach eine Totzeit, damit die Pumpe nicht endlos läuft.

Nein...sorry.
Ich meine den TO. In seinem Sketch fehlt mir einfach die Logik.
Und wenn die Schwimmer wieder oben sind, sollte die Pumpe doch ausschalten, also warum noch die "Totzeit" ?

HotSystems:
Und wenn die Schwimmer wieder oben sind, sollte die Pumpe doch ausschalten, also warum noch die "Totzeit" ?

Könnte mir vorstellen das dies eine Sicherheitsfunktion ist falls der obere Schwimmer defekt sein sollte. So würde ich es aufjedenfall auch machen falls die Pumpe laufen soll, wenn man nicht in der Nähe ist.

Viele Grüße,
bdorer

bdorer:
Könnte mir vorstellen das dies eine Sicherheitsfunktion ist falls der obere Schwimmer defekt sein sollte. So würde ich es aufjedenfall auch machen falls die Pumpe laufen soll, wenn man nicht in der Nähe ist.

Da könnte ich mir ganz andere Probleme vorstellen, bevor ein Schwimmer defekt geht.

Z.B.
2. Netzteil, welches bei defekt eine Hupe anschaltet oder SMS sendet.
2. Controller, der zusätzlich den Wasserstand prüft.

USW.

Wenn er mit einer Wasserpumpe in einen Behälter pumpt, dann ist das offenbar nicht aus der Wasserleitung, denn da brauche ich keine Pumpe, sondern ein Ventil.

Also kann es passieren, dass da wo er das Wasser rauspumpt, auch mal kein Wasser mehr zu holen ist. Dann läuft die Wasserpumpe trocken, was sie in der Regel sehr übel nimmt. Also braucht man auch noch eine Durchflusskontrolle in der Leitung nach der Pumpe, um dann wenn die Pumpe läuft kontrollieren zu können, ob noch Wasser durch die Pumpe läuft. Und wenn man schon einen Durchflusszähler kontrolliert, kann man damit auch gleich sehen ob der Behälter auf jeden Fall schon voll sein müßte. So hat man eine zusätzliche Kontrolle über den Füllstand, wenn der Endschalter nicht mehr geht.

@guntherb: State würde ich noch static machen

Die Pumplogik sollte sein:
su aktiv, bis so oder timeout zuschlägt. Timeout sollte in Abhängigkeit der tatsächlichen Pumpdauer gewählt werden.
Su und so bilden eine Hysterese

Weitere Logik und evtl. ein Meldesystem um Fehlfunktionen wie Wassermangel im Brunnen, defekte Schwimmerschalter, manuelle Manipulation, etc. ist anzuraten.
Z.B. 2 x timeout hintereinander lässt auf Wassermangel, defekte Pumpe oder oberer Schwimmerschalter schliessen.

#include <CombiePin.h>
#include <CombieTimer.h>

Combie::Pin::InputPin<3>        schwimmerschalterOben;
Combie::Pin::InputPin<2>        schwimmerschalterUnten;
Combie::Pin::OutputPin<8>       pumpe;
Combie::Pin::OutputPin<9>       leuchtMelderStoerung;
Combie::Timer::RisingEdgeTimer  pumpenTimeOut(900000UL); 

enum Stoerung:byte {AllesOk=0,Zeitueberschreitung,Sensorfehler};

const bool ein = true;
const bool aus = false;

Stoerung stoerung          = Stoerung::AllesOk; // // hilfsvariable Stoerungsmerker
bool pumpAnforderung = aus;

void setup() 
{
  Serial.begin(9600);
  Serial.println("Start");
  schwimmerschalterOben.initPullup();
  schwimmerschalterUnten.initPullup();
  leuchtMelderStoerung.init();
  pumpe.init();
}

void loop() 
{
  leuchtMelderStoerung = stoerung; // bei Stoerung LED entzuenden
  
  // harte Fehlerbehandlung
  if(stoerung)
  {
    pumpe = aus;
    return;
  }

  // baue Anforderung zum Pumpen
  if(!schwimmerschalterUnten)
  {
        pumpAnforderung = ein;
  }

  // Pumpanforderung konsumieren
  if(schwimmerschalterOben)
  {
     pumpAnforderung = aus;
  }

  // baue Sensor error
  if(schwimmerschalterOben && !schwimmerschalterUnten)
  {
    stoerung = Stoerung::Sensorfehler;
    Serial.println("Sensorfehler");
  }
  
  // baue Zeitueberschreitung error
  if(pumpenTimeOut(pumpAnforderung))
  {
    stoerung = Stoerung::Zeitueberschreitung;
    Serial.println("Zeitueberschreitung");
  }

  // Pumpen Abhandlung
  pumpe = pumpAnforderung && !stoerung;
}

ungetestet
Habe keine Pumpe und weiß die Schalterlogik nicht

CombieLib.zip (31.3 KB)

Wenn wir hier schon am spielen sind:

die Pumpenlogik passt auch in eine Zeile:

  digitalWrite(PumpenPin, !(digitalRead(PumpenPin) || digitalRead(SU)) || !digitalRead(SO));  // Schalterlogik:SU,SO schwimmen = High

:slight_smile:

da muss dann nur noch die TimeOut Funktion noch mit dazu, und etwas Fehlerbehandlung.
aber das ist ja oft so, dass die eigentliche Funktion recht klein ist, und das Drum Herum viel Platz braucht.

Bin gespannt, ob sich Pascal1 hier nochmal rührt.

20% Für die Funktion
80% Fehlerabhandlung

Egal, in welchem Zusammenhang.
Das ist ein guter Pi mal Daumen Wert