0

I'm trying to get two identical joystics (Saitek cyborg 3D) running with the USB-Host-Shield on a Mega 2560 and an USB-Hub.

I already get the event-messages of both of them:

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

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

#include "hidjoystickrptparser.h"

USB Usb;
USBHub Hub(&Usb);
HIDUniversal Hid1(&Usb);    // first Joystick
HIDUniversal Hid2(&Usb);    // second Joystick

JoystickEvents Joy1Events;
JoystickEvents Joy2Events;

JoystickReportParser Joy1(&Joy1Events);
JoystickReportParser Joy2(&Joy2Events);

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 (!Hid1.SetReportParser(0, &Joy1))
                ErrorMessage<uint8_t > (PSTR("SetReportParser1"), 1);
        if (!Hid2.SetReportParser(0, &Joy2))
                ErrorMessage<uint8_t > (PSTR("SetReportParser2"), 1);
}

void loop() {
        Usb.Task();
        Serial.print("\tX1: ");
        PrintHex<uint8_t > (JoystickEvents::mostRecentEvent.X, 0x80);
        Serial.print("\tY1: ");
        PrintHex<uint8_t > (JoystickEvents::mostRecentEvent.Y, 0x80);
        Serial.print("\tX2: ");
        PrintHex<uint8_t > (JoystickEvents::mostRecentEvent.Z1, 0x80);
        Serial.print("\tY2: ");
        PrintHex<uint8_t > (JoystickEvents::mostRecentEvent.Z2, 0x80);
        Serial.print("\tRz: ");
        PrintHex<uint8_t > (JoystickEvents::mostRecentEvent.Rz, 0x80);
        Serial.println("");

}

My question is: How can I distinguish the events from the different joysticks.

I'm trying to get some kind of port-information from the USB-Hub. Unfortunately something like

!hid->GetAddress()

within the JoystickReportParser doesn't do the job as it always returns 0.

Any ideas?

16
  • look at the hub demo
    – jsotola
    Jan 29, 2018 at 8:54
  • That's what I'm doing since several days but I don't get the clue. I get message-events but how do I recognize from which port they do come? In the hub demo there is this function: PrintAllAddresses() which takes a pointer to the device. But it's called within this statement: Usb.ForEachUsbDevice(&PrintAllAddresses);
    – Palmstroem
    Jan 29, 2018 at 9:02
  • In the hub demo there is this function: PrintAllAddresses() which takes a pointer to the device. But it's called within this statement: Usb.ForEachUsbDevice(&PrintAllAddresses); So how would I call something like this from within the JoytickReportParser or the eventhandler OnGamepadChanged ?
    – Palmstroem
    Jan 29, 2018 at 9:08
  • Or how do I get a pointer to the device from which a message-event comes in?
    – Palmstroem
    Jan 29, 2018 at 9:09
  • To be honest. I'm not so familiar with all this pointer arithmetic. All these -> :: ! * . Is still a little bit wired to me.
    – Palmstroem
    Jan 29, 2018 at 9:13

2 Answers 2

1

Not quite the question that was asked but part of the problem.

This is a simple introduction to pointers.

int variable = 42;                              // Create a variable
int* pointer = &variable;                       // Create a pointer to the variable (by getting its address)
*pointer = 99;                                  // Set the address to a new value
printf ("variable has the value %d", variable); // Print 99.

&x get the address of x

*y dereferences the pointer y, which means it 'turns it back to a variable' (this is simplistic view which isn't really true, but will do for now). So you could, if you wanted (which you wouldn't) do this *&z which would be the same as doing z.

Basically think of & and * as a pair of complimentary operators like - and + or * and /. (They aren't operators, but to start with it might help).

The next problem is when you do this

SomeFunction(&variable);

All function parameters in C/C++ are passed by value by default. This means the function gets a copy of them not the actual value. So if you had:

void FunctionOne (int val)
{
    val = 99;
}
int val = 4;
FunctionOne(val);
printf("val = %d", val);   // You will see 4 printed.

If you pass by reference then you will see 99 printed, to pass by reference you can do it one of two ways.

void FunctionTwo (int& valByRef);
void FunctionThree (int* valPointer);

FunctionTwo only works with C++ (It will work with an Arduino).

FunctionThree might cause a segmentation error if NULL is passed in and the function writes to it.

If it helps in my opinion pointers was one of the hardest concepts to grasp in classic C/C++ so don't worry if you don't understand it just have a play with it and see what you can do.

Calling Methods

  • object.method() - Call the function method on the instance called object which is of type X.
  • object->method() - Call the function method on the instance called object which is a pointer of type X.
  • object::method() - call the function method which is a static member of class object (static means it doesn't change any data within an instance of a class)
  • *object->method() - Avoid this like the plague. It either means *(object->method()) or (*object)->method() to know you would need to look at the type of object and the return type of the function. Its easier just to bracket it to remove ambiguity.

As for *object.&method() I've never come across that one, but that doesn't mean it doesn't exist. :)

5
  • Those who don't understand pointers are doomed to program in Javascript the rest of their lives.
    – user31481
    Jan 29, 2018 at 19:39
  • Thank you very much for this explanation. Good explanation! What confuses me is: Sometimes you can access a property by object.method or by object.object.method then by object::method and object->method or object.&method or *object.&object::method and object.@->/!%&wtf$method and so on. These things were not made for human brains to be understood.
    – Palmstroem
    Jan 29, 2018 at 20:00
  • Are you saying I'm not human? :) (See edit) Its learning a new language, and you just have to remember certain rules, the more you use them the more they become ingrained, there is no short cut :( Jan 31, 2018 at 1:07
  • Thank you very much. Superhuman! Any ideas for the original question? I'm inside a signal-handler-function and want to access an attribute of a parent or grand-parent class, but don't know how to find the right method. The documentation at felis.github.io/USB_Host_Shield_2.0/… seems to be quite comprehensive. But I don't get the clue.
    – Palmstroem
    Feb 1, 2018 at 18:11
  • !Hid->GetAddress() always returns 0. Maybe I need to do something like *evt.HIDDevice.Hid->GetAddress() but all trials like that lead to compile errors.
    – Palmstroem
    Feb 1, 2018 at 18:20
0

this could be a possible answer, in Arduino Code, the USB Host Shield 2.0 library is necessary:

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

// Logitech Attack 3 HID report 
struct GamePadEventData { 
    uint8_t X, Y, Z1, Z2, Rz; 
} __attribute__((packed)); 

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

class JoystickReportParser : public HIDReportParser { 
    JoystickEvents *joyEvents; 
    uint8_t joyId; 
    uint8_t oldPad[sizeof(GamePadEventData)]; 
    public: JoystickReportParser(JoystickEvents *evt, uint8_t id) : joyEvents(evt), joyId(id) {} 
    virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); 
}; 

void JoystickReportParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { 
    if (!is_rpt_id && len == sizeof(GamePadEventData)) { 
        // Checking if there are changes in report since the method was last called
        bool match = (sizeof(oldPad) == len) && (memcmp(oldPad, buf, len) == 0); 
        if (!match && joyEvents) { 
            joyEvents->OnGamePadChanged((const GamePadEventData*)buf, joyId); 
            memcpy(oldPad, buf, len); 
        }
    } 
} 
 
void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt, int joystick) { 
    Serial.print("Joystick "); 
    Serial.print(joystick); 
    Serial.print(": X: "); PrintHex<uint16_t>(evt->X, 0x80); 
    Serial.print(" Y: "); PrintHex<uint16_t>(evt->Y, 0x80); 
    Serial.print(" Z1: "); PrintHex<uint8_t>(evt->Z1, 0x80); 
    Serial.print(" Z2: "); PrintHex<uint8_t>(evt->Z2, 0x80); 
    Serial.print(" Rz: "); PrintHex<uint8_t>(evt->Rz, 0x80); 
    Serial.println(); 
} 

USB Usb; 
USBHub Hub(&Usb); 
HIDUniversal Hid1(&Usb); // first Joystick 
HIDUniversal Hid2(&Usb); // second Joystick 

JoystickEvents JoyEvents; 
JoystickReportParser Joy1(&JoyEvents, 1); 
JoystickReportParser Joy2(&JoyEvents, 2); 

void setup() { 
    Serial.begin(115200); 
        
    Serial.println("Start"); 
    
    if (Usb.Init() == -1) { 
        Serial.println("OSC did not start."); 
    } 
    
    delay(200); 
    
    if (!Hid1.SetReportParser(0, &Joy1) || !Hid2.SetReportParser(1, &Joy2)) { 
        ErrorMessage<uint8_t>(PSTR("SetReportParser"), 1); 
    } 

} 

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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