(Gelöst) USB HOST library - Wie bekomme ich Joystickdaten in den Loop?

Hallo,

ich habe einen Joystick und einen USB-Host Shield und einen Mega2560. Dann gibt's die für den USB_Host_Shield_2.0-master.lib Library mit zahllosen Beispielen. Davon das Beispiel USBHIDjoystick. Dabei sind aber die Joystickfunktionen in einer hidjoystickparser.cpp und einer hidjoystickparser.h datei in Klassen isoliert.
Im Loop wird nur eine Usb.Task() aufgerufen.
Wie aber kann ich jetzt im Loop z.B. abrufen ob eine Taste gedrückt wurde oder einen Joystickausschlag auslesen?
In der hidjoystickparser.cpp kann ich zwar im Code Dinge tun, aber beispielsweise keine globalen Variablen setzen. Das wäre ja auch nicht schön. Aber wie mache ich das? Schreibe ich mir eine Getter-Methode in der Klasse? Ich habe in diversen Foren dafür noch keine Lösung gefunden.

Es wird im Mainprogramm ja auch eine Usb Instanz geschaffen, eine Hid und eine Joy Instanz.
Aber so Dinge wie Joy.OnButtonDn() oder Hid.OnButtonDn(1) funktionieren nicht.

Wäre sehr für eure Hilfe dankbar. Also einfach in dem Beispiel USBHIDjoystick der USB_Host_Shield_2.0-master library im Loop auf die Joystickwerte zugreifen oder die Ereignisse abfangen.

Hi

Hast Du Dir die Library Mal angeschaut?
Wenn dort keine Methoden enthalten sind, Die Dich auf so schnöden Kram wie 'OnButtonDn' zugreifen lassen, geht's so nicht ;).
Ein Link zur Lib wäre bestimmt nicht schlecht, wobei sich Das schon nach einer ausgefressenen Code-Ansammlung anhört, wo nicht Mal der kreative Kopf des Progger auf Alles eine Antwort weiß.

Mein Weg wäre, den ganzen Code der Library nach Methoden durchzusuchen, Die man irgendwie benutzen kann.

Davon ab: globale Variablen kannst Du überall und von Jedem setzen/verändern - deshalb sind die Dinger ja global, daß man von überall dran kommt.

MfG

Danke.

Wenn ich mir im Mainfile (also das mit dem loop() ) eine globale Variable definiere und in den Methoden der .cpp darauf zugreifen will, kennt er die dort nicht. Hat mich auch gewundert, aber ich bin ja kein Compiler. Oder muss ich die noch irgendwie explizit als global definieren?

So etwas in der Art:

byte AAA  // hätte gedacht das ist eine globale Variable

wenn's nicht innerhalb irgendwelcher geschwungener Klammern {} definiert wird.
Oder muss man so etwas machen wie:

global byte AAA

???

@library. Die gehört zum allgemeinen Kanon. Das ist keine exotische Sache, sondern quasi die Offizielle, die man über den Libraymanager beziehen kann. Aber hier gibt's wohl etwas: GitHub Verstehe nicht, wieso das nicht besser im Beispiel erklärt wird. Andere Beispiele, wie etwa die PS3-Joystick kommen ganz ohne .cpp und .h Dateien aus.

Von dem, was Du machen willst, habe ich keine Ahnung, geschweige denn praktische Erfahrung. Ich versuche einen Gedankenanstoß.

Im Beispiel Using Logitech Extreme 3D Pro joystick with Arduino HID library wird eine Ausgabe gezeigt, die nicht loop aus le3dp.ino entspringt, sondern in le3dp_rptparser.cpp gebildet wird. Möglicherweise ist also loop nicht die richtige Stelle.

Auf USBHIDJoystick bezogen sehe ich in JoystickEvents::OnGamePadChanged Serial.print("X1: ");, was in der Analogie zum vorherigen Beispiel eine Ausgabe erzeugen sollte. Siehst Du diese?

Genau das ist mein Problem. Diese beiden Beispiele funktionieren wunderbar. Ich kann die Ausgabe auch abändern und habe unter Anderem daraus gelernt, wie das Joystickprotokoll konkret aussieht. Geht alles bestens. Jetzt arbeitet mein Hauptprogramm aber im Loop und macht dort verschiedenste Dinge. Den Joystick will ich nur dazu verwenden dort Dinge zu beeinflussen. Also müsste mein Joystick etwa bei einem Tastendruck eine globale Variable setzen, die ich dann im Loop abfragen kann. Oder ich müsste im Loop über eine Funktion abfragen können ob eine Joysticktaste gedrückt ist.

Mir ist aber nicht klar wie das geht. Wenn ich in der jeweiligen .cpp auf eine globale Variable zugreifen, oder diese beschreiben will, schimpft er, dass diese Variable in diesem Scope nicht definiert ist. :confused:

Natürlich haut dir der Kompiler/Linker den Kram um die Ohren, wenn du Mist baust.

Um eine Variable in mehreren Übersetzungseinheiten Sichtbar/Nutzbar zu bekommen, muss sie in jeder dieser Einheiten als extern deklariert werden und in einer dieser Übersetzungseinheiten definiert werden.

Fertig, dann ist das Thema durch.

Lerntipp:
Suche nach "Übersetzungseinheit C++ extern"

Beispiel, siehe Anhang


Die Lib, besser, der Ersteller der Lib, geht mit mir konform, und sagt "globale Variablen sind Mist.."
Konsequenter weise stellt er/sie/es eine Ereignisgetriebene Umgebung zur Verführung.
Liefert sogar Beispiele mit, wie man das abzuhandeln hat.

Das halte ich für eine gute Idee!

Natürlich ist es schade, dass du nicht verstehst, was sich da abspielt, bzw. wie du es nutzen kannst.

Aber ich kann hier auch keinen C++ Lehrgang mit dir durchziehen.
Ins Besondere, da ich keine Chance habe das auch zu testen, was ich dann erzählen würde.

Lerntipp:
Suche nach "Event driven programming" und "Ereignisorientierte Programmierung"

USBHIDJoystick.zip (2.42 KB)

Danke combie, genau das hatte ich gesucht. (Siehe Anhang im vorigen Post von combie)
Eine globale Variablendeklaration müsste man also in der .cpp-datei machen:

uint8_t globaleButtonVariable = 0; // definition
...
// Dort kann man sie dann auch verwenden ...
    globaleButtonVariable = but_id;

In der .h-datei aber extern wieder machen.

extern uint8_t globaleButtonVariable; // deklaration

Und dann kann man sie in der Main-Datei also im Loop einfach verwenden:

void loop() 
{
        Usb.Task();

 if(globaleButtonVariable)
 {
        Serial.print("Dn: ");
        Serial.println(globaleButtonVariable, DEC);
        globaleButtonVariable = 0; // nachricht konsumieren
 }        
}

Dank an combie!!

Aber sorry, wer kommt schon auf die Idee nach "Übersetzungseinheit C++ extern" zu suchen, wenn er einen Joystick in seinem Projekt einbinden will und sich durch huderte Seiten Beispielcode wühlt. Dass globale Variable nicht die schönste Lösung sind und ich die Ereignisse, die ja offenbar in den Libraries irgendwie definiert sind verwenden wollen würde, hatte ich auch bereits geschrieben. Inzwischen habe ich mir auch solch eine Lösung erarbeitet durch kleinweise Umarbeitung eines funktionierenden Beispiels (in meinem Fall das SRWS1 Steerinwheel.)

Hier für alle, die meine etwas andere Quick&Dirty-Lösung interessiert.
Um den Joystickzustände im Loop abzufragen schreib ich mir public Methoden im JoystickReportParser:

Hier die Ergänzungen im .h-file (die letzten beiden Zeilen kamen hinzu):

class JoystickReportParser : public HIDReportParser {
        JoystickEvents *joyEvents;

        uint8_t oldPad[RPT_GEMEPAD_LEN];
        uint8_t oldHat;
        uint16_t oldButtons;
        uint8_t mode;

public:
        JoystickReportParser(JoystickEvents *evt);

        virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
        virtual uint16_t  getButtons();     // <--- hier wird eine neue Methode definiert.
        virtual uint8_t   getHatswitch();   // <--- hier eine Andere.
};

Im .cpp-file gibt man an, was die neuen Methoden tun sollen:

uint16_t JoystickReportParser::getButtons() {
  return oldButtons;
}

uint8_t JoystickReportParser::getHatswitch() {
  return oldHat>>4;
}

Und dann kann man im loop endlich darauf zugreifen:

JoystickEvents JoyEvents;
JoystickReportParser Joy(&JoyEvents);
// blabla weitere Definitionen ...

setup() {...}

void loop() {
        Usb.Task();

        Serial.print(" Buttons: ");
        Serial.print(Joy.getButtons());
        Serial.print("   Hat: ");
        Serial.println(Joy.getHatswitch());
        delay(200);
}

Das hat natürlich den Nachteil, dass in diesem oben gezeigten Beispielfall ein Tastendruck durch den Rost fällt, wenn die Taste nur ganz kurz (innerhalb der 200ms Delaytime) gedrückt wird. In meinem Anwendungsfall habe ich aber einen 10ms Updatezyklus und da geht nix mehr verloren.

Die Ereignisse stehen weitherhin zur Verfügung, wenn man die benutzen will, aber hier ging es ja darum, wie man die Zustände im Loop verwenden kann.

Natürlich muss man sich noch darum kümmern, dass das Joystick-Protokoll auch richtig ausgelesen wird und in den hier verwendeten Variablen "oldButtons" und "oldHat" das Richtige drinsteht. Das Protokoll ist ja von Joystick zu Joystick verschieden, aber das war hier ja nicht das Thema.

Aber sorry, wer kommt schon auf die Idee nach "Übersetzungseinheit C++ extern" zu suchen, wenn er einen Joystick in seinem Projekt einbinden will und sich durch huderte Seiten Beispielcode wühlt.

Naja...

Es soll auch Leute geben, die die Sprachen, welche sie verwenden, auch verstehen wollen.
Diese Leute kaufen sich meist ein C++ Buch.
Und da drin sollte das haarklein vorgekaut sein.

:sunglasses: Offensichtlich gehörst du nicht dazu. :sunglasses:

Und deiner Argumentation entnehme ich, dass du das auch nicht willst.
:astonished: :astonished: :astonished:

Hi

Der preisgegebenen Lösung entnehme ich aber, daß Er durchaus auf dem rechten Weg ist.
Er hat Sich durch die Lib gewühlt und Sich die für Ihn nötigen Informationen daraus geholt - DAS nenne ich Programmieren.
... allerdings bin ich nur ein DIY-Progger, Der eben auch auf so lustige Sprachen wie C++ getroffen ist, Die Mal so was von Nichts mit den eigenen Anfängen zu tun haben ... Basic auf einem Schneider CPC 464 :slight_smile: (wo auch die ersten Assembler-Befehle die CPU ärgerten)

Mein Vorgehen beim Programmieren ist auch eher problemorientiert - ich ziehe mir keine Enzyklopädie rein, wenn ich glaube, nur wissen zu müssen, wie ein struct aufgebaut sein muß.

Zugegeben: Der Typ mit dem Bücherwissen hat direkt jede Menge 'drum herum' mit im Kopf, was mir wohl später fehlen wird - aber wäre ja schade, wenn Alle gleich wären (und der Büchertyp Seine Bücher nicht mehr kriegen würde :o ).

Viele Wege führen nach Rom

MfG

Derjenige, welcher verstanden hat, was er tut, und warum er es tut, hat die Chance gute stabile Programme zu schreiben.

Die mit Halbwissen, gelernt aus schlechten Codebeispielen und Mundpropaganda, pfuschen nur. Fallen auf die Nase, ohne zu ahnen, warum. Verbreiten in Foren Falschwissen, was man nur wieder mit Mühe und Not, aus den Köpfen der unbedarften Mitleser raus prügeln kann.

Ich nenne das:
"Es sich bequem auf dem Mount Stupid einrichten."

Wer das gerne möchte, meinen Segen er/sie/es hat.
Allerdings sollte man sich dessen dann aber auch sehr bewusst sein!
Aber leider geht das (fast) immer mit dem Gegenteil einher.
Den Effekt nenne ich "unbewusste Inkompetenz"
In der ausgeprägten Form dann bekannt als Dunning-Kruger-Effekt
Hier mal ein Hit dazu: Dunning-Kruger-Blues

Übrigens:
Ein jeder beginnt bei jedem neuen Thema mit der unbewussten Inkompetenz.
Das ist der Normalzustand direkt nach der Geburt.
Die Frage ist nur, ob man sich darauf ausruhen will....

Bücher sind nicht das Allheilmittel.
Aber doch meist besser überprüft, als Forenbeiträge und Videotutorials.

Lieber combie. Ist denn meine Lösung so falsch? Dann freue ich mich über Verbesserungsvorschläge. Die Aufgabe meines Codeschnipsels ist es, die ereignisorientiert entrudelnden Joytickwerte in einem regelmäßigen, zyklischen Task der im Loop läuft zu verwenden. Dabei ist es für mich völlig irrelevant ob eine Taste möglicherweise 3ms früher oder später gedrückt wurde. Die 10ms Zeitauflösung meines Looptasks reicht mir bei Weitem. Die Einfachheit der Lösung steht für mich in diesem Fall im Vordergrund. Für diese kleine Aufgabe gleich sämtliche Bücher von Bjarne Stroustrup auswendig zu lernen erschien mir nicht angebracht. Darum habe ich zunächst in Foren nach Hilfe gesucht, keine gefunden und schließlich selbst diesen Forenbeitrag verfasst und sogar Hilfe bekommen. Unter Anderem von Dir. Danke dafür.

Ok. Herr Professor. Ich fürchte, ich werde dir nicht so bald auf Augenhöhe begegnen können. Ich fürchte aber auch, wenn es dich so verärgert Menschen mit Halbwissen zu begegnen sind Arduinoforen möglicherweise nicht der Ort an dem du dich wohlfühlen wirst.

Ich denke halt, es wäre für andere Leser dieses Forums, die möglicherweise nach einer Lösung für solch eine Aufgabe suchen, hilfreich, wenn sie diese in möglichst knapper Form fänden und sich nicht erst durch 265 Beiträge unserer Konversation durcharbeiten müssten in denen du mich mit strenger Hand durch die Täler der Tränen fürst.

Wenn du also eine bessere Lösung kennst, wäre es hilfreich diese hier hereinzstellen. Gerne verbessere ich dann auch meinen obigen quick and dirty Lösungsvorschlag mit Referenzen auf deine Inputs.

Ich lerne auch gerne dazu, auch durch das Lesen von Büchern. Würdest du mir das Buch nennen, welches dir für mich als geeignet erscheint, könnte ich mir dieses sogar besorgen. Ich lerne aber devinitiv besser aus konkreten Verbesserungsvorschlägen als aus Pamphleten, die man beinahe schon als Beschimpufungen und Beleidigungen verstehen könnte. Und erwiesenermaßen lernen wir alle besser durch praktisches Erproben in Aufgaben und Anwendungen als durch das theoretische Durcharbeiten von Büchern. Wenn du mich darum aus deiner Supportliste streichen willst, sei es dir unbenommen.

Wenn Pamphlet bedeutet, dass dir meine Vorstellung von erfolgreicher Arbeit mit Arduino nicht gefällt, dann stimme ich dir zu, dann ist es ein Pamphlet.

Nein. :slight_smile: Das bedeutet Pamphlet nicht. Deine Vorstellung von erfolgreicher Arbeit mit Arduino wage ich nicht zu beurteilen. Dazu weiß ich darüber schlichtweg zu wenig. Aber ich könnte mir vorstellen, dass du gute Dinge zustande bringst. Ob mir das aber gefällt oder nicht, ist hier nicht von Belang.

Wenn Du keine sicherheitsrelevanten Programme schreibst, dann zitiere ich Serenifly, der mir sinngemäß schrieb: "Ein Programm, das tut, was es soll, ist ein gutes Programm!"

Danke. So sehe ich das auch.