Hilfe bei Unterbrechung Lichtschranke bei Rolltor/Kabine

Guten Morgen,

ich habe ein kleines Problem mit der Umsetzung einer Rolltor- / Kabinensteuerung. Meine kleine Recherche hat des Rätsels Lösung auch nicht gefunden, bzw. sind mir bisher keine zielführenden Suchbegriffe eingefallen.

Eine Kabine fährt bei Betätigung eines Tasters (Seiltaster oder ExternTaster) zwischen zwei Endschaltern auf und ab. Die Endschalter stoppen den Motor und ändern die Drehrichtung.

Ich habe ein Problem bei der Umsetzung der Lichtschranke. Wenn diese beim abwärts fahren unterbrochen wird, soll der Motor Stoppen, kurz warten, Drehrichtung nach oben schalten und Motor starten.
Wenn die Kabine in den Endlagen ist, soll nichts passieren.

Mein Problem ist, wenn die Lichtschranke unterbrochen bleibt, bleibt die Kabine ja dauerhaft stehen.
Wie bekomme ich es umgesetzt, dass nach oben gefahren wird, wenn das Lichtschrankensignal "high" bleibt?

Reicht dafür eine Abfrage der Flankenänderung (Low-High) der Lichtschranke im "else-Bereich"?

Hier mein bisheriger Code:

  const int seilSch = 12; // Seilzugschalter
  const int endSchO = 11; // Endschalter Oben
  const int endSchU = 10; // Endschalter Unten
  const int extSch = 9; // Extern Hoch
  const int lichtSchr = 8; // Lichtschranke
  int drehrichtungMot = 6;
  int powerMot = 5; // Spannung Motor
  int fehlerUnterbr = 4; // Unterbrechung bei Zeitüberschreitung
  int status_seilSch = 0;
  int status_endSchO = 0;
  int status_endSchU = 0;
  int status_extSch = 0;
  int status_lichtSchr = 0;

  void setup() {

pinMode (seilSch, INPUT);
pinMode (endSchO, INPUT);
pinMode (endSchU, INPUT);
pinMode (extSch, INPUT);
pinMode (lichtSchr, INPUT);
pinMode (drehrichtungMot, OUTPUT); //Relais K5
pinMode (powerMot, OUTPUT); // Relais K4
pinMode (fehlerUnterbr, OUTPUT); //Relais K6 und K7 - unbenutzt
digitalWrite (powerMot, HIGH);
}

void loop() {
status_seilSch = digitalRead (seilSch);
status_endSchO = digitalRead (endSchO);
status_endSchU = digitalRead (endSchU);
status_extSch = digitalRead (extSch);
status_lichtSchr = digitalRead (lichtSchr);

// Endschalter oben: Motor stopp, Drehrichtung nach unten schalten
if (status_endSchO == HIGH){
  digitalWrite (powerMot, HIGH);    
  //delay(50);
  digitalWrite (drehrichtungMot, HIGH);
}

// Endsachalter unten: Motor stopp, Drehrichtung nach oben schalten
if (status_endSchU == HIGH){ 
  digitalWrite (powerMot, HIGH);    
  //delay(50);
  digitalWrite (drehrichtungMot, LOW);
}

//wenn niemand in einer Lichtschranke steht
if (status_lichtSchr == LOW){ 

    //wenn Kabine oben und Seilschalter wird betätigt, Motor dreht
    if (status_seilSch == HIGH && status_endSchO == HIGH && status_endSchU == LOW){
      digitalWrite (powerMot, LOW);
      delay (1000); // um aus Endschalter zu fahren
    }

    // wenn Kabine unten und Seilschalter wird betätigt, Motor dreht
    if (status_seilSch == HIGH && status_endSchO == LOW && status_endSchU == HIGH){    
      digitalWrite (powerMot, LOW);
      delay (1000); // um aus Endschalter zu fahren
    }

    //wenn Kabine zwischen Endschaltern, dann Stopp und wieder nach oben
    if (status_seilSch == HIGH && status_endSchO == LOW && status_endSchU == LOW){ 
      digitalWrite (powerMot, HIGH);
      delay (2000);
      digitalWrite (drehrichtungMot, LOW);
      delay (200);
      digitalWrite (powerMot, LOW);
    }

    //extern Hochfahren wenn Kabine unten
    if (status_extSch == HIGH && status_endSchO == LOW && status_endSchU == HIGH){    
      digitalWrite (powerMot, LOW);
      delay (1000);
    }

    //wenn Kabine oben und Seilschalter wird betätigt, Motor dreht
    if (status_extSch == HIGH && status_endSchO == HIGH && status_endSchU == LOW){    
      digitalWrite (powerMot, LOW);
      delay (1000); // um aus Endschalter zu fahren
    }

    //wenn Kabine zwischen Endschaltern, dann Stopp und wieder nach oben
    if (status_extSch == HIGH && status_endSchO == LOW && status_endSchU == LOW){ 
      digitalWrite (powerMot, HIGH);
      delay (2000);
      digitalWrite (drehrichtungMot, LOW);
      delay (200);
      digitalWrite (powerMot, LOW);
    }

} //END if (status_lichtSchr == LOW

else{
    
    //soll nicht fahren, wenn Lichtschranke unterbrochen ist und Kabine in Endschaltern
    if (status_endSchU == HIGH || status_endSchO == HIGH){  
      digitalWrite (powerMot, HIGH);
    }
    
}// END else

  
} // END Loop

Ich kann den Code leider nicht an der Anlage testen, deshalb muss ich mein Problem so lösen und frage deshalb hier nach.

Ich bin für jede Hilfestellung dankbar. Vielen Dank im Voraus.

Grüße,

Stephan

SteveC:
Hier mein bisheriger Code: ...

Dein Code ist Meiner Meinung nach sehr schlecht zu lesen, weshalb ich mir die Mühe gespart habe. Eine Strukturierung durch Einrückungen und eine maximale Länge von 80 Zeichen je Zeile wären hilfreich.

Gruß

Gregor

Eine Strukturierung durch Einrückungen und eine maximale Länge von 80 Zeichen je Zeile wären hilfreich.

Erledigt.

Ich verstehe den Bewegungsablauf nicht:

Eine Kabine fährt bei Betätigung eines Tasters (Seiltaster oder ExternTaster) zwischen zwei Endschaltern auf und ab. Die Endschalter stoppen den Motor und ändern die Drehrichtung.

Ich habe ein Problem bei der Umsetzung der Lichtschranke. Wenn diese beim abwärts fahren unterbrochen wird, soll der Motor Stoppen, kurz warten, Drehrichtung nach oben schalten und Motor starten.
Wenn die Kabine in den Endlagen ist, soll nichts passieren.

Wann stopt die Kabine denn endgültig?

Grüße Uwe

Auch ich verstehe den Bewegungsablauf nicht so recht. Du schreibst, dass die Kabine zwischen zwei Endschaltern hin- und herfährt. Andererseits soll nichts passieren, wenn die Kabine in den Endlagen steht.

Wie isses denn nun und wie kommt die Kabine in die Endlagen? Haben die Endschalter nichts mit den Endlagen zu tun?

Gruß

Gregor

Der Funktionsablauf ist wie folgt:

Status: Kabine ist zwischen Endschaltern
Seilzug wird betätigt -> Kabine fährt nach oben in Endschalter
Endschalter Oben schaltet Motor ab und ändert Drehrichtung nach unten
Seilzug wird betätigt -> Kabine fährt nach unten in Endschalter
Endschalter Unten schaltet Motor ab und ändert Drehrichtung nach oben
Seilzug wird betätigt -> Kabine fährt nach oben in Endschalter
usw.

Lichtschranken:
Wenn Lichtschranke unterbrochen wird und der Motor zwischen zwei Endschaltern (also beide unbetätigt) ist, dann soll der Motor stoppen (*), die Drehrichtung nach oben ändern, Motor anschalten und in Endschalter oben fahren.

Dort ist er dann wieder in seiner Ausgangslage.

Mein Problem liegt darin, dass wenn die Lichtschranke unterbrochen bleibt (jemand steht drin) der Motor ja wegen der Schleife (siehe *) festhängt.

Ich weiß nicht, wie ich das Codemäßig umsetze.

Mein Problem liegt darin, dass wenn die Lichtschranke unterbrochen bleibt (jemand steht drin) der Motor ja wegen der Schleife (siehe *) festhängt.

Ich weiß nicht, wie ich das Codemäßig umsetze.

So ganz habe ich den Ablauf noch nicht verstanden...
Aber bemerkt, dass das ein Ablauf werden soll. Besser gesagt, eine Ablaufsteuerung.

Mir gefallen die ganzen verschränkten if mit ihren Bedingungen nicht.
Zumindest nicht auf diese Art.

Verschachtelungstiefen von über 2 entziehen sich einer intuitiven Erfassung.
Auch verknüpfte Bedingungen werden schnell zu komplex.
Damit wird die Entwicklung fehlerträchtig und schwerer zu testen.

Also: Halte es einfach und übersichtlich.

Auch scheinen mir die delay() allesamt überflüssig.
Das geht auch anders.

Mein Tipp:
Baue eine Zustandstabelle!

Zustände, welche ich so erkennen kann:

  1. Endlage oben
  2. Endlage unten
  3. Fährt runter
  4. Fährt hoch
  5. Runterfahrt gestoppt (wg Lichtschranke)
    Habe ich welche vergessen/übersehen?

Definiere für jeden Zustand die Bedingungen, welche zu einem Zustandswechsel führen sollen.

So bekommst du den Automaten sauber und übersichtlich gestrickt.

Übrigens:

pinMode (powerMot, OUTPUT); // Relais K4
// hier kurze Low Phase
digitalWrite (powerMot, HIGH);

Das ist wohl nicht beabsichtigt.

evtl. besser:

// hier ohne Low Phase
digitalWrite (powerMot, HIGH);
pinMode (powerMot, OUTPUT); // Relais K4

Nunja, ich habe mir die zur Verfügung stehenden Mittel (aka Kenntnisse) eingesetzt.

Die 1sek Delays benötige ich, um aus den Endschaltern rauszukommen. Die kleineren hatte ich mal eingesetzt um kleine Schaltpausen zu haben.

Das mit dem Automaten habe ich mir schon angeschaut und werde es wohl mit dem nächsten Projekt so umsetzen.

Prinzipiell habe ich es ja so aufgebaut:

if (Lichtschranke == LOW) {

reagiere auf meine Schaltvorgänge und fahre zwischen meinen Endschaltern hin und her.

}

else (jemand / etwas ist in der LIchtschranke --> Lichtschranke == HIGH){

nur wenn Kabine zwischen Endschaltern (also beide LOW), dann

Motor Stopp, Drehrichtung nach oben, Motor fährt in Endschalter oben und bleibt da solange die LIchtschranke unterbrochen ist.

Das Problem ist halt, wenn der Fremdkörper in der Lichtschranke bleibt. Dann ist man ja immer wieder bei "Motor Stopp" in der else Anweisung.

Nunja, ich habe mir die zur Verfügung stehenden Mittel (aka Kenntnisse) eingesetzt.

Ich weiß... habe es mir zumindest so gedacht...
Also ist jetzt eine gute Gelegenheit hinzuzulernen.
Denn mit deinen bisherigen Mitteln geht es ja nicht.

Die 1sek Delays benötige ich, um aus den Endschaltern rauszukommen.

Auch das weiß ich.
Und es lässt sich dennoch anders lösen.

Es ist nicht so, dass die delay da grundsätzlich falsch sind. Aber sie verhindern, dass du eine tragfähige Strategie hin bekommst.
z.B. wenn während des Delay, die Lichtschranke belegt wird, kollidiert das mit der anderen Anforderung: Sofort stoppen, wenn Lichtschranke belegt.
Das ist ein logischer Fehler.

Du kannst natürlich behaupten, dass niemals ein 3 Jähriges Kind, auf einem Lkw Dach fest gekettet, genau in dem 1 Sekundenbereich, vom Rolltor, zum sterben gezwungen wird, weil niemand Kinder auf Dächer bindet.
Ich glaube dir das, aber ein TÜV Prüfer nicht.
Und die Versicherung auch nicht.

Es gibt keinen Grund 1 Sekunde zu fahren, und erst dann die Lichtschranke zu prüfen.
Besser wäre es erst los zu fahren, wenn die Lichtschranke nicht belegt ist.
Nein, ein delay halte ich in der Situation für böse.

Man kann einen mechanischen/elektrischen Fehler erkennen, wenn der Endschalter nicht innerhalb einer Sekunde frei wird.
Auch für den Fall, dass beide Endschalter belegt sind, kann man einen Fehlerzustand generieren.

Das Problem ist halt, wenn der Fremdkörper in der Lichtschranke bleibt. Dann ist man ja immer wieder bei "Motor Stopp" in der else Anweisung.

Das ist genau der Grund, warum ich sagte, du sollst die Bedingungen vom Zustand abhängig machen.
Dann hättest du das Problem nicht.

Das mit dem Automaten habe ich mir schon angeschaut und werde es wohl mit dem nächsten Projekt so umsetzen.

Meine ernste Empfehlung:
Nicht im nächsten!
In diesem.

Es wird sowieso ein Automat daraus(wenn es denn funktioniert).
Egal, ob du das beabsichtigst.
Egal, ob du die Theorie dahinter vollständig verstanden hast.
Also, dann doch besser mit dem richtigen Ansatz schnell und erfolgreich fertig werden.

combie:
Nein, ein delay halte ich in der Situation für böse.

Ein delay blockiert den Arduino, was bei Bewegungen gefährlich und damit verboten ist. Verwende stattdessen millis und eine Ablaufsteuerung. Da stimme ich mit #8 vollkommen überein!

Sollte sich ein Mensch an Deiner Konstruktion verletzen können, so solltest Du lebenslange Schadenersatzzahlungen unbedingt vermeiden.

Guten Abend,

vielen Dank für die bisherigen Hinweise und Unterstützung. Ich habe mich ein wenig in die Ablaufsteuerung eingelesen und habe erstmal den Ablauf visualisiert:

Wenn ich es richtig verstanden habe, müsste die Ablaufsteuerung doch prinzipiell so aufgebaut werden:

switch state {

case kabine_oben:
	Motor = AUS;
	if (Seilschalter oder Extern hoch == HIGH){
	state fahrt_nach_unten
	}
	break;

case fahrt_nach_unten:
	Drehrichtung = Runter;
	Schaltpause;
	Motor = AN;
	if (Endschalter_unten == HIGH){
	state kabine_unten
	}
	if (Lichtschranke == HIGH){
	state unterbrechung
	}
	break;
	
case kabine_unten:
	Motor = AUS;
	if (Seilschalter oder Extern hoch == HIGH){
	state fahrt_nach_oben
	}
	break;

case fahrt_nach_oben:
	Drehrichtung = Rauf;
	Schaltpause;
	Motor = AN;
	if (Endschalter_oben == HIGH){
	state kabine_oben
	}
	break;

case unterbrechung:
	Motor = AUS;
	Wartezeit;
	if (Lichtschranke == LOW){
	state fahrt_nach_oben;
	}
	else {
	state unterbrechung;
	}
	break;

Ich hoffe, das passt so. Nun sind aber noch offene Fragen:

  • Wie komme ich in den Ablauf rein?
    Die Anlage bekommt Strom, wartet auf Betätigung des Seilzugs und soll nach oben fahren

  • Funktioniert das mit der Lichschranke bei case fahrt_nach unten?
    Kann ich von dort in den case unterbrechung springen und von da dann entweder zu fahrt_nach_oben oder in case unterbrechung bleiben, wenn die LIchtschranke nicht frei sein sollte?

Grüße

Steve

Hier wäre dann mein Sketch:

const int seilSch = 12; // Seilzugschalter
const int endSchO = 11; // Endschalter Oben
const int endSchU = 10; // Endschalter Unten
const int extSch = 9; // Extern Hoch
const int lichtSchr = 8; // Lichtschranke
int drehrichtungMot = 6;
int powerMot = 5; // Spannung Motor
int status_seilSch = 0;
int status_endSchO = 0;
int status_endSchU = 0;
int status_extSch = 0;
int status_lichtSchr = 0;
const byte kabine_oben = 0;
const byte kabine_unten = 1;
const byte fahrt_nach_unten = 2;
const byte fahrt_nach_oben = 3;
const byte unterbrechung = 4;
const byte start = 5;
int state = start;
const long prellzeit = 50;
unsigned long previousMillis = 0; 


void setup() {

  pinMode (seilSch, INPUT);
  pinMode (endSchO, INPUT);
  pinMode (endSchU, INPUT);
  pinMode (extSch, INPUT);
  pinMode (lichtSchr, INPUT);
  pinMode (drehrichtungMot, OUTPUT); //Relais K5
  digitalWrite (powerMot, HIGH);
  pinMode (powerMot, OUTPUT); // Relais K4

}

void loop() {

  status_seilSch = digitalRead (seilSch);
  status_endSchO = digitalRead (endSchO);
  status_endSchU = digitalRead (endSchU);
  status_extSch = digitalRead (extSch);
  status_lichtSchr = digitalRead (lichtSchr);

  unsigned long currentMillis = millis();
  
  switch (state) {

  case start:
      digitalWrite (powerMot, HIGH);
      if (status_seilSch == HIGH || status_extSch == HIGH) {
            state = fahrt_nach_oben;
      }

  case kabine_oben:
      digitalWrite (powerMot, HIGH);
      if (status_seilSch == HIGH || status_extSch == HIGH) {
            state = fahrt_nach_unten;
      }
      break;

  case fahrt_nach_unten:
      digitalWrite (drehrichtungMot, HIGH);
      if (currentMillis - previousMillis >= prellzeit){
            previousMillis = currentMillis;
            digitalWrite (powerMot, LOW);
      }
      if (status_lichtSchr == HIGH) {
            state = unterbrechung;
      }
      if (status_endSchU == HIGH) {
            state = kabine_unten;
      }
      break;

  case kabine_unten:
      digitalWrite (powerMot, HIGH);
      if (status_seilSch == HIGH || status_extSch == HIGH) {
            state = fahrt_nach_oben;
      }
      break;

  case fahrt_nach_oben:
      digitalWrite (drehrichtungMot, LOW);
      if (currentMillis - previousMillis >= prellzeit){
            previousMillis = currentMillis;
            digitalWrite (powerMot, LOW);
      }
      if (status_endSchO == HIGH) {
            state = kabine_oben;
      }
      break;

  case unterbrechung:
      while (status_lichtSchr == HIGH) {
            digitalWrite (powerMot, HIGH);
      }
      if (status_lichtSchr == LOW) {
            state = fahrt_nach_oben;
      }
      break;

  } // END switch state


} // END Loop

Ich habe jetzt mal einen case start definiert und am Anfang "state = start" gesetzt.

Das Thema Sicherheit durch die Lichtschranke sollte eigtl auch so passen. Der Motor steht, solange etwas unterbricht. Wenn die Unterbrechung weg ist, dann Fahrt nach oben.

Keine Delays mehr, die Schaltpause zwischen Drehrichtung und Saft auf den Motor mit millis () umgesetzt.

Über ein kurzes Feedback, ob das alles so Sinn macht würde ich mich freuen.

Grüße,

Steve

Das sieht schon viel besser aus!
Der richtige Weg ist schon sichtbar!

Ein paar Sachen müssen noch anders.

In einem solchen state-Case, solltest du keine Schleifen verwenden.
Und wenn, dann nur sehr kurz laufende.
Schleifen blockieren genau so wie ein Delay.

Und darum dreht es sich ja, diese Blockaden raus zu bekommen.

Beispiel:

case unterbrechung:
while (status_lichtSchr == HIGH) {
digitalWrite (powerMot, HIGH);
}

Hier ist keine Endlagenabfrage Bedienelementabfrage drin/möglich.

Nein, die Schleife muss weg.
Besser die ganze Funktion Loop() 100Tausend mal pro Sekunde durchlaufen lassen, als da drin eine Schleife setzen.

Einiges Anderes, würde ich in den Bereich Kosmetik einordnen.
z.B.
Suche mal nach "C++ enum switch case"

const int seilSch = 12; // Seilzugschalter
const byte seilSch = 12; // Seilzugschalter


Leider fehlt mir heute die Zeit das vollständig mit dir durchzugehen.

Aus dem Bauch:
Vermutlich würde ich den Automaten in 2 aufteilen.

Einer für die übergeordnete Ablaufsteuerung, und einer für die konkrete Motorsteuerung.

Ja, die while Schleife war ungünstig.

case unterbrechung:
      if (status_lichtSchr == HIGH) {
            digitalWrite (powerMot, HIGH);
      }
      else if(status_seilSch == HIGH || status_extSch == HIGH) {
            state = fahrt_nach_oben;
      }
      break;

So sieht es mittlerweile aus.

Funktion ist gegeben. Ich bin zufrieden. :slight_smile:

SteveC:
Funktion ist gegeben. Ich bin zufrieden. :slight_smile:

Wären Anmerkungen dennoch willkommen?

Gerne doch. Man lernt schließlich nie aus.

Ich beziehe mich auf #11.

int drehrichtungMot = 6;
int powerMot = 5; // Spannung Motor

Das sind auch Konstanten.

int status_seilSch = 0;
int status_endSchO = 0;
int status_endSchU = 0;
int status_extSch = 0;
int status_lichtSchr = 0;

Hier wäre bool verständlicher.

const long prellzeit = 50;

Hier wäre unsigned besser.

  pinMode (seilSch, INPUT);
  pinMode (endSchO, INPUT);
  pinMode (endSchU, INPUT);
  pinMode (extSch, INPUT);
  pinMode (lichtSchr, INPUT);

Was passiert bei Kabelbruch? Wäre INPUT_PULLUP eine sinnvolle Alternative? Sind die Enschalter Öffner?

previousMillis = currentMillis;

Wäre das nicht auch in start sinnvoll?

Wo hast Du NotAus vorgesehen? Schaltet NotAus die Motoren per Relais spannungfrei? Wird die Mechanik dann gebremst?