[gelöst] Frage zu geschaltetem Wechselblinker mit Zusatzanforderung

Hallo und ein freundlicher Gruß an alle hier,

für folgende Programmerweiterung eines Wechselblinkers fehlt mir ein vernünftiger Programmieransatz, oder ich finde in meinem Code den Fehler nicht. Eventuell kann und mag mir hier jemand helfen.
Letztlich soll das ganze eine Steuerung für den Bahnübergang einer Modelleisenbahn werden.

Beschreibung IST-Zustand (funktioniert):
Solange ein Schaltkontakt (für Insider: "Kontaktgleis") geöffnet ist, mögen zwei LEDs dunkel bleiben.
Solange der Kontakt geschlossen wird, sollen diese abwechselnd blinken.
Hier mein Code (der Einfachheit halber ohne PWM):

const int LED_re = 6;
const int LED_li = 5;
const int inputPin = 2;        // Eingangspin f. Schalter
int kontakt;

void setup() {
  pinMode(LED_re, OUTPUT);
  pinMode(LED_li, OUTPUT);
  digitalWrite(inputPin,HIGH);  // Internen PullUp für inputPin einschalten
}

void loop() {
  kontakt = digitalRead(inputPin);  //Schaltkontakt einlesen, Achtung: Negative Logik wg. PullUp

  if (kontakt==1){
    LEDstop();  
  }
  else if (kontakt==0){
    Wechselblink();
  }  
}

void LEDstop(){
  digitalWrite(LED_re, LOW);
  digitalWrite(LED_li, LOW);
}

void Wechselblink(){
  digitalWrite(LED_re, HIGH);   
  digitalWrite(LED_li, LOW);
  delay(1000);                  
  digitalWrite(LED_re, LOW);  
  digitalWrite(LED_li, HIGH);
  delay(1000);                  
}

Nun zum SOLL-Zustand:
Schaltkontakt offen wie oben.
Schaltkontakt geschlossen:

  1. Alle LEDs leuchten einmalig für eine Sekunde auf
  2. Erst danach wechselblinken sie, solange bis der Kontakt wieder geöffnet wird.

Mein Versuch hier im "else if" Zweig noch eine while-Schelife einzubauen funktioniert nur zur Hälfte. Das ganze startet wie gewünscht, hört aber nie mehr auf zu blinken.
Hier der Code:

const int LED_re = 6;
const int LED_li = 5;
const int inputPin = 2;        // Eingangspin f. Schalter


void setup() {
  pinMode(LED_re, OUTPUT);
  pinMode(LED_li, OUTPUT);
  digitalWrite(inputPin,HIGH);  // Internen PullUp für inputPin einschalten
}

void loop() {
  int kontakt = digitalRead(inputPin);  //Schaltkontakt einlesen, Achtung: Negative Logik wg. PullUp

  if (kontakt==1){
    LEDstop();  
  }
  else if (kontakt==0){
    LEDstart();
    while (kontakt==0) {
      Wechselblink();
    }
  }
}

void LEDstop(){
  digitalWrite(LED_re, LOW);
  digitalWrite(LED_li, LOW);
}

void Wechselblink(){
  digitalWrite(LED_re, HIGH);   
  digitalWrite(LED_li, LOW);
  delay(1000);                  
  digitalWrite(LED_re, LOW);  
  digitalWrite(LED_li, HIGH);
  delay(1000);                  
}

void LEDstart(){
  digitalWrite(LED_re, HIGH);
  digitalWrite(LED_li, HIGH);
  delay (1000);
  digitalWrite(LED_re, LOW);
  digitalWrite(LED_li, LOW);
}

Für Hinweise bin ich sehr dankbar.

Beste Grüße

Dirk

Ohne das jetzt genau analysiert zu haben: Bau in die while Schleife ein break mit ein

If (digitalRead(inputPin)) break;

Allerdings ist das von vornherein suboptimal programmiert.

VWDerby:
Für Hinweise bin ich sehr dankbar.

Hallo Dirk,

kontakt = digitalRead(inputPin);

fehlt in der while-Schleife.

Wenn Dich die Steuerung von Modellampeln interessiert: Anleitung: Endlicher Automat mit millis()

Der Arduino kommt doch hier nie wieder raus:

    while (kontakt==0) {
      Wechselblink();
    }

Da in Wechselblink() die Variable kontakt nicht verändert wird, bleibt er in der While-Schleife hängen.

Also entweder (quick&dirty) in Wechselblink den Schaltkontakt abfragen, oder, (besser) dem ganzen eine offene Struktur geben.
So dass die loop() ungehindert und schnell durchlaufen wird und immer nur geprüft was als nächster Arbeitsschritt ansteht.

Hilfreich dazu:
BlinkWithoutDelay und
BlinkwithoutDelay - Die Nachtwächtererklärung

Ich hoffe dies ist nur der Anfang. Weil eigentlich benötigst du zwei Kontaktgleise, eins vor und eins nach dem Bahnübergang. Schließlich wird die Verbindung mit jedem Rad hergestellt und es muss ja auch noch blinken wenn der Zug "auf" dem Übergang ist, das Kontaktgleis befindet sich aber "weit" vor dem Übergang.

Also müsste der Programmablauf folgender sein:
• 1. Rad schließt den Kontakt am Kontaktgleis vor dem Übergang und startet die Sperrung des Übergangs
• letztes Rad schließt den Kontakt am Kontaktgleis nach dem Übergang und beendet die Sperrung.

Da Geschwindigkeit und Zuglänge variieren können, müsste in dem Programm auch ein Achszähler eingebaut sein. Sollten diese Variablen nicht vorhanden sein und der Übergang auch nie in entgegengesetzter Richtung überfahren werden, dann kann der Sketch noch anders aussehen. Nach dem ersten Schließen läuft der Blinker für eine gewisse Zeit und endet dann einfach.

Gruß

MiReu

Das ganze hatten wir glaube ich vor längerer Zeit schon mal durchgekaut und ne gute Lösung gefunden

Vielen Dank an alle. Mein vordergründiges Problem ist dank agmue gelöst. Allen weiteren konstruktiven Anregungen werde ich dankbar nachgehen.
Kann man die Diskussion beenden und den Thread auf gelöst setzen?

VWDerby:
Kann man die Diskussion beenden und den Thread auf gelöst setzen?

Nö, wir diskutieren gerne fröhlich weiter ...

Du kannst aber #0 editieren und "[gelöst] Frage zu geschaltetem Wechselblinker mit Zusatzanforderung" als Thema eingeben.

Dann stelle ich mal folgenden Wechselblinker zur Diskussion:

class SimpleTimer
{
  private:
  unsigned long timeStamp;
  bool abgelaufen = true; // default Status: timer abgelaufen

  public:
  void start()
  {
    timeStamp   = millis();
    abgelaufen  = false;
  }

  bool operator()(const unsigned long ablaufZeit) 
  {
    if(!abgelaufen) abgelaufen = millis() - timeStamp >= ablaufZeit;
    return abgelaufen;
  }
};


class Wechselblinker   // ein einfacher endlicher Automat
{
  private:
  const byte rePin;
  const byte liPin;
  const unsigned long zeit;
  enum Schritt : byte {Start,Warte,SchrittA,SchrittB} schritt = Start;
  SimpleTimer timer;

  public:
  Wechselblinker(const byte rePin, const byte liPin, const unsigned long zeit):rePin(rePin),liPin(liPin),zeit(zeit){}
  
  void init() // sollte in setup() aufgerufen werden
  {
    pinMode(rePin, OUTPUT);
    pinMode(liPin, OUTPUT);
  }
  
  void operator()(const bool blinkAnforderung) 
  {
    switch(schritt) // Schrittkette
    {
      case Start:    schritt = Warte;
                     digitalWrite(rePin, LOW);   
                     digitalWrite(liPin, LOW);
                     break;    
                     
      case Warte:    if(blinkAnforderung) //wenn Anforderung erkannt
                     {
                       schritt = SchrittA;
                       digitalWrite(rePin, HIGH);   
                       digitalWrite(liPin, LOW);
                       timer.start();
                     }
                     break;    
                     
      case SchrittA: if(timer(zeit)) // wenn timer abgelaufen
                     { 
                       schritt = SchrittB;
                       digitalWrite(rePin, LOW);  
                       digitalWrite(liPin, HIGH);
                       timer.start(); 
                     }
                     break;    
                     
      case SchrittB: if(timer(zeit)) // wenn timer abgelaufen
                     { 
                        schritt = Start;
                     }
                     break;
    }
  }
};



const byte inputPin = 2;                   // Eingangspin f. Schalter
Wechselblinker wechselblinker(6,5,1000UL); // Wechselblinker Instanz erzeugen




void setup() 
{
  pinMode(inputPin,INPUT_PULLUP);  // Internen PullUp für inputPin einschalten
  wechselblinker.init();
}


void loop() 
{
  wechselblinker(!digitalRead(inputPin)); // Negative Logik wg. PullUp
}

combie:
Dann stelle ich mal folgenden Wechselblinker zur Diskussion:

class SimpleTimer

{
 private:
 unsigned long timeStamp;
 bool abgelaufen = true; // default Status: timer abgelaufen

public:
 void start()
 {
   timeStamp   = millis();
   abgelaufen  = false;
 }

bool operator()(const unsigned long ablaufZeit)
 {
   if(!abgelaufen) abgelaufen = millis() - timeStamp >= ablaufZeit;
   return abgelaufen;
 }
};

class Wechselblinker   // ein einfacher endlicher Automat
{
 private:
 const byte rePin;
 const byte liPin;
 const unsigned long zeit;
 enum Schritt : byte {Start,Warte,SchrittA,SchrittB} schritt = Start;
 SimpleTimer timer;

public:
 Wechselblinker(const byte rePin, const byte liPin, const unsigned long zeit):rePin(rePin),liPin(liPin),zeit(zeit){}
 
 void init() // sollte in setup() aufgerufen werden
 {
   pinMode(rePin, OUTPUT);
   pinMode(liPin, OUTPUT);
 }
 
 void operator()(const bool blinkAnforderung)
 {
   switch(schritt) // Schrittkette
   {
     case Start:    schritt = Warte;
                    digitalWrite(rePin, LOW);  
                    digitalWrite(liPin, LOW);
                    break;    
                   
     case Warte:    if(blinkAnforderung) //wenn Anforderung erkannt
                    {
                      schritt = SchrittA;
                      digitalWrite(rePin, HIGH);  
                      digitalWrite(liPin, LOW);
                      timer.start();
                    }
                    break;    
                   
     case SchrittA: if(timer(zeit)) // wenn timer abgelaufen
                    {
                      schritt = SchrittB;
                      digitalWrite(rePin, LOW);  
                      digitalWrite(liPin, HIGH);
                      timer.start();
                    }
                    break;    
                   
     case SchrittB: if(timer(zeit)) // wenn timer abgelaufen
                    {
                       schritt = Start;
                    }
                    break;
   }
 }
};

const byte inputPin = 2;                   // Eingangspin f. Schalter
Wechselblinker wechselblinker(6,5,1000UL); // Wechselblinker Instanz erzeugen

void setup()
{
 pinMode(inputPin,INPUT_PULLUP);  // Internen PullUp für inputPin einschalten
 wechselblinker.init();
}

void loop()
{
 wechselblinker(!digitalRead(inputPin)); // Negative Logik wg. PullUp
}

Vielen Dank für den Vorschlag. Soweit ich den Code verstehe arbeitet er aber mit einer festen Zeitdauer. Das funktioniert bei der geplanten Anwendung leider nicht.
Gruß Dirk

Stimmt, diese Anforderung ist nicht mit drin:

  1. Alle LEDs leuchten einmalig für eine Sekunde auf

Alle Anforerungen erfüllt.

class SimpleTimer
{
  private:
  unsigned long timeStamp;
  bool abgelaufen = true; // default Status: timer abgelaufen

  public:
  void start()
  {
    timeStamp   = millis();
    abgelaufen  = false;
  }

  bool operator()(const unsigned long ablaufZeit) 
  {
    if(!abgelaufen) abgelaufen = millis() - timeStamp >= ablaufZeit;
    return abgelaufen;
  }
};


class Wechselblinker   // ein einfacher endlicher Automat
{
  private:
  const byte rePin;
  const byte liPin;
  const unsigned long zeit;
  using SprungMarke   = void *;
  SprungMarke schritt = NULL;
  
  SimpleTimer timer;

  public:
  Wechselblinker(const byte rePin, const byte liPin, const unsigned long zeit):rePin(rePin),liPin(liPin),zeit(zeit){}
  
  void init() // sollte in setup() aufgerufen werden
  {
    pinMode(rePin, OUTPUT);
    pinMode(liPin, OUTPUT);
  }
  
  void operator()(const bool blinkAnforderung) 
  {
       if(schritt) goto * schritt;
       
       Start:        schritt = &&BeideAn;
                     digitalWrite(rePin, LOW);   
                     digitalWrite(liPin, LOW);
                     Serial.println("Schrittkette: Start durchlaufen");
                     return;    
                     
       BeideAn:      if(blinkAnforderung) //wenn Anforderung erkannt
                     {
                       schritt = &&BeideAus;
                       digitalWrite(rePin, HIGH);   
                       digitalWrite(liPin, HIGH);
                       timer.start();
                       Serial.println("Schrittkette: Anforderung erkannt, Beide Ein");
                     }
                     return; 
                       
                     
      BeideAus:      if(timer(zeit)) // wenn timer abgelaufen
                     { 
                       schritt = &&AusstiegA;
                       digitalWrite(rePin, LOW);   
                       digitalWrite(liPin, LOW);
                       timer.start();
                       Serial.println("Schrittkette: Anforderung erkannt, Beide Aus");
                     }
                     return;

                      
      AusstiegA:      if(timer(zeit)) // wenn timer abgelaufen
                      { 
                        schritt = blinkAnforderung?&&PhaseA:&&Start;
                        Serial.println("Schrittkette: AusstiegA geprueft");
                      }
                      return; 

      PhaseA:         schritt = &&AusstiegB;
                      digitalWrite(rePin, LOW);  
                      digitalWrite(liPin, HIGH);
                      timer.start(); 
                      Serial.println("Schrittkette: Zeige Bild A");
                      return;    
                      
                     
      AusstiegB:     if(timer(zeit)) // wenn timer abgelaufen
                     { 
                       schritt = blinkAnforderung?&&PhaseB:&&Start;
                       Serial.println("Schrittkette: AusstiegB geprueft");
                     }
                     return;  
                       
      PhaseB:        schritt = &&Ende;
                     digitalWrite(rePin, HIGH);  
                     digitalWrite(liPin, LOW);
                     timer.start(); 
                     Serial.println("Schrittkette: Zeige Bild B");
                     return;   
                     
      Ende:          if(timer(zeit)) // wenn timer abgelaufen
                     { 
                        if(blinkAnforderung)
                        {
                          Serial.println("Schrittkette: Weiter blinken");
                          schritt = &&PhaseA;
                        }else
                        {
                          schritt = &&Start;
                          Serial.println("Schrittkette: Rueckkehr zu Start");
                        }
                     }
                     return;
  }
};

class RunCounter // zeigt die Anzahl der Aufrufe pro Sekunde
{
  private:
  SimpleTimer timer;
  unsigned long count;

  public:
  void init() // sollte in setup() aufgerufen werden
  {
    timer.start();
  }
  
  void operator()()
  {
    count++;
    if(timer(1000))
    {
      Serial.print("Millis: "); Serial.print(millis());
      Serial.print("  --  "); 
      Serial.print("Aufrufe: "); Serial.print(count);
      Serial.println(" pro Sekunde ");
      count = 0;
      timer.start(); 
    }
  } 
};


const byte inputPin = 2;                   // Eingangspin f. Schalter
Wechselblinker wechselblinker(6,5,1000UL); // Wechselblinker Instanz erzeugen
RunCounter runcounter;


void yield() 
{
  wechselblinker(!digitalRead(inputPin)); // Negative Logik wg. PullUp
  runcounter();
}


void setup() 
{
  Serial.begin(9600);
  Serial.println();Serial.println("Start");Serial.println();

  pinMode(inputPin,INPUT_PULLUP);  // Internen PullUp für inputPin einschalten
  wechselblinker.init();
  runcounter.init();
}


void loop() 
{
  delay(4711);
}