Probleme mehrere gleichzeitige Tastendrücke zu senden

Hallo Leute!

Ich habe letztens mein erstes Projekt mit einem Arduino Leonardo und einigen Cherry MX Switches gestartet, mit denen ich einen Spielecontroller bauen will.

Ich weiß nicht, ob hier schonmal jemand von dem Spiel "Guitar Hero" gehört hat, jedenfalls soll der Controller, den ich bauen möchte, darin benutzbar sein.

So sieht ein originaler Gitarrencontroller aus: Gitarre

Der Controller beinhaltet also fünf bunte Knöpfe, die im Spiel noten darstellen, sowie eine "Strumbar"-Wippe zum anschlagen dieser Noten, ein Tremolo, mit dem man bei lange zu haltenden Noten so genannte "Starpower" auflädt, welche den Score multipliziert, einen Knopf um diese "Starpower" zu aktivieren und einen weiteren Knopf für Optionen.

Oft muss man in dem Spiel mehrere der bunten Knöpfe gleichzeitig gedrückt halten und diese mit der Wippe anschlagen, um einen Akkord zu erzeugen, womit wir auch bei meinem Problem wären: Ich hatte vor die vorinstallierte Keyboard library zu benutzen, allerdings habe ich keine Ahnung, wie ich damit die gewollten Funktionen einbauen kann. Einzelne Tastendrücke zu programmieren ist kein Problem, aber mehrere gleichzeitig zu übertragen scheint deutlich schwieriger zu sein.

Kann mir jemand hier vielleicht damit helfen?

Keyboard.press()
Keyboard.release()
Keyboard.releaseAll()

Ist doch ziemlich offensichtlich wie das mit mehreren Tasten gleichzeitig funktionieren könnte, oder?

Drücke-BLAU, drücke-ROT, drücke-GRUEN, WIPPE-anschlagen, alle loslassen
(oder jeden einzeln, kA ob das einen Unterschied macht).

Ja, in der Theorie ist es ziemlich offensichtlich, allerdings weiß ich halt nicht, wie ich es in code umsetzen soll. Die Befehle der Keyboard library sind mir bewusst, aber ich habe bis jetzt noch kein code Beispiel gesehen, welches mit mehreren Tasten gleichzeitig arbeitet. Würde mich gerne an sowas orientieren.

Scharrelese:
Die Befehle der Keyboard library sind mir bewusst,

Wo ist dann das Problem?

Wo ist dein gescheiterter Versuch?

#include <Keyboard.h>
int pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
int pressed[];
int unpressed[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char output[] = {'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p'};
void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  Keyboard.begin();
}

void loop() {
  checkpressed();
}

int[] checkpressed() {
  int x = 0;
  for( int i = 0 ; i < 10 ; i++) {
    if(digitalRead(pins[i] == HIGH) {
    x++;
    }
    int pressed[] = new int[x];
    for( int n = 0 ; n < 0 ; n++) {
    if(digitalRead(pins[n] == HIGH) {
    }  
    }
  }
  return pressed[];
}

Weiter bin ich bis jetzt nicht gekommen, die Idee dahinter war, alle Pins und alle Zustände in Arrays zu packen, zu prüfen, ob sich in der nächsten runde des Loops etwas an den Zuständen verändert hat und dann für die gedrückten Tasten den press Befehl weiterzuführen und die losgelassenen halt zu releasen.

Schönes Speicherleck dass du da gebaut hast. Wenn du nicht weißt wie man dynamischen Speicher verwendet lass es sein.
Du kannst entweder ein globales Array verwenden oder ein Array als Parameter übergeben. Das Array wird sowieso nie größer als 10 Einträge. Also braucht man auch keinen dynamischen Speicher.

Und auch das ist wieder mal ein Anwendung für ein struct:

int pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
int unpressed[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char output[] = {'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p'};

Dies “Fragment” crasht den Arduino und kümmert sich einen … um Keyboardausgaben.

new ohne delete ist nie eine gute Idee, Benutzung von dynamischem Speicher selten sinnvoll.

Ich würde für so viele Tasten eine Library (wie Bounce2) benutzen, dann könnte man so etwas machen

for (byte i = 0; i<sizeof(output); i++) {
  if (Taste[i].update()) {
    if (Taste[i].fell()) {
      Keyboard.press(output[i]);
    } else {
      Keyboard.release(output[i]);
    }
  }
}

Vielen Dank schon mal für die Hilfe! :grinning: Bounce2 scheint hier wirklich sehr hilfreich zu sein.
Würde mein Code in dieser Form Probleme verursachen?

#include <Bounce2.h>
#include <Keyboard.h>

#define Anzahl 10
const int ButtonPins[Anzahl] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
const char Eingabe[Anzahl] = {'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p'};

Bounce * buttons = new Bounce[Anzahl];

void setup() {

  for (int i = 0; i < Anzahl; i++) {
    buttons[i].attach( ButtonPins[i] , INPUT_PULLUP );
    buttons[i].interval(1);
  }
  Keyboard.begin();
}

void loop() {
  for (int i = 0; i < Anzahl; i++)  {
    if ( buttons[i].update()) {
      if ( buttons[i].fell() ) {
        Keyboard.press(Eingabe[i]);
      }
      else if ( buttons[i].rose() ) {
        Keyboard.release(Eingabe[i]);
      }
    }
  }
}

Ich habe mir dazu die Beispiele angesehen und “bounce_multiple” modifiziert, allerdings verstehe ich noch nicht ganz die Auswirkung von “interval()”. Könnte mir das nochmal jemand erklären?

Setzt die Debounce-Zeit, in dem Fall auf 1 ms.
Warum stellst du daran überhaupt rum?

Scharrelese:
Würde mein Code in dieser Form Probleme verursachen?

Sag du es mir, verursacht dein Kode Probleme?

Naja... es waren in dem Beispielprogramm 25 ms eingestellt und das kam mir dann doch ein bisschen lang vor. :smiley:

Soweit ich das beurteilen kann läuft jetzt alles, wie es soll.
Vielen herzlichen Dank nochmal für die Hilfe! :slight_smile:

Scharrelese:
Naja... es waren in dem Beispielprogramm 25 ms eingestellt und das kam mir dann doch ein bisschen lang vor. :smiley:

Du willst also mehr als 40 Tastendrücke pro Sekunde durchführen
und/oder bist du sicher dass deine Tasten nicht länger als eine ms prellen?

Der gebaute Controller soll ja wie gesagt, nachher im Spiel "Guitar Hero" verwendet werden können.
Hier mal ein kleiner Ausschnitt, wie es da so teilweise aussieht: INSANE GUITAR HERO SONG - EDTRIO 100% FC!!!!!! - YouTube
Hast allerdings recht, großartig viel mehr als 40 Eingaben pro Sekunde sind bei Durchschnittsspielern, wie mir eher unwahrscheinlich. :smiley: Werde das Ganze mal auf gesunde 10 ms ändern. Sollten jemals Probleme damit auftreten kann ich es immer noch im Nachhinein anpassen.
Übrigens steht im Datasheet der Cherry MX Silent Reds, die ich benutze, dass die Bouncezeit geringer, als 5ms ist. Ich denke mal ich bin auf der sicheren Seite :smiley:

debouncing.pdf 2018-04-08.png
Aus A Guide to Debouncing by Jack G. Ganssle