Komplexe Taster-Logik inkl. möglicher Tastenkombinationen

Hallo,

ich baue von Zeit zu Zeit kleine Bedienteile mit vielen Tasten und LEDs. Diese Bedienteile geben RS-232 Befehle aus und werten diese auch aus.

Die Geräte, welche gesteuert werden haben mehrere Ebenen, durch die man dann mit den Tasten am Bedienteil navigieren kann.

Je größer und komplexer diese Bedienteile nun werden, desto wilder meine if-Bedinungen um Fehleingaben zu verhindern.

Hinzu kommt, dass ich auch Tastenkombinationen implementiere.

Hier exemplarisch ein kleiner Codeauszug:

/**************************************************  Entprellung usw.  */

t1_old_st = t1_st;  // 3. "neuen Status" speichern
t2_old_st = t2_st;
t3_old_st = t3_st;
t4_old_st = t4_st;
t5_old_st = t5_st;
fz_old_st = fz_st;

t1_Bouncer.update ( );  // 1. Entprellten Zustand "erfragen"
t2_Bouncer.update ( );
t3_Bouncer.update ( );
t4_Bouncer.update ( );
t5_Bouncer.update ( );
fz_Bouncer.update ( );

t1_st = t1_Bouncer.read();  // 2. Tastenstatus aktualisieren
t2_st = t2_Bouncer.read();
t3_st = t3_Bouncer.read();
t4_st = t4_Bouncer.read();
t5_st = t5_Bouncer.read();
fz_st = fz_Bouncer.read();

t_st = (t1_st << 0 | t2_st << 1 | t3_st << 2 | t4_st << 3 | t5_st << 4 | fz_st << 5);  // Speichern sämtlicher aktuellen(!) Tastzustände im Byte

/**************************************************  Filterungen  */

e_st = B00011111 & t_st;                          // Eingangstasten rausfiltern (XXXbbbbb)
e_fz_st = B00111111 & t_st;                       // Eingangstasten inkl. Freeze rausfiltern (XXbbbbbb)

}

/**************************************************  Umschaltlogik für Ebenenwechsel  */

void MSR_st_sw()
{
  switch (sc_sel)
  {
  case 1:  // Gerät "1"
    {
      if(IPHD_ready)  // So lange eine gültige Verbindung besteht..
      {
        if(!tp_on && MSR_st != 1)  // ..,kein Testbild aktiviert ist und das Hauptprogramm nicht aktiviert ist..
        {
          Serial.println("\t\t\t\tWechsel zum Hauptprogramm\n");
          sw_time = millis();
          MSR_st = 1;
        }
        if(tp_on && MSR_st != 2)  // ..,ein Testbild aktiviert ist und das Testpattern-Programm nicht aktiviert ist..
        {
          Serial.println("\t\t\t\tWechsel zum Testpattern-Programm\n");
          sw_time = millis();
          MSR_st = 2;
        }
      }
    }
  }
}

if (t1_st != t1_old_st)                                
{                          
  if (t1_st)                                       
  {
    Serial.println("\t\t\t\t\t\tTaste 1 wurde gedrueckt");
    t_hold_time = millis();
    t_hold_st++;
  }
  else if (!t1_st)
  {
    Serial.println("\t\t\t\t\t\tTaste 1 wurde losgelassen");
    t_hold_st--;
  }
}

void t_func_main()
{
  switch(sc_sel)
  {
  case 1:  // Gerät "1"
    {
      if (t_st && sw_time < t_hold_time)  // Wenn mind. ein Knopf gedrückt wird (weil das Byte != 0 ist) und erst nach einem Ebenen-Wechsel ein Knopf gedrückt wurde..
      {
        switch(e_st)
        {
        case B00000001:  // Wenn Taste 1 betätigt wird..
          {
            if(t_hold_st == 1 && !t_act_st)  // ..nur eine Taste gedrückt gehalten wird und diese "Aktion" noch nicht ausgeführt wurde..
            {
              Serial.println("Eingang 1 geschaltet");
              Serial3.print("RTE 1\rFREEZ 0\r");
              e_sw(1, 0, 0, 0, 0);  // ..main_LED_st mit Schaltung für Eingang 1 beschreiben..
              main_LED_st[5] = 0;  // Freeze-LED ausschalten
              t_act_st = 1;
            }
            if(e_fz_st == B00000001 && millis() - t_hold_time >= t_dur)  // Wenn Taste 1 für ca. drei Sekunden gedrückt gehalten wird..
            {
              tp_1_sw();
            }
            if(e_fz_st == B00100001 && millis() - t_hold_time >= t_dur) // Wenn Taste 1 und Freeze für ca. drei Sekunden gedrückt gehalten werden..
            {
              Serial.println("Logocapture Eingang 1");
            }
          }
          break;

Wie man dem Code entnehmen kann, breche ich mir fast Einen dabei ab, die "aktuelle Codeauswertung" (Gibt es hierfür eignetlich eine konkrete Bezeichnung?) dahin zu navigieren, wo ich sie gerne hätte, ohne dass dabei andere Aktionen ausgelöst werden.

Die Tasterzustände werden jeweils in einem Bit gespeichert, die Zustände der einzelnen LEDs jeweils als Byte in einem Array (eine 2x pro Sekunde blinkende LED hat dann z.B. den Wert 2).

Nun zu meiner Frage:

Gibt es bereits mehr oder weniger vorgefertigte Lösungen/ Libraries, die einem etwas von diesen mitunter äußerst komplexen Schaltlogiken abnehmen? Fehlersuche ist teilweise wirklich knallhart. :~

Gruß Chris

"mitunter äußerst komplexe Schaltlogiken" musst du schon selber verstehen :wink:
In der Regel helfen "State Machine" ( endlicher Zustandautomat ) - Ansätze, das Chaos in den Griff zu kriegen.
Das Wort "endlich" sollte dir Hoffnung geben
Ausserdem ist es meist hilfreich, die verschiedenen Ebenen ( Tasten lesen / Logik / Aktionen ) zu trennen.
Du verwendest schon bounce, um das Entprellen auszulagern, geh einen Schritt weiter und pack die ganze Tastengeschichte in ein Objekt, das dir alle für dich erforderlichen Informationen der Taster liefert, aber den Rest für sich behält.
Das kann ein c++ Objekt sein oder nur eine Sammlung von Funktionen in einer Extra - Datei. Wenn du dann noch so wenig wie möglich globale Variable verwendest, sieht hoffenrtlich jede Ebene für sich schonmal einfacher aus.

State Machine ist allerdings ( nach meiner Einschätzing ) eher ein Denkansatz als eine verwendbare Library.
Die Zeit fürs Suchen und Verstehen von etwas Gefundenem kann sinnvoll sein, um einen eigenen Start zu finden. Danach muss es deine eigene Lösung werden. ( Meine Meinung, aber hier haben Leute mit gefundener und kaum verstandener Software schon tolle Sachen zustande gekriegt. Da wünsche ich dir gerne Rückmeldungen mit anderer Meinung)

Fehlersuche ist teilweise wirklich knallhart.

Welcome to the club. Das haben auch andere schon festgestellt.
"Fehlersuche ist zehnmal schwerer als Programmieren. Daher ist jeder Programmierer damit überfordert, seine eigenen Programme zu testen. Das muss einer machen, der zehnmal schlauer ist." Abhilfe: Nur einfachste Programme schreiben.
Große Unternehmen stellen bevorzugt Programmierer ein, die nur einfachste Programme zustande kriegen.

Mit dem Ansatz oben bekommst du zwar mehr Funktionen, aber die sollten einzeln getestet werden können und nur das ( aber genau das ) machen, was sie sollen.
Wenn sie getestet sind, kannst du sie verwenden, ohne jedesmal in ihren Innereien ins Stolpern zu kommen. Selbst wenn eine Funktion nur einmal irgendwo verwendet wird, sollte sie ausgelagert bleiben, wenn sie zu einem anderen Level gehört.

Wenn ein "Spezialfall" an zig Ecken Sonderbehandlung und extra if's braucht, ist am Design was faul!
Eventuell ist auch die Bedien-Logik schon verquer: Üblicherweise sollte der Benutzer eine Idee haben, was das Ding grade macht oder will. Wenn du als Programmierer das nicht klar siehst, wer dann ?
Oder andersrum: Klare Bedienerführung sollte auch klar programmiert werden können.

michael_x:
Die Zeit fürs Suchen und Verstehen von etwas Gefundenem kann sinnvoll sein, um einen eigenen Start zu finden.

Wie wahr.

Danke für den Tipp mit der State-Machine. Hat hier jmd. Erfahrungen mit dieser hier und kann evtl. ein wenig berichten:

http://playground.arduino.cc/code/FiniteStateMachine#Download

Bekomme leider bereits beim Ausführen des Beispiels eine Fehlermeldung.

Gruß Chris

Chris72622:
Bekomme leider bereits beim Ausführen des Beispiels eine Fehlermeldung.

Dann machst du vermutlich was falsch :wink:

Wie soll man dir mit so einer Aussage helfen ?
Hast du

NOTE: Updated version for Arduino1.0x can be found here

beachtet ?

Jetzt ja .

Danke!