Doppelte Buttonbelegung, wie eine Belegung blockieren?

Hi,
ich wollte mir gerne ein kleines Panel mit Tasten, Schaltern und einem Drehencoder für Flugsimulation basteln.
Soweit bin ich bisher gut voran gekommen.
Aktuell habe ich 11 Taster und den Drehencoder. Die 11 Taster sollen später Tasten am Autopiloten bedienen und der Drehencoder einige Werte verändern.

Die Tasten sollen zwei Funktionen bekommen, zumindest einige, die ich mit der Taste des Drehencoders wechseln möchte.

Standardmäßig fungiert jeder Taster als Joystick-Button (mit der ArduinoJoystickLibrary).
Drücke ich aber den Drehencoder, soll diese Funktion ausgesetzt werden. Stattdessen kann ich definierte Tasten drücken, um damit das Joystick-Binding des Drehencoders zu ändern.
Standardbinding für den Encoder sind Buttons 20 und 21. Drücke ich den Encoder und innerhalb von 3 Sekunden dann z.B. Taster 4, so soll nicht Button 4 des Joysticks angesteuert werden, sondern nur das Binding des Encoders auf 22 und 23 geändert werden.

Mein Problem:
Bin ich in diesem Auswahlmodus, nachdem ich den Drehencoder gedrückt habe, und drücke dann eine der Tasten, werden nicht nur die Encoderbindings geändert (das, was soll), sondern auch der Joystick-Button weitergegeben.

Ich hoffe das ist soweit verständlich.

Ich habe bereits versucht, eine Verriegelung zu programmieren. Sprich, wenn der Wahlmodus TRUE ist (isRotary == true), dann soll die Funktion ap_panel() nicht laufen.
Das funktioniert jedoch nicht, weil, sobald der Wahlmodus nach 3 Sekunden oder durch Tastendruck beendet wird, die Funktion ap_panel() wieder gestartet wird.

Sorry, wenn ich hier einiges falsch benenne, ich hoffe jedoch, es ist soweit verständlich, was ich vor habe und wo mein Problem liegt.

Hier noch mein bisheriger Code (ohne Kommentare und in gekürzter Form, nur das wesentliche, wegen der Zeichenbegrenzung von 9000):

void setup() {
  Serial.begin(9600);
  pinMode(ap_buttons, INPUT_PULLUP);
  pinMode(rotaryBtn, INPUT_PULLUP);
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  attachInterrupt(0, doEncoderA, CHANGE);
  attachInterrupt(1, doEncoderB, CHANGE);
  
  digitalWrite(rotaryBtn, HIGH); 
  Joystick.begin();
  
}
 
void pinChange() {
  rotating = true;
 
  if ( rotationPos == -1 ) {
    Joystick.pressButton(rotary_mode_positive);
    delay(50);
    Joystick.releaseButton(rotary_mode_positive);
    rotationPos = 0;
  }
  if ( rotationPos == 1 ) {
    Joystick.pressButton(rotary_mode_negative);
    delay(50);
    Joystick.releaseButton(rotary_mode_negative);
    rotationPos = 0;
  }
}
 
int readButtons_ap(int pin_ap) { 
  int ap_value = analogRead(ap_buttons);
 
  if ( ap_value > 1000 ) {
    ap_result = 99;
  } else if ( ( ap_value >= 10 ) && ( ap_value < 20 ) ) { 
    ap_result = 0;
 
  } else if ( ( ap_value > 550 ) && ( ap_value < 560 ) ) {
    ap_result = 1; 
     
  } else if ( ( ap_value > 717 ) && ( ap_value < 726 ) ) {
    ap_result = 2;
     
  } else if ( ( ap_value > 794 ) && ( ap_value < 804 ) ) {
    ap_result = 3;
     
  } else if ( ( ap_value > 840 ) && ( ap_value < 850 ) ) {
    ap_result = 4;
    
  } else if ( ( ap_value > 871 ) && ( ap_value < 881 ) ) {
    ap_result = 5;
         
  } else if ( ( ap_value > 892 ) && ( ap_value < 902 ) ) {
    ap_result = 6;
    
  } else if ( ( ap_value > 908 ) && ( ap_value < 918 ) ) {
    ap_result = 7;
 
  } else if ( ( ap_value > 920 ) && ( ap_value < 930 ) ) {
    ap_result = 8;
 
  } else if ( ( ap_value > 930 ) && ( ap_value < 940 ) ) {
    ap_result = 9;
 
  } else if ( ( ap_value > 940 ) && ( ap_value < 948 ) ) {
    ap_result = 10;
 
    
  } return ap_result; 
}

void ap_panel() {
  ap_btn_pressed = readButtons_ap(11); 
  
  if ( ap_btn_pressed != ap_old_btn ) { 
     Joystick.setButton(ap_btn_pressed, 1);
    }
    if ( ap_btn_pressed < 99 ) { 
      ap_currentbtn = ap_btn_pressed; 
    } else if ( ap_btn_pressed == ap_no_btn ) { 
    Joystick.releaseButton(ap_currentbtn); 
  }
  ap_old_btn = ap_btn_pressed;
}
 
void rotary_mode() {
  startTime = millis(); 
  if ( ( digitalRead(rotaryBtn) == LOW ) && ( rotaryBtn_pressed == false ) ) {  
    rotaryBtn_pressed = true; 
    endTime = startTime;
 
  }
 
  if ( ( rotaryBtn_pressed == true ) && ( startTime - endTime <= waitTime ) ) { 
      led.Update();
      isRotary = true;
    if ( rotary_apBtn == 2 ) {
      rotary_mode_positive = 20; 
      rotary_mode_negative = 21;
      digitalWrite(RXLED, HIGH);
      isRotary = false;
      } else if ( rotary_apBtn == 3 ) {
        rotary_mode_positive = 22;
        rotary_mode_negative = 23;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
       } else if ( rotary_apBtn == 4 ) {
        rotary_mode_positive = 24;
        rotary_mode_negative = 25;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
       } else if ( rotary_apBtn == 5 ) {
        rotary_mode_positive = 26;
        rotary_mode_negative = 27;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
      } else if ( rotary_apBtn == 9 ) {
        rotary_mode_positive = 28;
        rotary_mode_negative = 29;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
      } else if ( rotary_apBtn == 10 ) { 
        rotary_mode_positive = 30;
        rotary_mode_negative = 31;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
      }  
       
  }
  if ( isRotary == false ) {
    rotaryBtn_pressed = false;
  }
  if ( ( rotaryBtn_pressed == true ) && ( startTime - endTime >= waitTime ) )  { 
     rotaryBtn_pressed = false;    
     isRotary = false;
     }  
}
 
void loop() {
  rotary_mode();
  pinChange();
  ap_panel();
 
  delay(15); 
  
}

Der vollständige Code:

Wie setze ich das nun am besten um?
Mit Delays ist keine Lösung, da dann die Tasten nach dem Delay erkannt werden. Ich möchte nur, dass die Tasten erst dann erkannt werden, wenn diese nach Beenden des Wahlmodus erneut gedrückt wurden.

Gruß,
Jannomag

Setze Deinen Code bitte direkt ins Forum. Benutze dazu Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

Tommy56:
Setze Deinen Code bitte direkt ins Forum. Benutze dazu Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

“The message exceeds the maximum allowed length (9000 characters).”

EDIT: Habe jetzt den Code hinzugefügt, dabei musste ich die Kommentare entfernen und den Code kürzen.

Hallo Jannomag,

(fährst du einen Hannomag? konnte mir das Wortspiel jetzt nicht verkneifen)

Also code in code-Tags hin oder her.
Für alle die den Code selbst kommentieren möchten steht er jetzt in Code-Tags da.
Kannst du den vollen Code bitte noch einmal als Attachment mit Kommentaren posten?
Das erleichtert die Analyse doch sehr.

Was ich noch nicht verstehe ist was mit Button-binding genau gemeint ist.

Kannst du bitte mal ein reduziertes Beispiel mit 2 Buttons und vier Funktionen ganz konkret beschreiben?
Es gibt einen Anfnagszustand Button 1 betätigt "Fahrwerk ausfahren"
was macht du jetzt mit Drehencoder und Encoder-Button um das "Button-Binding" zu ändern und was bedeutet das dann für das ganze? Wie wird die Änderung des Bindings ausgelöst?
Was passiert nach der Veränderung des Bindings ?

viele Grüße Stefan

StefanL38:
Hallo Jannomag,

(fährst du einen Hannomag? konnte mir das Wortspiel jetzt nicht verkneifen)

Kommt aus alten Battlefield-Zeiten, wo ich mit meiner Truppe die Gegner in einem Hanomag aufgemischt habe und die dann Hanomag mit meinem Vornahmen Jan zu Jannomag machten - daher habe ich seit 2002 diesen Nickname.

StefanL38:
Also code in code-Tags hin oder her.
Für alle die den Code selbst kommentieren möchten steht er jetzt in Code-Tags da.
Kannst du den vollen Code bitte noch einmal als Attachment mit Kommentaren posten?
Das erleichtert die Analyse doch sehr.

Hab meinen ersten Post editiert.

StefanL38:
Was ich noch nicht verstehe ist was mit Button-binding genau gemeint ist.

Kannst du bitte mal ein reduziertes Beispiel mit 2 Buttons und vier Funktionen ganz konkret beschreiben?
Es gibt einen Anfnagszustand Button 1 betätigt "Fahrwerk ausfahren"
was macht du jetzt mit Drehencoder und Encoder-Button um das "Button-Binding" zu ändern und was bedeutet das dann für das ganze? Wie wird die Änderung des Bindings ausgelöst?
Was passiert nach der Veränderung des Bindings ?

Die Buttons sind für den Autopiloten.
U.a. gibt es dort Buttons für Höhe (ALT), Heading (HDG), Steig/Sinkrate (VS), Geschwindigkeit (SPD).
Drücke ich einen dieser Buttons, wird mithilfe von Joystick.pressButton (aktuell noch setButton, wird geändert, siehe Zeile 127) ein Joystick-Button getriggert, Beispiel für ALT ist Joystick-Button 4 und für HDG 3.

Dazu gibt es dann den Drehencoder, der ebenfalls Joystick-Buttons triggert, sodass ich das im Simulator in den Einstellungen zuweisen kann.

Für HDG wären das Joystick-Buttons 20 und 21, für ALT 22 und 23.
Diese Belegung wird über die Variablen rotary_mode_positive und rotary_mode_negative festgelegt.

Sinn des ganzen:
Drücke ich den Drehencoder, wird ein "Wahlmodus" gestartet, der mir 3 Sekunden gibt, eine Taste zu drücken, um das Binding des Drehencoders zu ändern.

Ablauf:
Ich drücke Taste 3, um HDG zu aktivieren und Taste 4 um ALT zu aktivieren.
Drehe ich den Drehencoder, lässt sich mit Joystick-Buttons 20 und 21 das Heading im Simulator einstellen.
Dann drücke ich den Drehencoder und innerhalb von 3 Sekunden dann Taste 4.
Nun werden Joystick-Buttons 22 und 23 über den Drehencoder angesteuert, um die Höhe einzustellen.
Dieser Wahlprozess läuft dabei ausschließlich auf dem Arduino ab, nicht im Simulator!

Problem:
Gehe ich in den Wahlmodus und drücke Taste 4, dann wird im Simulator der Joystick-Button 4 erkannt und ALT aktiviert/deaktivert. Das soll nicht passieren! Für die Zeit des Wahlmodus, sollen die Tasten kein "Joystick.pressButton" triggern, welches in der Funktion ap_panel() zu finden ist.

Ist das verständlicher ausgedrückt?

Hallo Jannomag,

dank für den Code. Das Beispiel das du gegen hast ist mir zu kompliziert.
Ich fliege nur Modellflugzeuge. Dafür reicht Gas Höehnruder, Seitenruder, Querruder.
Ich spiele keine Flugsimulatoren deshalb habe ich keine Ahnung davon wie man Flugsimulatioren bedient.

Ich versuche einmal selbst die vermutete Funktionalität zu beschreiben:

Es gibt Button 1 und Button 2
Normalzustand wenn Button 1 gedrückt wird ALT(itude) erhöht. Wenn Button 2 gedrückt wird wird Alt vermindert.
Das bedeutet im Code Wenn Hardware-Button-1 gedrückt wird sendet der Arduino einen Befehl an den PC der im Flugsimulatorprogramm ALT erhöht.

Wenn im Code der Hardware-Button-2 gedrückt wird sendet der Arduino einen Befehl der im Flugsimulatorprogramm ALT vermindert.

Wird der Drehencoder-Button gedrückt => drei Sekunden lang ist ein Wahlmodus aktiv.
Sobald du jetzt zum Beispiel den Hardware-Button 3 drückst wird die Funktion von Hardware-Button 1 und Hardware-Button 2 umgestellt von "ändere ALT" auf "ändere HEADING"

diese Beschreibung trifft jetzt wahrscheinlich nicht zu. Aber sie zeigt das Prinzip wie detailliert und mit wie viel ganz allgemeinverständlichen Worten du es besrcheiben musst damit ich es verstehe.

viele Grüße Stefan

Okay ich versuche es mal einfach zu veranschaulichen.
T = Taste
Btn = Joystick-Button
R+ = Drehencoder rechts
R- = Drehencoder links
RB = Drehencoder Button

T3 drücken --> Btn3
T4 drücken --> Btn4
R+ drehen --> Btn20
R- drehen --> Btn21

RB drücken --> Wahlmodus EIN --> 3 Sekunden Zeit --> T4 drücken --> R+ = Btn22 & R- = Btn23 --> Wahlmodus AUS

R+ drehen --> Btn22
R- drehen --> Btn23

RB drücken --> Wahlmodus EIN --> 3 Sekunden Zeit --> T3 drücken --> R+ = Btn20 & R- = Btn21 --> Wahlmodus AUS

R+ drehen --> Btn20
R- drehen --> Btn21

RB drücken --> 3 Sekunden warten --> Wahlmodus wieder AUS.

Problem:

RB drücken --> Wahlmodsu EIN --> 3 Sekunden Zeit --> T4 drücken --> Btn4 & R+ = Btn22, R- = Btn 23 --> Wahlmodus AUS

Erklärung: Wenn RB gedrückt wurde und dann T4 gedrückt wurde darf Btn4 NICHT getriggert werden.
Btn4 darf nur getriggert werden, wenn der Wahlmodus NICHT aktiv ist.

Ok ich denke jetzt verstehe ich es.

T4 löst im NICHT-Wahlmodus sende Joystick-Btn4 aus. (Ich nehme jetzt mal die Funktion "Fahrwerk ausfahren")

Wenn der Wahlmodus aktiviert ist dann soll ein Druck auf T4 nur die Zuordnung vom Encoder-DREHEN von Zuordnung "A" auf Zuordnung "B" ändern. Aber KEIN Befehl "sende Joystick-Btn4" an PC ausgelöst werden.

Du willst nur das der Drehencoder beim Drehen etwas anderes verstellt. aber die Funktion von T4 "Fahrwerk ausfahren" soll NICHT gesendet werden.

So im Prinzip richtig?

StefanL38:
Ok ich denke jetzt verstehe ich es.

T4 löst im NICHT-Wahlmodus sende Joystick-Btn4 aus. (Ich nehme jetzt mal die Funktion “Fahrwerk ausfahren”)

Wenn der Wahlmodus aktiviert ist dann soll ein Druck auf T4 nur die Zuordnung vom Encoder-DREHEN von Zuordnung “A” auf Zuordnung “B” ändern. Aber KEIN Befehl “sende Joystick-Btn4” an PC ausgelöst werden.

Du willst nur das der Drehencoder beim Drehen etwas anderes verstellt. aber die Funktion von T4 “Fahrwerk ausfahren” soll NICHT gesendet werden.

So im Prinzip richtig?

Jein. Im Wahlmodus soll gar kein Joystick-Befehl gesendet werden, nur die interne Belegung des Drehencoders geändert werden.
Ansonsten soweit korrekt.

OK. In deiner function loop() steht

void loop() {
  rotary_mode();
  pinChange();
  ap_panel();

  delay(15); // small delay is needed  
}

rotary_mode():
checkt ob Encoder encoder-button gedrückt wurde
wenn ja auswerten welche Taster Tx wurde gedrückt. Je nach Tx-nummer setze encoder-dreh-Funktion

ap_panel():
lese evtl. gedrückten hardware-AP-Button ein und sende den entsprechenden Joystick-Button-Befehl an den PC.

pinChange();
Wenn Impulse vom Drehencoder kommen Joystick-Button-Befehle an PC senden.

Also wenn ich das richtig sehe müsste es genügen den Aufruf von ap_panel() in eine If-bedingung zu packen

void loop() {
  rotary_mode();
  pinChange();
  if (!Wahl_modus_aktiv) {
    ap_panel();
  }	

  delay(15); // small delay is needed   
}

Du hast eine boolsche Variable die "rotaryBtn_pressed" heißt
die true gesetzt wird wenn du den encoder-button drückst.

Und wenn die Aktivierungszeit (die drei Sekunden) abgelaufen sind oder wenn Auswahl ausgeführt wurde wieder false
gesetzt wird.

if (!rotaryBtn_pressed)

ist die Kurzform von

if (rotaryBtn_pressed == false)

Das Ausrufezeichen ist der boolesche NOT-operator

!false = true
!true = false

Wenn die Variable "rotaryBtn_pressed" false gesetzt ist liefert der Ausdruck **!**rotaryBtn_pressed true

void loop() {
  rotary_mode();
  pinChange();
  if (!rotaryBtn_pressed) {
    ap_panel();
  }	

  delay(15); // small delay is needed   
}

viele Grüße Stefan

Das scheint nicht zu genügen. Das war gleich mein erster Ansatz, hat aber nicht funktioniert.
Drücke ich im Wahlmodus eine der Tasten, wird auch gleichzeitig der dazugehörige Joystick-Button getriggert.

Ich habe es gerade so “gelöst”:

  if ( ( rotaryBtn_pressed == true ) && ( startTime - endTime <= waitTime ) ) { // if the button was pressed and the time is below waitTime (stock 3000ms, 3sec)...
      led.Update(); // start the JLED Blinker, defined on top of this code
      isRotary = true;
      rotary_apBtn = readButtons_ap(11);
    if ( rotary_apBtn == 2 ) { // read the pressed button from readButtons_ap 
      rotary_mode_positive = 20; // and change the Joystick button for each rotary direction
      rotary_mode_negative = 21;
      digitalWrite(RXLED, HIGH);
      selectionDone = true;
      } else if ( rotary_apBtn == 3 ) {
        rotary_mode_positive = 22;
        rotary_mode_negative = 23;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
       } else if ( rotary_apBtn == 4 ) {
        rotary_mode_positive = 24;
        rotary_mode_negative = 25;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
       } else if ( rotary_apBtn == 5 ) {
        rotary_mode_positive = 26;
        rotary_mode_negative = 27;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
      } else if ( rotary_apBtn == 9 ) {
        rotary_mode_positive = 28;
        rotary_mode_negative = 29;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
      } else if ( rotary_apBtn == 10 ) { // 10 is normally for FLC but used for BARO for testing instead!
        rotary_mode_positive = 30;
        rotary_mode_negative = 31;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
      }    
  }

      
  if ( ( rotaryBtn_pressed == true ) && ( startTime - endTime >= waitTime ) || ( rotary_apBtn == 99 ) &&  ( selectionDone == true ) )  { // if the button is marked as pressed and the waitTime is over, mark the button as unpressed again to stop the wait-for-input timer.
     rotaryBtn_pressed = false;    
     isRotary = false;
     selectionDone == false;
     }
}

“gelöst” deswegen, weil das Timing von 3 Sekunden nicht unterbrochen wird, sobald eine Taste losgelassen (rotary_apBtn = 99) und selectionDone = true ist. Das Blockieren der Tasten, also dass kein Joystick-Button getriggert wird, funktioniert jedoch.

Jetzt wo de es schreibst...
Dann muss es an folgendem liegen.
Der Encoder-button wird gedrückt => Wahlmodus aktiv
Du drückst eine Taste Tx was den Wahlmodus beendet.
Das Programm läuft so schnell durch die loop, das Wahlmodus beendet ist aber der Button immer noch gedrückt ist
und dann wird natürlich ein Joystick-Befehl gesendet.

Das bedeutet dein Programm muss mit das Erzeugen von Joystick-Befehlen so lange unterlassen
bis der gedrückte Button losgelassen wurde.

Bis sich also der Status von "Button gedrückt" auf kein "Button gedrückt" geändert hat.
Es muss also eine Flankenerkennung von gedrückt nach ungedrückt gemacht werden.

viele Grüße Stefan

Nur wie erstelle ich diese Flankenerkennung. Mit normalen Buttons ist das ja kein Problem, aber ich bekomme das nicht mit meinen Tasten hin, da diese ja nicht einfach nur ein Pin auf LOW setzen, sondern anders ausgelesen werden über einen Spannungsteiler.

Eine Alternative zur Flankenerkennung wollte ich einfügen mit:

 if [...] ( rotary_apBtn == 99 ) && ( selectionDone == true )

Jedoch scheint das nicht so richtig zu funktionieren, da der Wahlmodus weiter läuft, auch, wenn diese Bedingung wahr ist.

Und zwar dachte ich mir, wenn die Variable der Taster auf 99 steht, also kein Taster mehr gedrückt ist, gleichzeitig aber bereits etwas gedrückt wurde und "selectionDone" dadurch auf True gesetzt wurde, dann soll das gleiche passieren, wie wenn die Zeit von 3 Sekunden ohne Tastendruck abgelaufen ist. (Siehe mein vorheriger Post).

Dann sollte ich noch dazu sagen, auch wenn man es im Code vielleicht erkennt:

Die 11 Taster sind an A3 über Spannungsteiler mit 6,8kOhm Widerständen angeschlossen. Die Spannung wird ausgelegten, ausgewertet und das Ergebnis über "ap_result" im Int readButtons ausgegeben.

Um Code zu sparen nehme ich für ap_panel() und rotary_mode() die gleiche Buttonfunktion.

hier meine Codeversion die Flankenerkennung machen (soll)
Es wird innerhalb von loop nur am Anfang von loop einmal der evtl. gedrückte Button eingelesen
und ganz am Ende der loop wird ap_old_btn aktualisiert

Ich habe mir die libs Joystick und Jled heruntergeladen. Aber ich bekomme eine Compiler-Fehlermeldung

exit status 1
'Joystick_' does not name a type; did you mean 'Joystick'?

hier meine codeversion mit flankenerkennung
Da ich nicht compilieren und testen konnte könnten da noch syntaktische Fehler und bugs drinstecken
aber das Prinzip kann man - denke ich - erkennen

#include <Joystick.h>
#include <jled.h>

Joystick_ Joystick;



enum PinAssigments { // rotary encoder pins
  encoderPinA = 2,
  encoderPinB = 3,
  
};
#define rotaryBtn 4 // rotary button pin


static boolean rotating = false;
boolean A_set = false;
boolean B_set = false;
volatile int rotationPos = 0;

boolean isRotary = false;

#define ap_buttons A3 // define the pin where the AP buttons are connected to 

// LED for testing
int RXLED = 17; 
auto led = JLed(RXLED).Blink(500, 200).Forever();

int rotary_mode_positive = 20; // standard button for rotary+
int rotary_mode_negative = 21; // standard button for rotary-
bool rotaryBtn_pressed = false;
int rotary_apBtn = 99; // standard value of rotary_apBtn is set to 99 because it's not existant when pressing any button

unsigned long startTime = 0;
unsigned long endTime = 0;
const int waitTime = 3000;

const int NoButton = 99;

int ap_currentbtn  = NoButton;
int ap_old_btn     = NoButton;
int ap_no_btn      = NoButton;
int ap_btn_pressed = 0;
int ap_result = 0;

void setup() {
  Serial.begin(9600); // serial monitor, comment out when not needed
  pinMode(ap_buttons, INPUT_PULLUP);
  pinMode(rotaryBtn, INPUT_PULLUP);
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  attachInterrupt(0, doEncoderA, CHANGE);
  attachInterrupt(1, doEncoderB, CHANGE);
  
  digitalWrite(rotaryBtn, HIGH); // set initial button state for Rotary Button to HIGH
  Joystick.begin(); // begin Joystick library
  
}

void pinChange() {
  rotating = true;

  if ( rotationPos == -1 ) {
    Joystick.pressButton(rotary_mode_positive);
    delay(50);
    Joystick.releaseButton(rotary_mode_positive);
    rotationPos = 0;
  }
  
  if ( rotationPos == 1 ) {
    Joystick.pressButton(rotary_mode_negative);
    delay(50);
    Joystick.releaseButton(rotary_mode_negative);
    rotationPos = 0;
  }
}

int readButtons_ap(int pin_ap) { 
  
int ap_value = analogRead(ap_buttons);

  if ( ap_value > 1000 ) {
    ap_result = NoButton;
  } 
  else if ( ( ap_value >= 10 ) && ( ap_value < 20 ) ) { // if the value is between 10 and 20...
    ap_result = 0;  // set the result to 0 (0 is the first button of Joystick!)

  } 
  else if ( ( ap_value > 550 ) && ( ap_value < 560 ) ) {
    ap_result = 1; 
     
  } 
  else if ( ( ap_value > 717 ) && ( ap_value < 726 ) ) {
    ap_result = 2;
     
  } 
  else if ( ( ap_value > 794 ) && ( ap_value < 804 ) ) {
    ap_result = 3;
     
  } 
  else if ( ( ap_value > 840 ) && ( ap_value < 850 ) ) {
    ap_result = 4;
    
  } else if ( ( ap_value > 871 ) && ( ap_value < 881 ) ) {
    ap_result = 5;
         
  } else if ( ( ap_value > 892 ) && ( ap_value < 902 ) ) {
    ap_result = 6;
    
  } else if ( ( ap_value > 908 ) && ( ap_value < 918 ) ) {
    ap_result = 7;

  } else if ( ( ap_value > 920 ) && ( ap_value < 930 ) ) {
    ap_result = 8;

  } else if ( ( ap_value > 930 ) && ( ap_value < 940 ) ) {
    ap_result = 9;

  } else if ( ( ap_value > 940 ) && ( ap_value < 948 ) ) {
    ap_result = 10;

    
  } return ap_result; // return the result value
}

// Reading the pressed buttons on the AP panel and convert them to Joystick buttons
void ap_panel() {
  ap_btn_pressed = ap_currentbtn; //readButtons_ap(11); // set the ap_ptn_pressed variable to the result from readButtons_ap
  
  if ( ap_btn_pressed != ap_old_btn ) { // if the value of ap_btn_pressed changed...
     Joystick.setButton(ap_btn_pressed, 1); // set the Joystick button (same number as ap_btn_pressed) to 1
    }
    if ( ap_btn_pressed < NoButton ) { // if the value is smaller than 99 store the button value
      ap_currentbtn = ap_btn_pressed; 
    } else if ( ap_btn_pressed == ap_no_btn ) { // if the button value is the same as ap_no_btn (99)...
    Joystick.releaseButton(ap_currentbtn); // ... release the last pressed button
  }
  //ap_old_btn = ap_btn_pressed; // store previous value for the next loop
}


void rotary_mode() {
  startTime = millis(); // starting millis() timer
  rotary_apBtn = ap_currentbtn; //readButtons_ap(11);
  
  if ( ( digitalRead(rotaryBtn) == LOW ) && ( rotaryBtn_pressed == false ) ) {  // if the rotary encoder button is pressed and wasn't before...
    rotaryBtn_pressed = true; // mark the button as pressed
    endTime = startTime; // start the timer
  }

  if ( ( rotaryBtn_pressed == true ) && ( startTime - endTime <= waitTime ) ) { // if the button was pressed and the time is below waitTime (stock 3000ms, 3sec)...
      led.Update(); // start the JLED Blinker, defined on top of this code
      isRotary = true;
    if ( rotary_apBtn == 2 ) { // read the pressed button from readButtons_ap 
      rotary_mode_positive = 20; // and change the Joystick button for each rotary direction
      rotary_mode_negative = 21;
      digitalWrite(RXLED, HIGH);
      isRotary = false;
      } else if ( rotary_apBtn == 3 ) {
        rotary_mode_positive = 22;
        rotary_mode_negative = 23;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
       } else if ( rotary_apBtn == 4 ) {
        rotary_mode_positive = 24;
        rotary_mode_negative = 25;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
       } else if ( rotary_apBtn == 5 ) {
        rotary_mode_positive = 26;
        rotary_mode_negative = 27;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
      } else if ( rotary_apBtn == 9 ) {
        rotary_mode_positive = 28;
        rotary_mode_negative = 29;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
      } else if ( rotary_apBtn == 10 ) { // 10 is normally for FLC but used for BARO for testing instead!
        rotary_mode_positive = 30;
        rotary_mode_negative = 31;
        digitalWrite(RXLED, HIGH);
        isRotary = false;
      }  
       
  }
  
  
  if ( btn_up && isRotary == false ) {
    rotaryBtn_pressed = false;
  }
  
  if ( btn_up && ( rotaryBtn_pressed == true ) && ( startTime - endTime >= waitTime ) )  { // if the button is marked as pressed and the waitTime is over, mark the button as unpressed again to stop the wait-for-input timer.
     rotaryBtn_pressed = false;    
     isRotary = false;
     }  
}


void loop() {
  ap_currentbtn = readButtons_ap(11);

  if ( (ap_old_btn == NoButton)  &&  (ap_currentbtn != NoButton)  ) { //change from no Button pressed to button IS pressed
	  btn_up   = false;
  }

  if ( (ap_old_btn != NoButton)  &&  (ap_currentbtn == NoButton)  ) { //change from Button IS pressed to no button pressed
	  btn_up   = true;
  }
    
  rotary_mode();
  pinChange();
  if (!rotaryBtn_pressed) {
    ap_panel();
  }	

  ap_old_btn = ap_currentbtn;
  
  delay(15); // small delay is needed 
  
}



void doEncoderA() {
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done

  // Test transition, did things really change?
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set )
      // encoderPos += 1;
      rotationPos = 1;

    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB() {
  if ( rotating ) delay (1);
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      //encoderPos -= 1;
      rotationPos = -1;

    rotating = false;
  }
}

Danke.
Ich habe diese Bibliothek benutzt, vielleicht hast du die falsche installiert.

Deinen Code habe ich getestet, jedoch funktioniert er nicht.
Drücke ich eine Taste, bleibt der jeweilige Joystick-Button gedrückt. Woran es liegt, konnte ich jetzt direkt nicht erkennen.

Ich bin allerdings selber auf eine Lösung gekommen, welche ich bereits oben genannt habe.
Jedoch habe ich die Variable “selectionDone” vorher oben in der Funktion auf False gesetzt, was dazu führte, dass diese immer automatisch zurückgesetzt wurde bei jedem Loop-Durchgang.
Ich habe nun die Variable in die if-else-Funktion, damit es nicht passiert.
Siehe hier:

void rotary_mode() {
  startTime = millis(); // starting millis() timer
    rotary_apBtn = 99;
    rotary_apBtn_store = 99; 
    
    
  if ( ( digitalRead(rotaryBtn) == LOW ) && ( rotaryBtn_pressed == false ) ) {  // if the rotary encoder button is pressed and wasn't before...
    rotaryBtn_pressed = true; // mark the button as pressed
    endTime = startTime; // start the timer
    
  }

  if ( ( rotaryBtn_pressed == true ) && ( startTime - endTime <= waitTime ) ) { // if the button was pressed and the time is below waitTime (stock 3000ms, 3sec)...
      led.Update(); // start the JLED Blinker, defined on top of this code
      isRotary = true;
      rotary_apBtn = readButtons_ap(11);
    if ( rotary_apBtn == 2 ) { // read the pressed button from readButtons_ap 
      rotary_mode_positive = 20; // and change the Joystick button for each rotary direction
      rotary_mode_negative = 21;
      digitalWrite(RXLED, HIGH);
      selectionDone = true;
      } else if ( rotary_apBtn == 3 ) {
        rotary_mode_positive = 22;
        rotary_mode_negative = 23;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
       } else if ( rotary_apBtn == 4 ) {
        rotary_mode_positive = 24;
        rotary_mode_negative = 25;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
       } else if ( rotary_apBtn == 5 ) {
        rotary_mode_positive = 26;
        rotary_mode_negative = 27;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
      } else if ( rotary_apBtn == 9 ) {
        rotary_mode_positive = 28;
        rotary_mode_negative = 29;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
      } else if ( rotary_apBtn == 10 ) { // 10 is normally for FLC but used for BARO for testing instead!
        rotary_mode_positive = 30;
        rotary_mode_negative = 31;
        digitalWrite(RXLED, HIGH);
        selectionDone = true;
      }    
  } else {
    selectionDone = false;
  }

      
  if ( ( rotaryBtn_pressed == true ) && ( startTime - endTime >= waitTime ) || ( rotary_apBtn == 99 ) &&  ( selectionDone == true ) )  { // if the button is marked as pressed and the waitTime is over, mark the button as unpressed again to stop the wait-for-input timer.
     rotaryBtn_pressed = false;    
     isRotary = false;
     selectionDone == false;
     }
}

Damit geht’s. Ob’s “sauber” ist, weiß ich nicht…dafür habe ich zu wenig Ahnung von Arduino / C. Wichtig ist, dass es funktioniert.

Hilfe zur Selbsthilfe ist super…und manchmal hilft es auch sehr gut den ganzen Kram nochmal durchzugehen und die Wartezeit auf Antworten mit anderen Dingen zu nutzen.

Nächster Schritt an meinem Panel:
Analog-Achse, Schalter und RGB-LEDs…dann Gehäuse erstellen und drucken und hoffen, dass alles passt