0

I'm building my own quadcopter and going to use an Arduino for this. An Arduino Micro for the quadcopter and an Arduino UNO for the controller. I want to use an USB host shield with a Logitech Extreme 3D Pro to control the quadcopter.

I have the joystick already working with de example from the USB host shield example, but I can't get the values to my void loop(). There is not much information on the internet about this.

The link to the library and example I'm using: https://github.com/felis/USB_Host_Shield_2.0/tree/master/examples/HID/le3dp

.INO file:

/* Simplified Logitech Extreme 3D Pro Joystick Report Parser */

#include <hid.h>
#include <hiduniversal.h>
#include <usbhub.h>

#include "le3dp_rptparser.h"

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

USB                                             Usb;
USBHub                                          Hub(&Usb);
HIDUniversal                                    Hid(&Usb);
JoystickEvents                                  JoyEvents;
JoystickReportParser                            Joy(&JoyEvents);

void setup()
{
  Serial.begin( 115200 );
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
      Serial.println("OSC did not start.");

  delay( 200 );

  if (!Hid.SetReportParser(0, &Joy))
      ErrorMessage<uint8_t>(PSTR("SetReportParser"), 1  );
}

void loop()
{
    Usb.Task();
}

.cpp file:

#include "le3dp_rptparser.h"

JoystickReportParser::JoystickReportParser(JoystickEvents *evt) :
    joyEvents(evt)
{}

void JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
{
    bool match = true;

    // Checking if there are changes in report since the method was last called
    for (uint8_t i=0; i<RPT_GAMEPAD_LEN; i++) {
        if( buf[i] != oldPad[i] ) {
            match = false;
            break;
        }
  }
    // Calling Game Pad event handler
    if (!match && joyEvents) {
        joyEvents->OnGamePadChanged((const GamePadEventData*)buf);

        for (uint8_t i=0; i<RPT_GAMEPAD_LEN; i++) oldPad[i] = buf[i];
    }
}

void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
    Serial.print("X: ");
    PrintHex<uint16_t>(evt->x, 0x80);
    Serial.print(" Y: ");
    PrintHex<uint16_t>(evt->y, 0x80);
    Serial.print(" Hat Switch: ");
    PrintHex<uint8_t>(evt->hat, 0x80);
    Serial.print(" Twist: ");
    PrintHex<uint8_t>(evt->twist, 0x80);
    Serial.print(" Slider: ");
    PrintHex<uint8_t>(evt->slider, 0x80);
    Serial.print(" Buttons A: ");
    PrintHex<uint8_t>(evt->buttons_a, 0x80);
    Serial.print(" Buttons B: ");
    PrintHex<uint8_t>(evt->buttons_b, 0x80);
    Serial.println("");
}

.h file:

#if !defined(__HIDJOYSTICKRPTPARSER_H__)
#define __HIDJOYSTICKRPTPARSER_H__

#include <hid.h>

struct GamePadEventData
{
  union { //axes and hut switch
    uint32_t axes;
    struct {
      uint32_t x : 10;
      uint32_t y : 10;
      uint32_t hat : 4;
      uint32_t twist : 8;      
    };
  };
  uint8_t buttons_a;
  uint8_t slider;
  uint8_t buttons_b;
};

class JoystickEvents
{
public:
    virtual void OnGamePadChanged(const GamePadEventData *evt);
};

#define RPT_GAMEPAD_LEN sizeof(GamePadEventData)/sizeof(uint8_t)

class JoystickReportParser : public HIDReportParser
{
    JoystickEvents      *joyEvents;

  uint8_t oldPad[RPT_GAMEPAD_LEN];

public:
    JoystickReportParser(JoystickEvents *evt);

    virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

#endif // __HIDJOYSTICKRPTPARSER_H__

So I hope that someone can give me a solution how I can get the value out of the code below (I changed the code a bit, so that I get decimal values) into the void loop().

void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
    Serial.print("X: ");
    Serial.print(evt->x);
    Serial.print("\t Y: ");
    Serial.print(evt->y);
    Serial.print("\t Hat Switch: ");
    Serial.print(evt->hat);
    Serial.print("\t Twist: ");
    Serial.print(evt->twist);
    Serial.print("\t Slider: ");
    Serial.print(evt->slider);
    Serial.print("\t Buttons A: ");
    Serial.print(evt->buttons_a);
    Serial.print("\t Buttons B: ");
    Serial.print(evt->buttons_b);
    Serial.println("");
}

I've some experience with Arduino programming but not much.

Please ask me if you need more information.

2 Answers 2

1

The following assumes you are seeing OnGamePadChanged being called.

One way you could get the values you want (GamePadEventData *evt) into the loop() would be to add a member variable to the JoystickEvents class, so that class would become something like

class JoystickEvents
{
public:
    virtual void OnGamePadChanged(const GamePadEventData *evt);
    static GamePadEventData mostRecentEvent;
};

In OnGamePadChanged, make a copy of evt:

GamePadEventData JoystickEvents::mostRecentEvent;
void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
    mostReventEvent = *evt;
}

Now you can read it from loop() using something like

Serial.print("X: ");
Serial.print(JoystickEvents::mostReventEvent.x);

A significant limitation of this method is that it will only keep the most recent event. Maybe that's OK for your purpose.

It's probably not quite what the author of that code had in mind: he probably expected you to do the work in OnGamePadChanged: his implementation of that method looks like an example so you can see how it works.

4
  • I got it working a few minutes ago in a bit different way. I will post my way as an anwer so other people can find and use it. I'm also going to try it your way so I can learn from it. Thank you!
    – Jeroen
    Dec 6, 2016 at 18:48
  • I've tried it but I get an error which says: base operand of ' ->' has non-pointer type 'GamePadEventData'. Do you know what I did wrong?
    – Jeroen
    Dec 7, 2016 at 15:33
  • My typo, now fixed. I used -> instead of .. The former is used if you have a pointer to an object, the latter is you have the object itself. (In future it would be useful to indicate the line where the error occurred.)
    – Mark Smith
    Dec 7, 2016 at 15:38
  • Sorry, my bad. I got it working right now. Thank you very much!
    – Jeroen
    Dec 7, 2016 at 15:42
0

After trying some other things I came up with the following idea:

I moved this piece of code from the .cpp file to the .INO file:

void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
    Serial.print("X: ");
    PrintHex<uint16_t>(evt->x, 0x80);
    Serial.print(" Y: ");
    PrintHex<uint16_t>(evt->y, 0x80);
    Serial.print(" Hat Switch: ");
    PrintHex<uint8_t>(evt->hat, 0x80);
    Serial.print(" Twist: ");
    PrintHex<uint8_t>(evt->twist, 0x80);
    Serial.print(" Slider: ");
    PrintHex<uint8_t>(evt->slider, 0x80);
    Serial.print(" Buttons A: ");
    PrintHex<uint8_t>(evt->buttons_a, 0x80);
    Serial.print(" Buttons B: ");
    PrintHex<uint8_t>(evt->buttons_b, 0x80);
    Serial.println("");
}

After I changed some of the code in the .INO file the code looks like this:

/* Simplified Logitech Extreme 3D Pro Joystick Report Parser */

#include <hid.h>
#include <hiduniversal.h>
#include <usbhub.h>

#include "le3dp_rptparser.h"

int joyX = 512;       // min: 0 | max: 1023 | center: 512
int joyY = 512;       // min: 0 | max: 1023 | center: 512
int joyHat = 8;       // up: 0 | right-up: 1 | right: 2 | right-down: 3 | down: 4 | left-down: 5 | left: 6 | left-up: 7 | center: 8
int joyTwist = 128;   // min: 0 | max: 255 | center: 128
int joySlider = 0;    // min: 0 | max: 255
int joyButtonsA = 0;
int joyButtonsB = 0;

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

USB                                             Usb;
USBHub                                          Hub(&Usb);
HIDUniversal                                    Hid(&Usb);
JoystickEvents                                  JoyEvents;
JoystickReportParser                            Joy(&JoyEvents);

void setup()
{
  Serial.begin( 115200 );
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
      Serial.println("OSC did not start.");

  delay( 200 );

  if (!Hid.SetReportParser(0, &Joy))
      ErrorMessage<uint8_t>(PSTR("SetReportParser"), 1  );
}

void loop()
{
    //Print all the values into the serial monitor:
    Usb.Task();
    Serial.print("X: ");
    Serial.print(joyX);
    Serial.print("\t Y: ");
    Serial.print(joyY);
    Serial.print("\t Hat: ");
    Serial.print(joyHat);
    Serial.print("\t Twist: ");
    Serial.print(joyTwist);
    Serial.print("\t Slider: ");
    Serial.print(joySlider);
    Serial.print("\t Buttons A: ");
    Serial.print(joyButtonsA);
    Serial.print("\t Buttons B: ");
    Serial.println(joyButtonsB);
}

void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
  //Put the values from the joystick in another variable:
  joyX = evt->x;
  joyY = evt->y;
  joyHat = evt->hat;
  joyTwist = evt->twist;
  joySlider = evt->slider;
  joyButtonsA = evt->buttons_a;
  joyButtonsB = evt->buttons_b;
}

It works very good for me and I'm now able to send the values to another Arduino with the nRF24L01+.

Not the answer you're looking for? Browse other questions tagged or ask your own question.