All posts by Zachery Littell

XInput Library for Teensyduino

Recently Jeremy Williams from Tested! has been working on his very own controller.  This one emulated another classic arcade giant, the pinball machine.  Running into the same issues I experienced creating a fight-stick, he came across my project and began contacting me.  I was more than ecstatic to work with Jeremy on his project.  I began editing my own fight-stick code to accommodate the needs of his pinball controller.  This went on for a while before I had a large amount of unused code and variables sitting oddly idle on my screen. Making my code clunky and convoluted was not my intention, but it quickly became my reality.  I discussed my concerns and options with Jeremy and he expressed that he was more than willing to rework his code base if I moved to an Xinput library for the teensyduino IDE.  This was my new course of action and I think it turned out really well in the end.

I created lots of useful functions and even multiple ways of doing certain actions based on use case.  I kept adding functionality as Jeremy saw needs for things in his project.  He even suggested lots of features that he didn’t need himself, but just happened to think of while working with the library.  I updated the fight-stick code to work with the new library and pushed it to GIT.  I also fixed compatibility issues people were having with compiling on the latest teensyduino.  I included the new fight-stick code and a simple example in the library and made them accessible using the Example menu option.  Unfortunately, changing the code and switching to using a library nullified a lot of the documentation I did here in my blog posts.  Most of the USB portion holds true, but has just been moved to the library.  I really hope to rewrite the “making of” blog to better suit the library, but for the time being you can either cross reference the code into the new library or get an older version of the fight stick code from GitHub.

My current workload for this project revolves around completing the documentation for the library.  I want to do a nice API guide write-up for using the library.  I really think this update to a library has made my code more accessible for generic use instead of how specific it was before.  I am hoping this allows people to start crafting really neat and innovative controllers, especially for VR use like Jeremy has done!

Check out these links:
MSF FightStick Code Base
MSF Xinput Library Code Base
Jeremy William’s PinSim Controller over at Tested!

 

Thanks
-ZACK-

[MSF] FightStick (TeensyLC XINPUT Controller) – 8

IMG_20150501_192714471

Wrapping it all up!


We finally did it! We have created a custom USB device on the TeensyLC and allowed it to send the packets expected from a X360 controller. We even went as far as to allow it to receive LED commands from the host. This writeup became much longer than expected rather quickly. It also can be a bit dry at times, I admit that. That is partially the reason the writeup took so long to complete. I really wanted to go over everything as much as possible and then allow the reader the ability to skip what they deemed unnecessary. I figured the first few sections would be most useful for the advanced readers since they discuss more of how the controllers work and creating the non standard HID device. I didn’t want to leave any newcomers in the dust during this write up either. Since this project crosses two hobbies, there is a chance that people who have little or no experience with embedded development will end up here because of the fightstick aspect.

I am hoping this code gets ported to many other devices. You just need to learn how the USB stack interfaces with the rest of the code and then the same principles discussed here should apply. This should open the door for a lot of cool controllers to be developed and directly supported by current generation of games on the PC. It would be nice to see people add to this project and make it better. Even while doing this writeup, I noticed many instances where I could have reduced cycle times and plenty of places where I used “magic numbers” instead of defines and constants. One main function I am going to be looking into is possibly switching out the descriptors with PS3 compatible ones. I would like to be able to hold a button while plugging the device in and have it overwrite the current descriptors with descriptors similar to a dualshock 3. Adding PS3 support accessible with a button press would be a great addition to the fightstick.

I hope everyone enjoyed this project and I am excited to see what is done with it. Always feel free to email me links to some of the projects you may have done using the XINPUT code provided here!

Table of Contents

 

[MSF] FightStick (TeensyLC XINPUT Controller) – 7

arduino code

Come on, show us the code already!


Now that we have defined the device and prepared responses for any queries from the PC, the only two things we have left to do is the code for our inputs and the code for our LED patterns. We use two files in the library teensy3 cores directory named usb_xinput.c and usb_xinput.h to set up functions allowing us to send button packets and receive LED packets. We will start with explaining the header file first:

#ifndef USBxinput_h_
#define USBxinput_h_

#include "usb_desc.h"

#if defined(XINPUT_INTERFACE)

#include <inttypes.h>

// C language implementation
#ifdef __cplusplus
extern "C" {
#endif
int usb_xinput_recv(void *buffer, uint32_t timeout);
int usb_xinput_available(void);
int usb_xinput_send(const void *buffer, uint32_t timeout);
#ifdef __cplusplus
}
#endif


// C++ interface
#ifdef __cplusplus
class usb_xinput_class
{
public:
  int available(void) {return usb_xinput_available(); }
  int recv(void *buffer, uint16_t timeout) { return usb_xinput_recv(buffer, timeout); }
  int send(const void *buffer, uint16_t timeout) { return usb_xinput_send(buffer, timeout); }
};

extern usb_xinput_class XInput;

#endif // __cplusplus

#endif // XINPUT_INTERFACE

#endif // USBxinput_h_

As you can see, this is simply the header for the source file we mentioned previously. We check that the correct things are defined and then we start with some forward declarations for the functions we will be using in the C source file. Once those forward declarations are completed, we are going to create a class, usb_xinput_class that will allow us to bundle these functions together for easier access. We label them with shorter names and then declare an external variable XInput to use this class in our source. This allows us to type XInput.available(), XInput.recv(), and XInput.send() instead of their complete names. As you can see, this creates a much more straight forward approach to the functions we will be using.

#include "usb_dev.h"
#include "usb_xinput.h"
#include "core_pins.h" // for yield(), millis()
#include <string.h>    // for memcpy()
//#include "HardwareSerial.h"

#ifdef XINPUT_INTERFACE // defined by usb_dev.h -> usb_desc.h
#if F_CPU >= 20000000

//Function receives packets from the RX endpoint
//We will use this for receiving LED commands
int usb_xinput_recv(void *buffer, uint32_t timeout)
{
  usb_packet_t *rx_packet;
  uint32_t begin = millis();

  while (1) {
    if (!usb_configuration) return -1;
    rx_packet = usb_rx(XINPUT_RX_ENDPOINT);
    if (rx_packet) break;
    if (millis() - begin > timeout || !timeout) return 0;
    yield();
  }
  memcpy(buffer, rx_packet->buf, XINPUT_RX_SIZE);
  usb_free(rx_packet);
  return XINPUT_RX_SIZE;
}

//Function to check if packets are available
//to be received on the RX endpoint
int usb_xinput_available(void)
{
  uint32_t count;

  if (!usb_configuration) return 0;
  count = usb_rx_byte_count(XINPUT_RX_ENDPOINT);
  return count;
}

// Maximum number of transmit packets to queue so we don't starve other endpoints for memory
#define TX_PACKET_LIMIT 3

//Function used to send packets out of the TX endpoint
//This is used to send button reports
int usb_xinput_send(const void *buffer, uint32_t timeout)
{
  usb_packet_t *tx_packet;
  uint32_t begin = millis();

  while (1) {
    if (!usb_configuration) return -1;
    if (usb_tx_packet_count(XINPUT_TX_ENDPOINT) < TX_PACKET_LIMIT) {
      tx_packet = usb_malloc();
      if (tx_packet) break;
    }
    if (millis() - begin > timeout) return 0;
    yield();
  }
  memcpy(tx_packet->buf, buffer, XINPUT_TX_SIZE);
  tx_packet->len = XINPUT_TX_SIZE;
  usb_tx(XINPUT_TX_ENDPOINT, tx_packet);
  return XINPUT_TX_SIZE;
}

#endif // F_CPU
#endif // XINPUT_INTERFACE

You will notice we have some includes at the top to include our header and to give us a few of the useful functions we need such as yield, memcpy, etc. We then check that our xinput_interface is defined and that the CPU clock is greater than or equal to 20MHz. The first function in this source file is usb_xinput_recv. This function grabs a packet if its ready and uses memcpy to copy it over to rx_packet. We use memcpy because its an array of bytes and memcpy allows us to copy the whole array in memory without having to index through the array. The first part of this function starts in a while 1 indefinite loop. If the usb is not configured then the function breaks, returning a -1. If the usb is configured it then uses the USB library function usb_rx to grab a packet from the endpoint we defined as XINPUT_RX_ENDPOINT earlier. If the packet does not equal 0 the while loop breaks. However, if it does equal 0 we will begin using millis to check if we have reached the timeout value we specified when calling the function. If we have not timed out yet we yield and then start the while loop over looking for a new non zero packet. If we do timeout the while loop breaks and the function returns 0. Once a nonzero packet has been received, we copy it to the pointer buffer that we passed to the function and free up rx_packet using the function from the USB library usb_free. Finally we return the predefined value of XINPUT_RX_SIZE stating that we used memcpy to copy that many bytes.

The next function is a simple one that returns a count of how many bytes are available to receive from the endpoint XINPUT_RX_ENDPOINT using the USB library function usb_rx_byte_count(). We will use this function to check if LED pattern data is available to receive before calling usb_xinput_recv().

The last function in this source file is the one that we will use to send button packets to the host, usb_xinput_send(). Similarly to the receive function, we start off in an infinite while loop. If the USB is not configured we return a -1. This line is extremely important in both these functions when you are not running on host power. We do not want to be trying to send or receive packets on the USB bus if we are not configured and enumerated completely with the host. Next we use the function usb_tx_packet_count and pass it our define XINPUT_TX_ENDPOINT to check and make sure that the packet count is less than our TX_PACKET_LIMIT. We do not want overflows or to capture partial packets. If we are less than the packet limit we then use the function usb_malloc() to allocate memory for our packet and pass the pointer to the variable tx_packet. If tx_packet is greater than 0 we break from the while loop, if not we use millis to check if we have reached our timeout. If we have timed out the function returns 0, but if it hasn’t timed out we will yield and then start the while loop over. Once we are out of the while loop, we use memcpy to copy the pointer to the variable we passed that contains our button packet into tx_packet. We then set the length of tx_packet to the predefined value of XINPUT_TX_SIZE. Finally, we use the USB library function usb_tx() to transmit our button packet to the endpoint defined as XINPUT_TX_ENDPOINT and return the value defined as XINPUT_TX_SIZE.

These USB functions were based directly on the examples included in the Teensy installation. They are simple once broken down, but can be confusing at first because of the need and use of pointers by the USB library. Passing the pointers around lets these functions store the received data right where we want it and allows the USB stack to transmit our data directly from where we put it. This creates less convoluted code and a speedier execution. If you aren’t catching on you can attempt to read this a few more times, look up the meaning of some of these functions (memcpy, malloc, etc), or just trust that they work. As always, you can contact me and I can attempt at a better explanation.

//General Declarations
#define MILLIDEBOUNCE 20  //Debounce time in milliseconds
#define NUMBUTTONS 14  //Number of all buttons
#define NUMBUTTONSONLY 10 //Number of just buttons
#define interval 150	//interval in milliseconds to update LED
#define USB_TIMEOUT 12840	//packet timeout for USB

//LED STYLE DEFINES
#define NO_LED 0
#define ONBOARD_LED 1
#define EXTERNAL_LED 2

//LED Pattern Defines
#define ALLOFF 0x00
#define ALLBLINKING 0x01
#define FLASHON1 0x02
#define FLASHON2 0x03
#define FLASHON3 0x04
#define FLASHON4 0x05
#define ON1  0x06
#define ON2  0x07
#define ON3  0x08
#define ON4  0x09
#define ROTATING 0x0A
#define BLINK	 0x0B
#define SLOWBLINK 0x0C
#define ALTERNATE 0x0D

//BUTTON MASK DEFINES
#define R3_MASK 0x80
#define L3_MASK 0x40
#define BACK_MASK 0x20
#define START_MASK 0x10
#define DPAD_RIGHT_MASK 0x08
#define DPAD_LEFT_MASK 0x04
#define DPAD_DOWN_MASK 0x02
#define DPAD_UP_MASK 0x01
#define Y_MASK 0x80
#define X_MASK 0x40
#define B_MASK 0x20
#define A_MASK 0x10
#define LOGO_MASK 0x04
#define RB_MASK 0x02
#define LB_MASK 0x01

//Byte location Definitions
#define BUTTON_PACKET_1 2
#define BUTTON_PACKET_2 3
#define LEFT_TRIGGER_PACKET 4
#define RIGHT_TRIGGER_PACKET 5
#define LEFT_STICK_X_PACKET_LSB 6
#define LEFT_STICK_X_PACKET_MSB 7
#define LEFT_STICK_Y_PACKET_LSB 8
#define LEFT_STICK_Y_PACKET_MSB 9
#define RIGHT_STICK_X_PACKET_LSB 10
#define RIGHT_STICK_X_PACKET_MSB 11
#define RIGHT_STICK_Y_PACKET_LSB 12
#define RIGHT_STICK_Y_PACKET_MSB 13

//Pin Declarations
#define pinUP 5  //Up on stick is pin 5
#define pinDN 6  //Down on stick is pin 6
#define pinLT 7  //Left on stick is pin 7
#define pinRT 8  //Right on stick is pin 8
#define pinB1 9  //Button 1 is pin 9 (Start of top row and across)
#define pinB2 10  //Button 2 is pin 10
#define pinB3 11  //Button 3 is pin 11
#define pinB4 12  //Button 4 is pin 12
#define pinB5 14  //Button 5 is pin 13 (Start of second row and across)
#define pinB6 15  //Button 6 is pin 14
#define pinB7 16  //Button 7 is pin 15
#define pinB8 17  //Button 8 is pin 16
#define pinST 18  //Start Button is pin 17
#define pinSL 19  //Select Button is pin 18
#define pinOBLED 13  //Onboard LED pin

//Position of a button in the button status array
#define POSUP 0
#define POSDN 1
#define POSLT 2
#define POSRT 3
#define POSB1 4
#define POSB2 5
#define POSB3 6
#define POSB4 7
#define POSB5 8
#define POSB6 9
#define POSB7 10
#define POSB8 11
#define POSST 12
#define POSSL 13

The next file we will go over is the header for the actual project. I used a lot of defines in my code and really wanted these outside of the INO file. It starts to make it extremely unruly and lengthy for no reason when these are included in the actual project source file. As you can see we start off with a few more generic definitions. We want to define things like the amount of time we use to debounce the buttons, the number of all inputs, how many of them are only buttons, the interval at which the LED is updated, and the length of time we will use for packet timeout in our USB functions. We then have 3 defines that are used when selecting what style of LED you want to use. The only options I have code for are NO_LED and ONBOARD_LED. This gives you the option of not having any LED or at least using patterns on the single onboard LED to indicate what player the current controller is. The 3rd option for EXTERNAL_LED is meant to eventually be used if you would like to have your own 4 LED ring like an actual controller has. I have not coded this only because my fightstick case has no spot for an LED setup like this and I couldn’t really find any small prototype boards that were cheap and had 4 lights in a circular shape. The next section of defines are all for the LED patterns that are sent from the PC Host. Using these defines allows me to give more meaningful names to these patterns instead of trying to remember which pattern corresponded to 08H. The defines under Button Mask defines are used when setting buttons. These correspond solely to the position of that bit in one of the button packets. For example, DPAD Left is held in the first button packet and is in the 3rd bit position from the right. This means that we want to store it here xxxxxXxx, which corresponds to 0x04 in hex. So when we OR the bit mask with the packet that stores DPAD Left it will set the correct bit to a 1.

FightStick Bitmask Examples 1

The byte location definitions allow us to refer to the index of the bytes in the packet array in a more friendly manner. Instead of making us remember that BUTTON_PACKET_1 is the 3rd byte in the array (0, 1, 2) we can just define it as 2 here and use BUTTON_PACKET_1 from now on. Next up are pin defines. These were discussed previously when wiring up the board and basically just map readable words to these pin numbers. The final portion of defines are used when trying to reference a button in our button status array. We use this array to hold all the current statuses of the buttons as we read them from our controller and then use the array again when setting the correct bits in the packets going to the host. Hopefully those of you who are programming proficient didn’t stop reading after I painfully described some first day lessons on defines. Also I am hoping that those of you who are not proficient now have a better understanding of why defines and header files are used.

/*
    Mechanical Squid Factory presents to you:
    Open Source Fight Stick of Awesomesauce
    Compatible w/ PC
    
    Developer: Zack "Reaper" Littell
    Email: zlittell@gmail.com
    www.zlittell.com
    
    Cheap and awesome were the goals so hopefully that works out
    Uses the Teensy-LC
    
    This tricks the computer into loading the xbox 360 controller driver.
    Then it sends button reports in the same way as the xbox 360 controller.
    Which means this is an example of making the teensy into a XINPUT device :).
    
    This is used in a box with a joystick, 8 buttons on top, and a start select on the front.
    Also used in a similar setup but with a "hit box" layout.
    Press start and select together for xbox logo.
    This should work with steam big picture and stuff.
    
    Attempted to save PWM capable pins for LEDs if needed in the future.
    But some were sacrificed for ease of layout
    
    Would love to eventually move to the FreeScale KDS and off the arduino IDE
    
    Credit where credit is due:
    Paul Stoffregen - for the awesome teensy and all the awesome examples he has included
    Hamaluik.com - for allowing me to not murder the arduino "IDE" out of frustration of hidden "magic"
    BeyondLogic.org - for a great resource on all things USB protocol related
    BrandonW - I contacted him a long time ago for a different project to get log files from his
             - beagle usb 12 between the 360 and controller.  I used them again for verification
             - and understanding during this project. (brandonw.net)
    free60.org - for their page on the x360 gamepad and its lusb output plus the explanations of the descriptors
    Microsoft - Windows Message Analyzer.  It wouldn't have been possible at times without this awesome message
              - analyzer capturing USB packets.  Debugged many issues with enumerating the device using this.
              
    Also one final shoutout to Microsoft... basically **** you for creating xinput and not using HID to do so.
    XINPUT makes signing drivers necessary again, which means paying you.  Also you have ZERO openly available
    documentation on the XUSB device standard and I hate you for that.
*/

//Includes
#include <Bounce.h>
#include "FightStick.h"

//LED STYLE
//NO_LED = Do not use LED
//ONBOARD_LED = use on board LED
//EXTERNAL_LED = in the future add option to use outputs for 4 LEDS
const int LEDSTYLE = ONBOARD_LED;

//Global Variables
byte buttonStatus[NUMBUTTONS];  //array Holds a "Snapshot" of the button status to parse and manipulate
uint8_t TXData[20] = {0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};  //Holds USB transmit packet data
uint8_t RXData[3] = {0x00, 0x00, 0x00};  //Holds USB receive packet data

//LED Toggle Tracking Global Variables
uint8_t LEDState = LOW;	//used to set the pin for the LED
uint32_t previousMS = 0; //used to store the last time LED was updated
uint8_t LEDtracker = 0;	//used as an index to step through a pattern on interval

//LED Patterns
uint8_t patternAllOff[10] = {0,0,0,0,0,0,0,0,0,0};
uint8_t patternBlinkRotate[10] = {1,0,1,0,1,0,1,0,1,0};
uint8_t patternPlayer1[10] = {1,0,0,0,0,0,0,0,0,0};
uint8_t patternPlayer2[10] = {1,0,1,0,0,0,0,0,0,0};
uint8_t patternPlayer3[10] = {1,0,1,0,1,0,0,0,0,0};
uint8_t patternPlayer4[10] = {1,0,1,0,1,0,1,0,0,0};

//Variable to hold the current pattern selected by the host
uint8_t patternCurrent[10] = {0,0,0,0,0,0,0,0,0,0};

//Setup Button Debouncing
Bounce joystickUP = Bounce(pinUP, MILLIDEBOUNCE);
Bounce joystickDOWN = Bounce(pinDN, MILLIDEBOUNCE);
Bounce joystickLEFT = Bounce(pinLT, MILLIDEBOUNCE);
Bounce joystickRIGHT = Bounce(pinRT, MILLIDEBOUNCE);
Bounce button1 = Bounce(pinB1, MILLIDEBOUNCE);
Bounce button2 = Bounce(pinB2, MILLIDEBOUNCE);
Bounce button3 = Bounce(pinB3, MILLIDEBOUNCE);
Bounce button4 = Bounce(pinB4, MILLIDEBOUNCE);
Bounce button5 = Bounce(pinB5, MILLIDEBOUNCE);
Bounce button6 = Bounce(pinB6, MILLIDEBOUNCE);
Bounce button7 = Bounce(pinB7, MILLIDEBOUNCE);
Bounce button8 = Bounce(pinB8, MILLIDEBOUNCE);
Bounce buttonSTART = Bounce(pinST, MILLIDEBOUNCE);
Bounce buttonSELECT = Bounce(pinSL, MILLIDEBOUNCE);

//void Configure Inputs and Outputs
void setupPins()
{
    //Configure the direction of the pins
    //All inputs with internal pullups enabled
    pinMode(pinUP, INPUT_PULLUP);
    pinMode(pinDN, INPUT_PULLUP);
    pinMode(pinLT, INPUT_PULLUP);
    pinMode(pinRT, INPUT_PULLUP);
    pinMode(pinB1, INPUT_PULLUP);
    pinMode(pinB2, INPUT_PULLUP);
    pinMode(pinB3, INPUT_PULLUP);
    pinMode(pinB4, INPUT_PULLUP);
    pinMode(pinB5, INPUT_PULLUP);
    pinMode(pinB6, INPUT_PULLUP);
    pinMode(pinB7, INPUT_PULLUP);
    pinMode(pinB8, INPUT_PULLUP);
    pinMode(pinST, INPUT_PULLUP);
    pinMode(pinSL, INPUT_PULLUP);
    pinMode(pinOBLED, OUTPUT);  
    //Set the LED to low to make sure it is off
    digitalWrite(pinOBLED, LOW);
}

//Update the debounced button statuses
//We are looking for falling edges since the boards are built
//for common ground sticks
void buttonUpdate()
{
  if (joystickUP.update()) {buttonStatus[POSUP] = joystickUP.fallingEdge();}
  if (joystickDOWN.update()) {buttonStatus[POSDN] = joystickDOWN.fallingEdge();}
  if (joystickLEFT.update()) {buttonStatus[POSLT] = joystickLEFT.fallingEdge();}
  if (joystickRIGHT.update()) {buttonStatus[POSRT] = joystickRIGHT.fallingEdge();}
  if (button1.update()) {buttonStatus[POSB1] = button1.fallingEdge();}
  if (button2.update()) {buttonStatus[POSB2] = button2.fallingEdge();}
  if (button3.update()) {buttonStatus[POSB3] = button3.fallingEdge();}
  if (button4.update()) {buttonStatus[POSB4] = button4.fallingEdge();}
  if (button5.update()) {buttonStatus[POSB5] = button5.fallingEdge();}
  if (button6.update()) {buttonStatus[POSB6] = button6.fallingEdge();}
  if (button7.update()) {buttonStatus[POSB7] = button7.fallingEdge();}
  if (button8.update()) {buttonStatus[POSB8] = button8.fallingEdge();}
  if (buttonSTART.update()) {buttonStatus[POSST] = buttonSTART.fallingEdge();}
  if (buttonSELECT.update()) {buttonStatus[POSSL] = buttonSELECT.fallingEdge();}
}

//ProcessInputs
//Button layout on fight stick
//      SL ST
//5  6  7  8
//1  2  3  4
//X360 Verson
//      BK  ST
//X  Y  RB  LB
//A  B  RT  LT
void processInputs()
{
  //Zero out button values
  //Start at 2 so that you can keep the message type and packet size
  //Then fill the rest with 0x00's
  for (int i=2; i<13; i++) {TXData[i] = 0x00;}
  
  //Button Packet 1 (usb data array position 2)
  //SOCD cleaner included
  //Programmed behavior is UP+DOWN=UP and LEFT+RIGHT=NEUTRAL
  //DPAD Up
  if (buttonStatus[POSUP]) {TXData[BUTTON_PACKET_1] |= DPAD_UP_MASK;}
  //DPAD Down
  if (buttonStatus[POSDN] && !buttonStatus[POSUP]) {TXData[BUTTON_PACKET_1] |= DPAD_DOWN_MASK;}
  //DPAD Left
  if (buttonStatus[POSLT] && !buttonStatus[POSRT]) {TXData[BUTTON_PACKET_1] |= DPAD_LEFT_MASK;}
  //DPAD Right
  if (buttonStatus[POSRT] && !buttonStatus[POSLT]) {TXData[BUTTON_PACKET_1] |= DPAD_RIGHT_MASK;}
  
  //Button Start OR Select OR Both (XBOX Logo)
  if (buttonStatus[POSST]&&buttonStatus[POSSL]) {TXData[BUTTON_PACKET_2] |= LOGO_MASK;}
  else if (buttonStatus[POSST]) {TXData[BUTTON_PACKET_1] |= START_MASK;}
  else if (buttonStatus[POSSL]) {TXData[BUTTON_PACKET_1] |= BACK_MASK;}
  
  //Button Packet 2 (usb data array position 3)
  //Button 1
  if (buttonStatus[POSB1]) {TXData[BUTTON_PACKET_2] |= A_MASK;}
  //Button 2
  if (buttonStatus[POSB2]) {TXData[BUTTON_PACKET_2] |= B_MASK;}
  //Button 5
  if (buttonStatus[POSB5]) {TXData[BUTTON_PACKET_2] |= X_MASK;}
  //Button 6
  if (buttonStatus[POSB6]) {TXData[BUTTON_PACKET_2] |= Y_MASK;}
  //Button 7
  if (buttonStatus[POSB7]) {TXData[BUTTON_PACKET_2] |= RB_MASK;}
  //Button 8
  if (buttonStatus[POSB8]) {TXData[BUTTON_PACKET_2] |= LB_MASK;}
  
  //Triggers (usb data array position 4 and 5)
  //0xFF is full scale
  //Button 3
  if (buttonStatus[POSB3]) {TXData[LEFT_TRIGGER_PACKET] = 0xFF;}
  //Button 4
  if (buttonStatus[POSB4]) {TXData[RIGHT_TRIGGER_PACKET] = 0xFF;}
}

//Select pattern
//Examines USB packet and sets the correct pattern
//according to the 3rd byte
/*
Process the LED Pattern
0x00 OFF
0x01 All Blinking
0x02 1 Flashes, then on
0x03 2 Flashes, then on
0x04 3 Flashes, then on
0x05 4 Flashes, then on
0x06 1 on
0x07 2 on
0x08 3 on
0x09 4 on
0x0A Rotating (1-2-4-3)
0x0B Blinking*
0x0C Slow Blinking*
0x0D Alternating (1+4-2+3)*
*Does Pattern and then goes back to previous
*/
void LEDPatternSelect()
{
  //All blinking or rotating
  if((RXData[2]==ALLBLINKING)||(RXData[2]==ROTATING))
  {
    //Copy the pattern array into the current pattern
    memcpy(patternCurrent, patternBlinkRotate, 10);
    //Reset the index to beginning of pattern
    LEDtracker = 0;	
  }
  //Device is player 1
  else if ((RXData[2]==FLASHON1)||(RXData[2]==ON1))
  {
    //Copy the pattern array into the current pattern
    memcpy(patternCurrent, patternPlayer1, 10);
    //Reset the index to beginning of pattern
    LEDtracker = 0;
  }
  //Device is player 2
  else if ((RXData[2]==FLASHON2)||(RXData[2]==ON2))
  {
    //Copy the pattern array into the current pattern
    memcpy(patternCurrent, patternPlayer2, 10);
    //Reset the index to beginning of pattern
    LEDtracker = 0;
  }
  //Device is player 3
  else if ((RXData[2]==FLASHON3)||(RXData[2]==ON3))
  {
    //Copy the pattern array into the current pattern
    memcpy(patternCurrent, patternPlayer3, 10);
    //Reset the index to beginning of pattern
    LEDtracker = 0;
  }
  //Device is player 4
  else if ((RXData[2]==FLASHON4)||(RXData[2]==ON4))
  {
    //Copy the pattern array into the current pattern
    memcpy(patternCurrent, patternPlayer4, 10);
    //Reset the index to beginning of pattern
    LEDtracker = 0;
  }
  //If pattern is not specified perform no pattern
  else
  {
    //Copy the pattern array into the current pattern
    memcpy(patternCurrent, patternAllOff, 10);
    //Pattern is all 0's so we don't care where LEDtracker is at
  }
}

//Cycle through the LED pattern
void LEDPatternDisplay(void)
{
  //Grab the current time in mS that the program has been running
  uint32_t currentMS = millis();
  
  //subtract the previous update time from the current time and see if interval has passed
  if ((currentMS - previousMS)>interval)
  {
    //Set the led state correctly according to next part of pattern
    LEDState = patternCurrent[LEDtracker];
    //update the previous time
    previousMS = currentMS;
    //increment the pattern tracker
    LEDtracker++;
    //write the state to the led
    digitalWrite(pinOBLED, LEDState);
  }
  
  //if we increased ledtracker to 10, it needs to rollover
  if (LEDtracker==10) {LEDtracker=0;}
}

//Setup
void setup() 
{
  setupPins();
}

void loop() 
{
  //Poll Buttons
  buttonUpdate();
  
  //Process all inputs and load up the usbData registers correctly
  processInputs();
  
  //Check for bootloader jump
  if (buttonStatus[POSUP] & buttonStatus[POSB1] & buttonStatus[POSB5] & buttonStatus[POSST] & buttonStatus[POSSL])
  {
    _reboot_Teensyduino_();
  }
  
  //Update Buttons
  XInput.send(TXData, USB_TIMEOUT);
  
  //Check if packet available and parse to see if its an LED pattern packet
  if (XInput.available() > 0)
  {
    //Grab packet and store it in RXData array
    XInput.recv(RXData, USB_TIMEOUT);
    
    //If the data is an LED pattern command parse it
    if(RXData[0] == 1)
    {
      LEDPatternSelect();
    }
  }
  
  //Process the LED pattern if the style is onboard LED
  if (LEDSTYLE == ONBOARD_LED)
  {
    LEDPatternDisplay();
  }
}

We finally made it! We are now ready to go over the code that actually utilizes all the libraries and makes the controller function. The first thing you will see, after my lengthy commented introduction, is a couple of includes. One of them includes Bounce.h so we can use some nice functions to debouce our inputs and the other includes our header file so that we can utilize our nifty definitions. Then we need to set the style of LED we want using a constant integer LEDSTYLE. There might be later releases that do this on a pin, but sadly there’s a lot of extras we could add and not a whole lot of pins to add them to. I didn’t want to have to open the stick up and add any wires, so I just kept it a variable for now. Next up we have an array to hold a snapshot of the current button states, an array we will use for our transmit data, and an array for our received data. The TX array is preloaded with a 0x14 in the 1st index telling the host the size of the packet and a 0x01 in the 2nd index indicating that this is a button packet. The next 3 variables are for our LED control functionality. LEDState is used to hold the state of the LED, previousMS holds the time the LED was last updated, and LEDtracker is used to step through the array holding our pattern. I have simplified the patterns used for the onboard LED to a pattern for off, blinking rotate, and one for each possible player the controller can be set as. With this controller always being plugged in there was no reason to include the patterns the show low battery or that the controller is not connected to/searching for a console. We also need an array to hold the pattern we are currently displaying. The button debouncing section sets up variables of a class known as bounce. There is a variable for each controller input and we simply call the bounce function and pass it the corresponding pin and the millisecond debounce time that we decided on earlier. This allows us to take advantage of arduino’s built-in pin debouncing library by calling the update subroutine of each button class.

The first function in our ino file is setupPins(). This function does exactly as it is titled and uses pinMode to set all of the button pins to input with internal pullups activated. I also set the pin for the onboard LED to output and then use digitalWrite to make it output 0. Next up is the function buttonUpdate, in this function we go through all the bounce variables we set up earlier. For each button we call its update subroutine, which reads the pin and updates its status. If the update routine returns true that means the input has changed and then we set the correct index in buttonStatus to the value returned from calling the fallingEdge() subroutine. Falling edge is used because the buttons are common ground and we want to detect the transition to ground as a 1. Once we have an updated array of button states, we need to reflect those button states in our transmit packets. This activity is done in the next function, processInputs(). First, we start off with a for loop that starts at 2 and goes through our TXData array. This zeroes out everything after the initial bytes that state size of packet and message type. Once the data has been zeroed we start to bitmask in the values of the buttons. We check to see if each button is a 1 and if it is we apply the appropriate bitmask to the byte the holds that buttons status. The only differences here is that for the guide button we check for both start and select being pressed. Also the triggers are in their own packets and are represented with analog values. We will set them either to 0x00 to indicate not pressed or 0xFF to indicate full scale.

FightStick BitMask Examples 2

The function LEDPatternSelect() is called to check the RXData and copy the matching pattern into the patternCurrent variable. This function takes into account the multiple possibilities of patterns being called and combines them into a smaller subset of patterns for use with the onboard LED. The memcpy instruction is used here because of the need to easily copy the entire pattern array from one area to another. We then set LEDtracker to 0 to ensure that we begin stepping through the pattern at its beginning. LEDtracker will be used as an index for displaying the pattern array. LEDPatternDisplay is used to actually display whichever pattern has been loaded into patternCurrent. We use an interval constant and the millis() function to detect how much time has occurred since we last updated the LED’s state. So we take the current time in milliseconds and subtract it from the last changed time and if it is greater than the interval chosen the pattern is advanced a step. LEDState is set to the value held in patternCurrent at the index of LEDtracker, the previous time is updated to the current time, the index is increased, and finally a digitalWrite puts whatever value is in the LEDstate variable on to the onboard LED pin. The last statement of this function checks if LEDtracker is equal to 10 and then if true resets it back to 0 to start the pattern over again. This function uses these methods so that there is no need to use a delay of any sort. This allows our main loop of code to continue to run as fast as possible. As long as we constantly call this function the LED will display the appropriate pattern.

The only two functions we have left to discuss are standard in an arduino sketch. The only thing that setup() does in this sketch is make a call to setupPin(). The loop() function is where all the magic happens. This function will loop infinitely and will be used to place calls to things we want to do constantly as the program runs. We start off by polling the buttons to update their statuses. The next step is to call processInputs to prepare the TX packets for sending. I have included an if statement that allows us to achieve a bootloader jump when UP, Button 1, Button 5, Start, Select are all pressed. This was a problem once we began testing code inside the fightstick cases. You had to open the bottom up and hit the little button on the teensy to get it to restart into the bootloader. The beta testers were not fond of doing that more than once or twice. Next we call our send subroutine and pass it our previously prepared TXData array and the constant USB_TIMEOUT. We have now, in theory, sent the host our button update packet. We now need to check if data is available to be received. The available subroutine is used for this and we check if the value returned is greater than 0. If it is greater than 0, we use the recv subroutine and pass it the array to store the received bytes in, RXData, and our USB_TIMEOUT constant. We then check if the first byte (index 0) is equal to 1 which indicates that this is an LED pattern command from the host. If it is a one we will call LEDPatternSelect to handle copying the respective pattern into currentPattern. Finally, the last portion of our loop function checks to see if the LEDSTYLE we defined is set to ONBOARD_LED. If the style is set to onboard, we then call LEDPatternDisplay to handle displaying the pattern on the LED. However, if the style is not onboard, we just disregard the LED pattern and continue on. So in essence the loop function just continues to update the buttons, set up our TX packets, check for bootloader jump, send our data, receive data, update the current LED pattern, and display the current LED pattern.

Table of Contents

[MSF] FightStick (TeensyLC XINPUT Controller) – 6

475px-USB_Icon.svg

Defining the XINPUT USB Device


Teensyduino is set up to define USB devices through the usb_desc.h file. So we are going to want to add the following code to this file:

usb_desc_h_1

#elif defined(USB_XINPUT)
  #define DEVICE_CLASS	0xFF
  #define DEVICE_SUBCLASS	0xFF
  #define DEVICE_PROTOCOL	0xFF
  #define DEVICE_VERSION 0x0114
  #define DEVICE_ATTRIBUTES 0xA0
  #define DEVICE_POWER	0xFA
  #define VENDOR_ID		0x045e
  #define PRODUCT_ID		0x028e
  #define MANUFACTURER_NAME	{'©','M','i','c','r','o','s','o','f','t'}
  #define MANUFACTURER_NAME_LEN	10
  #define PRODUCT_NAME		{'C','o','n','t','r','o','l','l','e','r'}
  #define PRODUCT_NAME_LEN	10
  #define EP0_SIZE	8
  #define NUM_ENDPOINTS	6
  #define NUM_USB_BUFFERS	24
  #define NUM_INTERFACE	4
  #define XINPUT_INTERFACE	0
  #define XINPUT_RX_ENDPOINT	2
  #define XINPUT_RX_SIZE 3
  #define XINPUT_TX_ENDPOINT	1
  #define XINPUT_TX_SIZE 20
  #define CONFIG_DESC_SIZE 153
  #define ENDPOINT1_CONFIG ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
  #define ENDPOINT3_CONFIG ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY
  #define ENDPOINT5_CONFIG ENDPOINT_TRANSMIT_AND_RECEIVE
  #define ENDPOINT6_CONFIG ENDPOINT_TRANSIMIT_ONLY

I placed these defines after USB_FLIGHTSIM and before the closing #endif. It does not matter where you place it as long as it is somewhere between the USB_SERIAL if defined and the closing end if statement. The first thing you may notice is that there is quite a bit of extra defines used here compared to some of the other devices. We need to add a lot of conditional statements into the USB code so that if these things are defined they are included however if they are not they need to default to what was previously in the library. These exist because our device is not HID nor is it generic. Normally for a HID device the class, subclass, and protocol are all the same, however we need to set them to whats known as Vendor Specific. You will see many more examples of this as we continue. The device class, subclass, and protocol are all set to 0xFF. This signifies to the host that these are vendor specific and handled by a driver. The device version is just a version number specified by the manufacturer. The device attributes are defined as 0xAD. The LSB is irrelevant to us because it is set as reserved in the USB spec. The MSB shows us that the device is bus powered and supports remote wake up. The device power is set to 0xFA, this is in 2mA units. That indicates that the device tells the host it can draw up to 500mA from the bus (0xFA is 250 * 2mA units = 500mA). This seems extremely high and can probably be calculated/measured and lowered, however, I just set it to the same as the 360 controller for simplicity’s sake. This may change in the future since it has caused issues with people not being able to use the device on their keyboards built-in USB hub. The VID and PID need to match something the driver is going to be looking for. The standard 360 controller has a vendor ID of 0x045e (Microsoft) and the product ID is 0x028e. I set the manufacturer name and product name the same as the controller. I don’t think this is necessary, but once again for maximum capability I added it in. Endpoint 0 is a reserved endpoint that is used to perform control transfers. This is what the host uses to probe the device and initialize it. Endpoint 0 has its size set to 8 bytes. We specify that the device has 6 endpoints, 24 buffers for USB, and 4 interfaces. In this project, we only have a real need to utilize interface 0. This interface does all of the button reporting and LED commands. For use later in the code we define interface 0 as XINPUT_INTERFACE. Endpoint 2 will be used as XINPUT_RX_ENDPOINT and will receive LED commands from the host in a packet that is 3 bytes long. Button reports will go out to the host on endpoint 1, which is defined as XINPUT_TX_ENDPOINT and the packets are 20 bytes long. We then define the configuration descriptor size to be 153. Since this project does not require the configuration descriptor to be dynamic, it is easiest to just define that size right here. The last thing we need to do is define the configuration of each endpoint for use later in the code.

The next step in creating this USB device is to write code for telling the host what type of device this is. The standard chain of events that we will see is the host will ask for the device descriptor and read it back until it grabs the length. Once it gets the length it will ask for the device descriptor again, but this time it will ask for the full descriptor specifying the length. It will then determine what configuration is needed (there is only one on this device) and ask for that configuration descriptor. We need to set up these two descriptors and the array of responses to queries from the host. This is done in the USB USB_DESC.c file. Since this file is set up for HID devices, the device descriptor is actually extremely generic. First we will go to where device_descriptor[] is defined and add the following lines:

usb_desc_c_1

// USB Device Descriptor.  The USB host reads this first, to learn
// what type of device is connected.
static uint8_t device_descriptor[] = {
        18,                                     // bLength
        1,                                      // bDescriptorType
        0x00, 0x02,                             // bcdUSB
#ifdef DEVICE_CLASS
        DEVICE_CLASS,                           // bDeviceClass
#else
  0,
#endif
#ifdef DEVICE_SUBCLASS
        DEVICE_SUBCLASS,                        // bDeviceSubClass
#else
  0,
#endif
#ifdef DEVICE_PROTOCOL
        DEVICE_PROTOCOL,                        // bDeviceProtocol
#else
  0,
#endif
        EP0_SIZE,                               // bMaxPacketSize0
        LSB(VENDOR_ID), MSB(VENDOR_ID),         // idVendor
        LSB(PRODUCT_ID), MSB(PRODUCT_ID),       // idProduct

#ifdef DEVICE_VERSION							// bcdDevice
    LSB(DEVICE_VERSION), MSB(DEVICE_VERSION),
#else
        0x00, 0x01,                             
#endif
        1,                                      // iManufacturer
        2,                                      // iProduct
        3,                                      // iSerialNumber
        1                                       // bNumConfigurations
};

Normally the device has no need to specify a device version, however, the X360 does have a specific device version and this is the best way to include it without breaking support for other devices. The next part that we need to change is the configuration descriptor. The first thing we need to add to the configuration descriptor is the ability to define device attributes and device power, like so:

usb_desc_c_2

#ifdef DEVICE_ATTRIBUTES
    DEVICE_ATTRIBUTES,						// bmAttributes
#else
        0xC0,                                   
#endif
#ifdef DEVICE_POWER
    DEVICE_POWER,							// bMaxPower
#else
        50,                                     
#endif

Once we add in device attributes and power, we need to tell the compiler to include our controller descriptor when XINPUT_INTERFACE is defined. This configuration descriptor is gathered directly from the controller. It must match for the driver to work correctly. This tells the host about all the interfaces and endpoints that make up the device. I put comments on each line of the descriptor so that you can follow along and understand what each line means. Now we are going to add the following lines to config_descriptor[].

usb_desc_c_3_1usb_desc_c_3_2usb_desc_c_3_3

#ifdef XINPUT_INTERFACE
//Interface 0
  9,				//bLength (length of interface descriptor 9 bytes)
  4,				//bDescriptorType (4 is interface)
  0,				//bInterfaceNumber (This is interface 0)
  0,				//bAlternateSetting (used to select alternate setting.  notused)
  2,				//bNumEndpoints (this interface has 2 endpoints)
  0xFF,			//bInterfaceClass (Vendor Defined is 255)
  0x5D,			//bInterfaceSubClass
  0x01,			//bInterfaceProtocol
  0,				//iInterface (Index of string descriptor for describing this notused)
  //Some sort of common descriptor? I pulled this from Message Analyzer dumps of an actual controller
  17,33,0,1,1,37,129,20,0,0,0,0,19,2,8,0,0,
  //Endpoint 1 IN
  7,				//bLength (length of ep1in in descriptor 7 bytes)
  5,				//bDescriptorType (5 is endpoint)
  0x81,			//bEndpointAddress (0x81 is IN1)
  0x03,			//bmAttributes (0x03 is interrupt no synch, usage type data)
  0x20, 0x00,		//wMaxPacketSize (0x0020 is 1x32 bytes)
  4,				//bInterval (polling interval in frames 4 frames)
  //Endpoint 2 OUT
  7,				//bLength (length of ep2out in descriptor 7 bytes)
  5,				//bDescriptorType (5 is endpoint)
  0x02,			//bEndpointAddress (0x02 is OUT2)
  0x03,			//bmAttributes (0x03 is interrupt no synch, usage type data)
  0x20, 0x00,		//wMaxPacketSize (0x0020 is 1x32 bytes)
  8,				//bInterval (polling interval in frames 8 frames)
//Interface 1
  9,				//bLength (length of interface descriptor 9 bytes)
  4,				//bDescriptorType (4 is interface)
  1,				//bInterfaceNumber (This is interface 1)
  0,				//bAlternateSetting (used to select alternate setting.  notused)
  4,				//bNumEndpoints (this interface has 4 endpoints)
  0xFF,			//bInterfaceClass (Vendor Defined is 255)
  0x5D,			//bInterfaceSubClass (93)
  0x03,			//bInterfaceProtocol (3)
  0,				//iInterface (Index of string descriptor for describing this notused)
  //A different common descriptor? I pulled this from Message Analyzer dumps of an actual controller
  27,33,0,1,1,1,131,64,1,4,32,22,133,0,0,0,0,0,0,22,5,0,0,0,0,0,0,
  //Endpoint 3 IN
  7,				//bLength (length of ep3in descriptor 7 bytes)
  5,				//bDescriptorType (5 is endpoint)
  0x83,			//bEndpointAddress (0x83 is IN3)
  0x03,			//bmAttributes (0x03 is interrupt no synch, usage type data)
  0x20, 0x00,		//wMaxPacketSize (0x0020 is 1x32 bytes)
  2,				//bInterval (polling interval in frames 2 frames)
  //Endpoint 4 OUT
  7,				//bLength (length of ep4out descriptor 7 bytes)
  5,				//bDescriptorType (5 is endpoint)
  0x04,			//bEndpointAddress (0x04 is OUT4)
  0x03,			//bmAttributes (0x03 is interrupt no synch, usage type data)
  0x20, 0x00,		//wMaxPacketSize (0x0020 is 1x32 bytes)
  4,				//bInterval (polling interval in frames 4 frames)
  //Endpoint 5 IN
  7,				//bLength (length of ep5in descriptor 7 bytes)
  5,				//bDescriptorType (5 is endpoint)
  0x85,			//bEndpointAddress (0x85 is IN5)
  0x03,			//bmAttributes (0x03 is interrupt no synch, usage type data)
  0x20, 0x00,		//wMaxPacketSize (0x0020 is 1x32 bytes)
  64,				//bInterval (polling interval in frames 64 frames)
  //Endpoint 5 OUT (shares endpoint number with previous)
  7,				//bLength (length of ep5out descriptor 7 bytes)
  5,				//bDescriptorType (5 is endpoint)
  0x05,			//bEndpointAddress (0x05 is OUT5)
  0x03,			//bmAttributes (0x03 is interrupt no synch, usage type data)
  0x20, 0x00,		//wMaxPacketSize (0x0020 is 1x32 bytes)
  16,				//bInterval (polling interval in frames 16 frames)
//Interface 2
  9,				//bLength (length of interface descriptor 9 bytes)
  4,				//bDescriptorType (4 is interface)
  2,				//bInterfaceNumber (This is interface 2)
  0,				//bAlternateSetting (used to select alternate setting.  notused)
  1,				//bNumEndpoints (this interface has 4 endpoints)
  0xFF,			//bInterfaceClass (Vendor Defined is 255)
  0x5D,			//bInterfaceSubClass (93)
  0x02,			//bInterfaceProtocol (3)
  0,				//iInterface (Index of string descriptor for describing this notused)
  //Common Descriptor.  Seems that these come after every interface description?
  9,33,0,1,1,34,134,7,0,
  //Endpoint 6 IN
  7,				//bLength (length of ep6in descriptor 7 bytes)
  5,				//bDescriptorType (5 is endpoint)
  0x86,			//bEndpointAddress (0x86 is IN6)
  0x03,			//bmAttributes (0x03 is interrupt no synch, usage type data)
  0x20, 0x00,		//wMaxPacketSize (0x0020 is 1x32 bytes)
  16,				//bInterval (polling interval in frames 64 frames)+
//Interface 3
//This is the interface on which all the security handshaking takes place
//We don't use this but it could be used for man-in-the-middle stuff
  9,				//bLength (length of interface descriptor 9 bytes)
  4,				//bDescriptorType (4 is interface)
  3,				//bInterfaceNumber (This is interface 3)
  0,				//bAlternateSetting (used to select alternate setting.  notused)
  0,				//bNumEndpoints (this interface has 0 endpoints ???)
  0xFF,			//bInterfaceClass (Vendor Defined is 255)
  0xFD,			//bInterfaceSubClass (253)
  0x13,			//bInterfaceProtocol (19)
  4,				//iInterface (Computer never asks for this, but an x360 would. so include one day?)
  //Another interface another Common Descriptor
  6,65,0,1,1,3
#endif // XINPUT_INTERFACE

You will notice that the iInterface value for interface 3 states a value of 4. This means that string 4 contains a descriptor for this interface. This is the security interface used to handshake with an actual XBOX 360. We really do not need this, however, anyone that might try a man in the middle style device will need this. The 360 will cease communication with the device if this string is not returned. So we define a structure of type usb_string_descriptor_struct and fill it with the correct string.

usb_desc_c_4

struct usb_string_descriptor_struct usb_string_xinput_security_descriptor = {
    0xB2, 0x03, 0x58, 0x00, 0x62, 0x00, 0x6F, 0x00, 0x78, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00,
    0x63, 0x00, 0x75, 0x00, 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x4D, 0x00,
    0x65, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2C, 0x00,
    0x20, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00,
    0x20, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2C, 0x00, 0x20, 0x00, 0xA9, 0x00,
    0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x35, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x69, 0x00,
    0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00,
    0x43, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00,
    0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x2E, 0x00, 0x20, 0x00, 0x41, 0x00, 0x6C, 0x00, 0x6C, 0x00,
    0x20, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x20, 0x00,
    0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x64, 0x00,
    0x2E, 0x00
    };

Finally we need to add the command used to ask for this string and specify a response in the usb_descriptor_list[] array. The host will send a request for 0x0304. The 0x03 portion indicates the host wants a string. The 0x04 is the index, which as you remember, is the index we specified as interface 3’s iInterface. We then tell the device to return security descriptor string we just set up.

usb_desc_c_5

#ifdef XINPUT_INTERFACE
{0x0304, 0x0409, (const uint8_t *)&usb_string_xinput_security_descriptor, 0},
#endif
Table of Contents

[MSF] FightStick (TeensyLC XINPUT Controller) – 5

Xinput usb option

Adding Xinput to USB Menu


This was where the project took an evil turn. It took all of 15 minutes to load up the joystick example and fill it with my own code. The complications came when trying to create a device not previously defined in the teensyduino libraries. I am used to libraries and examples that show you what to define and what to include. Where I can make copies in the project directory of anything I need. The arduino IDE is all about doing things in the background with no user intervention. This makes it terribly hard to make changes for just individual projects. This entire process creates a situation where, hopefully I stay up to date with teensyduino releases. I am replacing libraries within the installation which means I replace new functionality if I don’t keep up with releases. But this section isn’t about my hatred for the arduino environment and ideologies.

This is my first arduino IDE project and I was absolutely lost. The great thing about the internet was that someone was there to help me out. I found that help from Kenton Hamaluik‘s project where he makes a teensy joystick for his wedding arcade. Reading his post showed me that we needed to add a menu item that when selected defined a statement that we would continue to check for to include code. The first step is to edit the file boards.txt, which is located in the sub-folder hardware\teensy\avr\ of your arduino installation.

Boards_1

teensyLC.menu.usb.xinput=[MSF] Shoryuken! (XINPUT DEVICE)
teensyLC.menu.usb.xinput.build.usbtype=USB_XINPUT
teensyLC.menu.usb.xinput.fake_serial=teensy_gateway

We need to add 3 simple lines to insert our device into the TeensyLC usb menu. Scroll down to right around line 321, then insert the 3 lines shown above the line that says “No USB”. You can theoretically put the entry anywhere in the menu, but I felt at the bottom would be best. Now the Xinput device type has been added to the menu and when selected defines USB_XINPUT as the usb type.

We still need to add a couple of pieces of code so that the correct headers are loaded and objects defined. The first piece of code we need to add is to the file called usb_inst.cpp. We need to tell the compiler that if USB_XINPUT is defined that it should created an instance of usb_xinput_class labeled XInput. I put the code below in at line 75:

usb_inst_orig_1

#ifdef USB_XINPUT
  usb_xinput_class XInput;
#endif

Finally to wrap this portion up, all we have left to do is include usb_xinput.h in the file Wprogram.h. We will discuss usb_xinput.h and its counterpart, usb_xinput.c, in a later section. All you need to know for now is that these two files contain the libraries we use to bridge our code with the standard USB code provided by teensyduino. I stuffed this include into line 32 of wprogram.h as follows:

wprogram_h_1

#include "usb_xinput.h"
Table of Contents