1
  • Intro:

I've been trying (and failing for four entire days straight so far) to get my Atmega32u4 device (Arduino Pro Micro) to emulate an Xbox controller.

It doesn't have to pretend it's an Xbox controller, but I need to communicate with the XInput driver, so emulating an official controller seemed like the best way to start.

  • The problem:

When using the code example XInputPadMicro by Bootsector it gets me exactly halfway. My device can either read OR write from/to the device driver. But not both. Getting both to work is essential to my project.

  • The code:

Device/Configuration descriptor can be found in XInputPadMicro (I haven't changed these).

Configuration changed event: Enabling the "OUT" endpoint will break the "IN" endpoint.

#define JOYSTICK_EPADDR_IN      (ENDPOINT_DIR_IN | 1)
#define JOYSTICK_EPADDR_OUT     (ENDPOINT_DIR_OUT | 1)

void EVENT_USB_Device_ConfigurationChanged(void) 
{
    bool ConfigSuccess = true;

    ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_EPADDR_IN, EP_TYPE_INTERRUPT, 20, 1);

    //If I enable this, the "IN" Endpoint will stop sending data.
    //ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_EPADDR_OUT, EP_TYPE_INTERRUPT, 8, 1);
}

USB control request event:

void EVENT_USB_Device_ControlRequest(void) 
{           
    /* Handle HID Class specific requests */
    switch (USB_ControlRequest.bRequest)
    {
        case HID_REQ_GetReport:
            if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
            {
                Endpoint_ClearSETUP();
                Endpoint_Write_Control_Stream_LE(&gamepad_state, 20);
                Endpoint_ClearIN();
            }
        break;

        case HID_REQ_SetReport:
            if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
            {
                Endpoint_ClearSETUP();
                Endpoint_Read_Control_Stream_LE(&RXData, 8);
                Endpoint_ClearOUT();
            }
        break;
    }
}

HID Task (called every cycle):

void HID_Task(void) 
{
    /* Device must be connected and configured for the task to run */
    if (USB_DeviceState != DEVICE_STATE_Configured)
        return;

    Endpoint_SelectEndpoint(JOYSTICK_EPADDR_OUT);

    if (Endpoint_IsOUTReceived())
    {
        toggle = !toggle;
        SetLED(LED3, toggle);

        Endpoint_Read_Stream_LE(&RXData, 8, NULL);

        SetLED(LED1, RXData[3] > 0 || RXData[4] > 0);

        Endpoint_ClearOUT();
    }

    /* Select the Joystick Report Endpoint */
    Endpoint_SelectEndpoint(JOYSTICK_EPADDR_IN);

    /* Check to see if the host is ready for another packet */
    if (Endpoint_IsINReady()) 
    {   
        /* Write Joystick Report Data */
        Endpoint_Write_Stream_LE(&gamepad_state, 20, NULL);

        /* Finalize the stream transfer to send the last packet */
        Endpoint_ClearIN();
    }
}

Am I missing something crucial? Perhaps about the inner workings of the USB protocol? I am at a loss here.

0 Answers0