Jekyll2021-04-23T07:56:59-05:00/feed.xmlZack’s WorkbenchWelcome to my workbench.Zack LittellPython script to edit serial numbers in hex files.2021-01-11T00:00:00-06:002021-01-11T00:00:00-06:00/scripting/python-script-for-editing-serials<h6 id="introduction">INTRODUCTION</h6>
<p>Scripting is something that helps engineers of all types (software/test/electrical/etc). It truly can be done in just about any language you are familiar with, but I do feel that some languages are better than others and interpreted languages are one of the best options. However, much like any other engineering task, the best language to use can really depend on what languages you are familiar with and what end result you want to achieve. For this task I just needed a script that I could access from bash and I feel like python is the perfect language for such a task. If I needed a GUI for production staff to utilize, I might go with a .NET language because my familiarity with Python based GUI apps is nil to none.</p>
<p>The goal of this project is to replace a 10 ascii character serial number with a string provided to the script. The final code for this project can be found over on my <a href="https://github.com/zlittell/MSF.SerialHEXEdit">GitHub</a> and introduces a few extra options that we won’t discuss here, but would be a good exercise on expanding functionality.</p>
<h6 id="preparing-microcontroller-code">PREPARING MICROCONTROLLER CODE</h6>
<p>In order to get this to work we reserve a location for our character array in the RAM of our microcontroller. The full example linker file is <a href="https://github.com/zlittell/Macro9PadFW/blob/master/src/startup/samd11d14am_flash.ld">here</a>. First we want to carve out the actual memory section in our memory list.
According to the datasheet our memory section should look something like this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">MEMORY</span>
<span class="p">{</span>
<span class="n">rom</span> <span class="p">(</span><span class="n">rx</span><span class="p">)</span> <span class="o">:</span> <span class="n">ORIGIN</span> <span class="o">=</span> <span class="mh">0x00000000</span><span class="p">,</span> <span class="n">LENGTH</span> <span class="o">=</span> <span class="mh">0x00004000</span>
<span class="n">ram</span> <span class="p">(</span><span class="n">rwx</span><span class="p">)</span> <span class="o">:</span> <span class="n">ORIGIN</span> <span class="o">=</span> <span class="mh">0x20000000</span><span class="p">,</span> <span class="n">LENGTH</span> <span class="o">=</span> <span class="mh">0x00001000</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then we will want to carve off a few areas for specific purposes. Below we have carved out a future bootloader area, but currently set its length to 0. Next we have our normal ROM space from 0x0000 to 0x3EF4. Then we reserve 10 bytes (0xA) for the serial. Next I have an eeprom emulation area at 0x3F00 with a length of 0x0100. Finally we have our RAM section mapped. <em>NOTE</em> These sections do not use the full 0x4000 of ROM available and thats okay. I do not need it and I don’t want to change it now that I have units in the wild with this code.
Here is what we end up with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MEMORY
{
boot (r) : ORIGIN = 0x00000000, LENGTH = 0x00000000
rom (rx) : ORIGIN = 0x00000000, LENGTH = 0x00003EF4
serial (rw) : ORIGIN = 0x00003EF4, LENGTH = 0x0000000A
eep (rw) : ORIGIN = 0x00003F00, LENGTH = 0x00000100
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00001000
}
</code></pre></div></div>
<p>The very next thing we need to do is tell the linker that we should keep this space free even if we don’t use the variable in our code. We do this by creating a section in the linker file for the memory we carved away previously.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SECTIONS
{
/* eeprom sim area */
.eepromBlock :
{
KEEP(*(.deviceProfileSection))
} > eep
/* serial number section */
.serialNumber :
{
KEEP(*(.serialNumberSection))
} > serial
.text :
{
...
</code></pre></div></div>
<p>We tell the linker to keep the serialNumberSection even if not referenced by code and then tell the linker to put that into the “serial” MEMORY area. Now we have the section correctly reserved and if you compiled you should be able to see that. Next we should create an array of ascii characters at serialNumberSection to represent our serial number. For this I created the file, <a href="https://github.com/zlittell/Macro9PadFW/blob/master/src/serialnumber.c">serialnumber.c</a> and put the following code into it:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="kt">char</span> <span class="n">__attribute__</span> <span class="p">((</span><span class="n">section</span> <span class="p">(</span><span class="s">".serialNumberSection"</span><span class="p">)))</span> <span class="n">DeviceSerialNumber</span><span class="p">[</span><span class="n">DEVICESERIALNUMBERLENGTH</span><span class="p">]</span> <span class="o">=</span>
<span class="p">{</span>
<span class="s">"0123456789"</span>
<span class="p">};</span>
</code></pre></div></div>
<p>This simple bit of code creates a const char array DeviceSerialNumber of “0123456789” and stores it at the serialNumberSection we created previously (DEVICESERIALNUMBERLENGTH is defined in the header as 10).</p>
<h6 id="understanding-the-hex-file">Understanding the Hex File</h6>
<p>With all of this done, we can compile our code and we get this very interesting section towards the end of our hex file:
<code class="language-plaintext highlighter-rouge">:0A3EF40030313233343536373839B7</code>
<br />
Lines in an intel hex file are quite easy to read. They begin with a start code which is a single colon character (“:”). Then two hexadecimal characters describe a byte that represents the length of just the data in this line, here it is 0x0A or 10 in decimal. The next four characters describe the two bytes that represent the location in memory for this line of data. These hex characters should look familiar because 0x3EF4 is the origin we specified in our linker for the serial number to exist at. Following the address bytes is a byte that describes the type of information on this line. Here we see 00 (0x00) which tells us that this line has DATA on it. You should also recognize the next 10 bytes (0x30313233343536373839) as the ASCII representation of our serial 0123456789. If you don’t know exactly how characters are converted to ASCII, you can see a table <a href="http://www.asciitable.com/">here</a>. Finally there are two characters to represent the checksum byte. This is an 8 bit checksum, where we add up all bytes then take the 2’s complement. If you are unfamiliar with 2’s complement, you will take the 8 bit value and invert it (this is 1’s complement) then add 1 to the sum. This gives us a byte that when added to all the other bytes creates a byte sum of 0. In this example (0x0A + 0x3E + 0xF4 + 0x00 + 0x30 + 0x31 + 0x32 + 0x33 + 0x34 + 0x35 + 0x36 + 0x37 + 0x38 + 0x39 + 0xB7 = 0x400) and we only care about the single byte and nothing that overflows from there (0x400 & 0xFF = 0x00).</p>
<p>While this gives you the basics of reading a simple data line in a hex file there are more complexities to them and if you want to get a broader understanding you can start <a href="https://www.kanda.com/blog/microcontrollers/intel-hex-files-explained/">here</a>.</p>
<h6 id="beginning-our-script">BEGINNING OUR SCRIPT</h6>
<p>The main purpose of this script is to locate a specific line in the hex file based on its memory address and then replace it with a generated valid line. This means that we need to be able to open/copy/save files as well as parse the files for text content, convert strings to ascii, and calculate checksums. When I start any script like this, I just focus on base tasks and build from there. How do I open a file and then save it? How do I scan through the file and how do I search the lines for specific text? And many more as I continued to create a script that did what I needed to. Starting at the simplest place and building logically is a great way to build large projects. I don’t really need to take in arguments from the command line until I am closer to the end and focusing on it too early could actually cause me to have to rework more code than I should have to later on.</p>
<p>So lets place this <a href="/assets/files/serial-example.hex">hex file</a> into a folder for us to work in. Then we can create a file serialTest.py in the same folder. Lets put some really simple code in this file to simply open our serial-example.hex and print the first line.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"serial-example.hex"</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">inputWriter</span><span class="p">.</span><span class="n">readline</span><span class="p">())</span>
</code></pre></div></div>
<p>these two simple lines will open our hex file with read-only (‘r’) attribute. This uses pythons ‘with’ block to have exception catching if we want. This simple script will print the first line of our hex file to the console:
<code class="language-plaintext highlighter-rouge">:10000000480800202901000025010000250100000A</code></p>
<p>The next big step is to take our input hex file and copy every line to a new hex file that we will name “output.hex”. All this takes is 3 simple lines of code under our previous withblock:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"serial-example.hex"</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"output.hex"</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">outputWriter</span><span class="p">:</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="n">outputWriter</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</code></pre></div></div>
<p>This opens the input file with the readonly flag and then opens the output file with the write only flag (creating the file if it doesn’t exist already). Finally the last couple lines say that for every line in the input file, write that line to the output file. Now the next simple task is to locate our memory location 0x3EF4 and, for the time being, we can just replace it with a simple line like “INSERTHERE”. Python makes looking for substrings in strings easy using the “in” keyword. So lets replace our “for line” code with the following:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="k">if</span> <span class="s">"3EF4"</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
<span class="n">outputWriter</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"INSERTHERE</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">outputWriter</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</code></pre></div></div>
<p>Now if we open output.hex we can scroll to the bottom and clearly see where the <code class="language-plaintext highlighter-rouge">:0A3EF40030313233343536373839B7</code> line is replaced with “INSERTHERE”.</p>
<p>Next we are going to take a hardcoded serial string and turn that into a valid intel hex line to insert instead of our placeholder “INSERTHERE”. We will work with a hardcoded <code class="language-plaintext highlighter-rouge">serialString = "0123443210"</code> and convert it to string of ascii bytes to use. The most complex part of this step is going to be creating the checksum. Remember we need a byte that overflows to a perfect 0x00 when summed with every byte in the string. The easiest way to start is to combine the ascii serial bytes with what needs to exist at the beginning of the string (data length + memory location + record type). Then we will use a library called wrap that we can import from textwrap. This will allow us to wrap this single string every 2 characters giving us an array of bytes described with two hex characters. We can convert each hex byte of the array to integers and add them all together using this sum to calculate the checksum value. We will convert that checksum value to a string of 2 digit hex characters. Finally using the string we originally constructed before wrapping, we will prepend a colon (“:”) and append the checksum. This whole process will create a new valid line for our hex file that looks like this <code class="language-plaintext highlighter-rouge">:0A3EF40030313233343433323130D0</code>. We will use this line instead when we locate the serial line in our hex file. Our script at this point should look something like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">textwrap</span> <span class="kn">import</span> <span class="n">wrap</span>
<span class="c1"># Hardcoded serial number string
</span><span class="n">serialNumber</span> <span class="o">=</span> <span class="s">"0123443210"</span>
<span class="c1"># Convert to ascii hex and create new line for hex file
</span><span class="n">serialString</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">serialNumber</span><span class="p">,</span> <span class="s">'ascii'</span><span class="p">).</span><span class="nb">hex</span><span class="p">()</span>
<span class="n">replacementLine</span> <span class="o">=</span> <span class="s">"0A"</span> <span class="o">+</span> <span class="s">"3EF4"</span> <span class="o">+</span> <span class="s">"00"</span> <span class="o">+</span> <span class="n">serialString</span>
<span class="c1"># Calculate checksum and finish new line
</span><span class="n">wrappedBytes</span> <span class="o">=</span> <span class="n">wrap</span><span class="p">(</span><span class="n">replacementLine</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">sumValue</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">wrappedBytes</span><span class="p">:</span>
<span class="n">sumValue</span> <span class="o">+=</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">,</span><span class="mi">16</span><span class="p">)</span>
<span class="n">checksumValue</span> <span class="o">=</span> <span class="p">(</span><span class="o">-</span><span class="p">(</span><span class="n">sumValue</span> <span class="o">%</span> <span class="mi">256</span><span class="p">)</span> <span class="o">&</span> <span class="mh">0xFF</span><span class="p">)</span>
<span class="n">replacementLine</span> <span class="o">+=</span> <span class="nb">str</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">'{:02X}'</span><span class="p">,</span> <span class="n">checksumValue</span><span class="p">)</span>
<span class="n">replacementLine</span> <span class="o">=</span> <span class="s">":"</span> <span class="o">+</span> <span class="n">replacementLine</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
<span class="c1"># Copy input file to output file, overwriting serial line
</span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"serial-example.hex"</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"output.hex"</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">outputWriter</span><span class="p">:</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="k">if</span> <span class="s">"3EF4"</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
<span class="n">outputWriter</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">replacementLine</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">outputWriter</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</code></pre></div></div>
<p>So now we really have something going with our script. If you are confused on what anything is doing you can debug the script or just start adding in print() commands in different places to assist with understanding the program flow. The last thing we really have to accomplish is to take in command line arguments because we aren’t going to want to use the same hardcoded serial number every time. We are going to import getopt for accepting commandline arguments into our script. For the sake of simplicity I am only going to write code for an argument to accept a serial number passed in. This single argument will create a script that you can change a couple of variables in for each project and then call with a provided serial number and output your program hex file. If you want to expand on that you can visit this projects github and look at the python script there. It has many arguments that let you use the same script on varying projects, such as specifying input hex, output hex, serial number, incrementable serial file, and memory location. We will use “-s –serial” as the short and long form of our serial argument. We must check that the serial argument is passed, verify it’s length, then use it with the above code to create a replacement line in our hex file. We are just going to remove our hardcoded serial string and then add a little bit of code to the beginning of our script, in order to get our starting string from user input. The whole script should now look like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">getopt</span>
<span class="kn">from</span> <span class="nn">textwrap</span> <span class="kn">import</span> <span class="n">wrap</span>
<span class="c1"># Set up initial variable values
</span><span class="n">serialNumber</span> <span class="o">=</span> <span class="s">""</span>
<span class="c1"># Set up possible arguments
</span><span class="n">short_options</span> <span class="o">=</span> <span class="s">"s:"</span>
<span class="n">long_option</span> <span class="o">=</span> <span class="p">[</span><span class="s">"serial"</span><span class="p">]</span>
<span class="n">full_cmd_arguments</span> <span class="o">=</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span>
<span class="n">argument_list</span> <span class="o">=</span> <span class="n">full_cmd_arguments</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
<span class="n">arguments</span><span class="p">,</span> <span class="n">values</span> <span class="o">=</span> <span class="n">getopt</span><span class="p">.</span><span class="n">getopt</span><span class="p">(</span><span class="n">argument_list</span><span class="p">,</span> <span class="n">short_options</span><span class="p">,</span> <span class="n">long_option</span><span class="p">)</span>
<span class="c1"># Parse arguments
</span><span class="k">for</span> <span class="n">current_argument</span><span class="p">,</span> <span class="n">current_value</span> <span class="ow">in</span> <span class="n">arguments</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">current_argument</span><span class="p">)</span> <span class="ow">in</span> <span class="p">(</span><span class="s">"-s"</span><span class="p">,</span> <span class="s">"--serial"</span><span class="p">):</span>
<span class="n">serialNumber</span> <span class="o">=</span> <span class="n">current_value</span>
<span class="c1"># Validate arguments
</span><span class="k">if</span> <span class="p">(</span><span class="n">serialNumber</span> <span class="o">==</span><span class="s">""</span><span class="p">):</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="s">"Must pass serial number argument."</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">serialNumber</span><span class="p">)</span><span class="o">!=</span><span class="mi">10</span><span class="p">):</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="s">"Serial must be 10 characters."</span><span class="p">)</span>
<span class="c1"># Convert to ascii hex and create new line for hex file
</span><span class="n">serialString</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">serialNumber</span><span class="p">,</span> <span class="s">'ascii'</span><span class="p">).</span><span class="nb">hex</span><span class="p">()</span>
<span class="n">replacementLine</span> <span class="o">=</span> <span class="s">"0A"</span> <span class="o">+</span> <span class="s">"3EF4"</span> <span class="o">+</span> <span class="s">"00"</span> <span class="o">+</span> <span class="n">serialString</span>
<span class="c1"># Calculate checksum and finish new line
</span><span class="n">wrappedBytes</span> <span class="o">=</span> <span class="n">wrap</span><span class="p">(</span><span class="n">replacementLine</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">sumValue</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">wrappedBytes</span><span class="p">:</span>
<span class="n">sumValue</span> <span class="o">+=</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">,</span><span class="mi">16</span><span class="p">)</span>
<span class="n">checksumValue</span> <span class="o">=</span> <span class="p">(</span><span class="o">-</span><span class="p">(</span><span class="n">sumValue</span> <span class="o">%</span> <span class="mi">256</span><span class="p">)</span> <span class="o">&</span> <span class="mh">0xFF</span><span class="p">)</span>
<span class="n">replacementLine</span> <span class="o">+=</span> <span class="nb">str</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">'{:02X}'</span><span class="p">,</span> <span class="n">checksumValue</span><span class="p">)</span>
<span class="n">replacementLine</span> <span class="o">=</span> <span class="s">":"</span> <span class="o">+</span> <span class="n">replacementLine</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
<span class="c1"># Copy input file to output file, overwriting serial line
</span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"serial-example.hex"</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"output.hex"</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">outputWriter</span><span class="p">:</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">inputWriter</span><span class="p">:</span>
<span class="k">if</span> <span class="s">"3EF4"</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
<span class="n">outputWriter</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">replacementLine</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">outputWriter</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</code></pre></div></div>
<p>When you run the script with <code class="language-plaintext highlighter-rouge">python3 serialTest.py</code> you will now get <code class="language-plaintext highlighter-rouge">Must pass serial number argument.</code> as an output. If we try something like <code class="language-plaintext highlighter-rouge">python3 serialTest.py -s 1243</code> we will get an output of <code class="language-plaintext highlighter-rouge">Serial must be 10 characters.</code> on the terminal. Finally if we enter <code class="language-plaintext highlighter-rouge">python3 serialTest.py -s 1233211230</code> our script will complete without error and when we open our hex file we see our 0x3ef4 memory line changed to <code class="language-plaintext highlighter-rouge">:0A3EF40031323333323131323330D2</code>.</p>
<h6 id="conclusion">CONCLUSION</h6>
<p>This is the simplest form of this script or what I would consider a least viable product (Remember products aren’t just things you create to sell, products are anything you create that is utilized by yourself or someone else). You could then call this script from other scripts that track serial numbers. Maybe assembly workers will scan barcodes, qr codes, or manually enter data that is stored in a database when they create this serial and program your device. You could then tie the serial in this database to customers when they purchase a device and shipping sends out an invoiced item. Many different solutions are possible and they can be anything under the sun really. This is why the full blown script even has a mode where it just tracks the last used serial in a file and increments it. Automation scripting allows you to streamline your production process whether you are 1 person at your home or an engineer at a company with many employees. The oppurtunities are endless!</p>Zack LittellINTRODUCTION Scripting is something that helps engineers of all types (software/test/electrical/etc). It truly can be done in just about any language you are familiar with, but I do feel that some languages are better than others and interpreted languages are one of the best options. However, much like any other engineering task, the best language to use can really depend on what languages you are familiar with and what end result you want to achieve. For this task I just needed a script that I could access from bash and I feel like python is the perfect language for such a task. If I needed a GUI for production staff to utilize, I might go with a .NET language because my familiarity with Python based GUI apps is nil to none.Creating an Xinput Library for Teensyduino2016-05-10T00:00:00-05:002016-05-10T00:00:00-05:00/fightstick/xinput-library-for-teensyduino<p>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.</p>
<p>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.</p>
<p>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!</p>
<p>Check out these links:</p>
<p><a href="https://github.com/zlittell/MSF-FightStick">MSF FightStick Code Base</a><br />
<a href="https://github.com/zlittell/MSF-XINPUT">MSF Xinput Library Code Base</a><br />
<a href="http://www.tested.com/tech/gaming/569647-how-build-pinsim-virtual-reality-pinball-machine/">Jeremy William’s PinSim Controller over at Tested!</a></p>
<p>Thanks<br />
-ZACK-</p>Zack LittellRecently 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.Adding Xinput to USB Menu2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/adding-xinput-to-usb-menu<p><img src="/assets/images/fightstick/xinput-usb-option.jpg" alt="Xinput usb option" width="500px" /></p>
<p>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.</p>
<p>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 <a href="https://blog.hamaluik.ca/posts/making-a-custom-teensy3-hid-joystick/">Kenton Hamaluik</a>‘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.</p>
<p><img src="/assets/images/fightstick/boards_1.jpg" alt="Boards_1 Code" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">teensyLC</span><span class="p">.</span><span class="n">menu</span><span class="p">.</span><span class="n">usb</span><span class="p">.</span><span class="n">xinput</span><span class="o">=</span><span class="p">[</span><span class="n">MSF</span><span class="p">]</span> <span class="n">Shoryuken</span><span class="o">!</span> <span class="p">(</span><span class="n">XINPUT</span> <span class="n">DEVICE</span><span class="p">)</span>
<span class="n">teensyLC</span><span class="p">.</span><span class="n">menu</span><span class="p">.</span><span class="n">usb</span><span class="p">.</span><span class="n">xinput</span><span class="p">.</span><span class="n">build</span><span class="p">.</span><span class="n">usbtype</span><span class="o">=</span><span class="n">USB_XINPUT</span>
<span class="n">teensyLC</span><span class="p">.</span><span class="n">menu</span><span class="p">.</span><span class="n">usb</span><span class="p">.</span><span class="n">xinput</span><span class="p">.</span><span class="n">fake_serial</span><span class="o">=</span><span class="n">teensy_gateway</span>
</code></pre></div></div>
<p>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.</p>
<p>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:</p>
<p><img src="/assets/images/fightstick/usb_inst_orig_1.jpg" alt="usb_inst_orig_1" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef USB_XINPUT
</span> <span class="n">usb_xinput_class</span> <span class="n">XInput</span><span class="p">;</span>
<span class="cp">#endif
</span></code></pre></div></div>
<p>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:</p>
<p><img src="/assets/images/fightstick/wprogram_h_1.jpg" alt="wprogram_h_1" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include "usb_xinput.h"
</span></code></pre></div></div>Zack LittellDefining the Xinput USB Device2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/defining-the-xinput-device<p><img src="/assets/images/usb_icon.png" alt="USB_Icon" /><br />
Teensyduino is set up to define USB devices through the usb_desc.h file. So we arae going to want to add the following code to this file:</p>
<p><img src="/assets/images/fightstick/usb_desc_h_1.jpg" alt="usb_desc_h_1" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#elif defined(USB_XINPUT)
</span> <span class="cp">#define DEVICE_CLASS 0xFF
</span> <span class="cp">#define DEVICE_SUBCLASS 0xFF
</span> <span class="cp">#define DEVICE_PROTOCOL 0xFF
</span> <span class="cp">#define DEVICE_VERSION 0x0114
</span> <span class="cp">#define DEVICE_ATTRIBUTES 0xA0
</span> <span class="cp">#define DEVICE_POWER 0xFA
</span> <span class="cp">#define VENDOR_ID 0x045e
</span> <span class="cp">#define PRODUCT_ID 0x028e
</span> <span class="cp">#define MANUFACTURER_NAME {'©','M','i','c','r','o','s','o','f','t'}
</span> <span class="cp">#define MANUFACTURER_NAME_LEN 10
</span> <span class="cp">#define PRODUCT_NAME {'C','o','n','t','r','o','l','l','e','r'}
</span> <span class="cp">#define PRODUCT_NAME_LEN 10
</span> <span class="cp">#define EP0_SIZE 8
</span> <span class="cp">#define NUM_ENDPOINTS 6
</span> <span class="cp">#define NUM_USB_BUFFERS 24
</span> <span class="cp">#define NUM_INTERFACE 4
</span> <span class="cp">#define XINPUT_INTERFACE 0
</span> <span class="cp">#define XINPUT_RX_ENDPOINT 2
</span> <span class="cp">#define XINPUT_RX_SIZE 3
</span> <span class="cp">#define XINPUT_TX_ENDPOINT 1
</span> <span class="cp">#define XINPUT_TX_SIZE 20
</span> <span class="cp">#define CONFIG_DESC_SIZE 153
</span> <span class="cp">#define ENDPOINT1_CONFIG ENDPOINT_TRANSIMIT_ONLY
</span> <span class="cp">#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY
</span> <span class="cp">#define ENDPOINT3_CONFIG ENDPOINT_TRANSIMIT_ONLY
</span> <span class="cp">#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY
</span> <span class="cp">#define ENDPOINT5_CONFIG ENDPOINT_TRANSMIT_AND_RECEIVE
</span> <span class="cp">#define ENDPOINT6_CONFIG ENDPOINT_TRANSIMIT_ONLY
</span></code></pre></div></div>
<p>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.</p>
<p>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:</p>
<p><img src="/assets/images/fightstick/usb_desc_c_1.jpg" alt="usb_desc_c_1" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// USB Device Descriptor. The USB host reads this first, to learn</span>
<span class="c1">// what type of device is connected.</span>
<span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">device_descriptor</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
<span class="mi">18</span><span class="p">,</span> <span class="c1">// bLength</span>
<span class="mi">1</span><span class="p">,</span> <span class="c1">// bDescriptorType</span>
<span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x02</span><span class="p">,</span> <span class="c1">// bcdUSB</span>
<span class="cp">#ifdef DEVICE_CLASS
</span> <span class="n">DEVICE_CLASS</span><span class="p">,</span> <span class="c1">// bDeviceClass</span>
<span class="cp">#else
</span> <span class="mi">0</span><span class="p">,</span>
<span class="cp">#endif
#ifdef DEVICE_SUBCLASS
</span> <span class="n">DEVICE_SUBCLASS</span><span class="p">,</span> <span class="c1">// bDeviceSubClass</span>
<span class="cp">#else
</span> <span class="mi">0</span><span class="p">,</span>
<span class="cp">#endif
#ifdef DEVICE_PROTOCOL
</span> <span class="n">DEVICE_PROTOCOL</span><span class="p">,</span> <span class="c1">// bDeviceProtocol</span>
<span class="cp">#else
</span> <span class="mi">0</span><span class="p">,</span>
<span class="cp">#endif
</span> <span class="n">EP0_SIZE</span><span class="p">,</span> <span class="c1">// bMaxPacketSize0</span>
<span class="n">LSB</span><span class="p">(</span><span class="n">VENDOR_ID</span><span class="p">),</span> <span class="n">MSB</span><span class="p">(</span><span class="n">VENDOR_ID</span><span class="p">),</span> <span class="c1">// idVendor</span>
<span class="n">LSB</span><span class="p">(</span><span class="n">PRODUCT_ID</span><span class="p">),</span> <span class="n">MSB</span><span class="p">(</span><span class="n">PRODUCT_ID</span><span class="p">),</span> <span class="c1">// idProduct</span>
<span class="cp">#ifdef DEVICE_VERSION // bcdDevice
</span> <span class="n">LSB</span><span class="p">(</span><span class="n">DEVICE_VERSION</span><span class="p">),</span> <span class="n">MSB</span><span class="p">(</span><span class="n">DEVICE_VERSION</span><span class="p">),</span>
<span class="cp">#else
</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span>
<span class="cp">#endif
</span> <span class="mi">1</span><span class="p">,</span> <span class="c1">// iManufacturer</span>
<span class="mi">2</span><span class="p">,</span> <span class="c1">// iProduct</span>
<span class="mi">3</span><span class="p">,</span> <span class="c1">// iSerialNumber</span>
<span class="mi">1</span> <span class="c1">// bNumConfigurations</span>
<span class="p">};</span>
</code></pre></div></div>
<p>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:</p>
<p><img src="/assets/images/fightstick/usb_desc_c_2.jpg" alt="usb_desc_c_2" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef DEVICE_ATTRIBUTES
</span> <span class="n">DEVICE_ATTRIBUTES</span><span class="p">,</span> <span class="c1">// bmAttributes</span>
<span class="cp">#else
</span> <span class="mh">0xC0</span><span class="p">,</span>
<span class="cp">#endif
#ifdef DEVICE_POWER
</span> <span class="n">DEVICE_POWER</span><span class="p">,</span> <span class="c1">// bMaxPower</span>
<span class="cp">#else
</span> <span class="mi">50</span><span class="p">,</span>
<span class="cp">#endif
</span></code></pre></div></div>
<p>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[].</p>
<p><img src="/assets/images/fightstick/usb_desc_c_3_1.jpg" alt="usb_desc_c_3_1" /><br />
<img src="/assets/images/fightstick/usb_desc_c_3_2.jpg" alt="usb_desc_c_3_2" /><br />
<img src="/assets/images/fightstick/usb_desc_c_3_3.jpg" alt="usb_desc_c_3_3" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef XINPUT_INTERFACE
</span><span class="c1">//Interface 0</span>
<span class="mi">9</span><span class="p">,</span> <span class="c1">//bLength (length of interface descriptor 9 bytes)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//bDescriptorType (4 is interface)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//bInterfaceNumber (This is interface 0)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//bAlternateSetting (used to select alternate setting. notused)</span>
<span class="mi">2</span><span class="p">,</span> <span class="c1">//bNumEndpoints (this interface has 2 endpoints)</span>
<span class="mh">0xFF</span><span class="p">,</span> <span class="c1">//bInterfaceClass (Vendor Defined is 255)</span>
<span class="mh">0x5D</span><span class="p">,</span> <span class="c1">//bInterfaceSubClass</span>
<span class="mh">0x01</span><span class="p">,</span> <span class="c1">//bInterfaceProtocol</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//iInterface (Index of string descriptor for describing this notused)</span>
<span class="c1">//Some sort of common descriptor? I pulled this from Message Analyzer dumps of an actual controller</span>
<span class="mi">17</span><span class="p">,</span><span class="mi">33</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">37</span><span class="p">,</span><span class="mi">129</span><span class="p">,</span><span class="mi">20</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">19</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span>
<span class="c1">//Endpoint 1 IN</span>
<span class="mi">7</span><span class="p">,</span> <span class="c1">//bLength (length of ep1in in descriptor 7 bytes)</span>
<span class="mi">5</span><span class="p">,</span> <span class="c1">//bDescriptorType (5 is endpoint)</span>
<span class="mh">0x81</span><span class="p">,</span> <span class="c1">//bEndpointAddress (0x81 is IN1)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bmAttributes (0x03 is interrupt no synch, usage type data)</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="c1">//wMaxPacketSize (0x0020 is 1x32 bytes)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//bInterval (polling interval in frames 4 frames)</span>
<span class="c1">//Endpoint 2 OUT</span>
<span class="mi">7</span><span class="p">,</span> <span class="c1">//bLength (length of ep2out in descriptor 7 bytes)</span>
<span class="mi">5</span><span class="p">,</span> <span class="c1">//bDescriptorType (5 is endpoint)</span>
<span class="mh">0x02</span><span class="p">,</span> <span class="c1">//bEndpointAddress (0x02 is OUT2)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bmAttributes (0x03 is interrupt no synch, usage type data)</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="c1">//wMaxPacketSize (0x0020 is 1x32 bytes)</span>
<span class="mi">8</span><span class="p">,</span> <span class="c1">//bInterval (polling interval in frames 8 frames)</span>
<span class="c1">//Interface 1</span>
<span class="mi">9</span><span class="p">,</span> <span class="c1">//bLength (length of interface descriptor 9 bytes)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//bDescriptorType (4 is interface)</span>
<span class="mi">1</span><span class="p">,</span> <span class="c1">//bInterfaceNumber (This is interface 1)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//bAlternateSetting (used to select alternate setting. notused)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//bNumEndpoints (this interface has 4 endpoints)</span>
<span class="mh">0xFF</span><span class="p">,</span> <span class="c1">//bInterfaceClass (Vendor Defined is 255)</span>
<span class="mh">0x5D</span><span class="p">,</span> <span class="c1">//bInterfaceSubClass (93)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bInterfaceProtocol (3)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//iInterface (Index of string descriptor for describing this notused)</span>
<span class="c1">//A different common descriptor? I pulled this from Message Analyzer dumps of an actual controller</span>
<span class="mi">27</span><span class="p">,</span><span class="mi">33</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">131</span><span class="p">,</span><span class="mi">64</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">32</span><span class="p">,</span><span class="mi">22</span><span class="p">,</span><span class="mi">133</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">22</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span>
<span class="c1">//Endpoint 3 IN</span>
<span class="mi">7</span><span class="p">,</span> <span class="c1">//bLength (length of ep3in descriptor 7 bytes)</span>
<span class="mi">5</span><span class="p">,</span> <span class="c1">//bDescriptorType (5 is endpoint)</span>
<span class="mh">0x83</span><span class="p">,</span> <span class="c1">//bEndpointAddress (0x83 is IN3)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bmAttributes (0x03 is interrupt no synch, usage type data)</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="c1">//wMaxPacketSize (0x0020 is 1x32 bytes)</span>
<span class="mi">2</span><span class="p">,</span> <span class="c1">//bInterval (polling interval in frames 2 frames)</span>
<span class="c1">//Endpoint 4 OUT</span>
<span class="mi">7</span><span class="p">,</span> <span class="c1">//bLength (length of ep4out descriptor 7 bytes)</span>
<span class="mi">5</span><span class="p">,</span> <span class="c1">//bDescriptorType (5 is endpoint)</span>
<span class="mh">0x04</span><span class="p">,</span> <span class="c1">//bEndpointAddress (0x04 is OUT4)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bmAttributes (0x03 is interrupt no synch, usage type data)</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="c1">//wMaxPacketSize (0x0020 is 1x32 bytes)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//bInterval (polling interval in frames 4 frames)</span>
<span class="c1">//Endpoint 5 IN</span>
<span class="mi">7</span><span class="p">,</span> <span class="c1">//bLength (length of ep5in descriptor 7 bytes)</span>
<span class="mi">5</span><span class="p">,</span> <span class="c1">//bDescriptorType (5 is endpoint)</span>
<span class="mh">0x85</span><span class="p">,</span> <span class="c1">//bEndpointAddress (0x85 is IN5)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bmAttributes (0x03 is interrupt no synch, usage type data)</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="c1">//wMaxPacketSize (0x0020 is 1x32 bytes)</span>
<span class="mi">64</span><span class="p">,</span> <span class="c1">//bInterval (polling interval in frames 64 frames)</span>
<span class="c1">//Endpoint 5 OUT (shares endpoint number with previous)</span>
<span class="mi">7</span><span class="p">,</span> <span class="c1">//bLength (length of ep5out descriptor 7 bytes)</span>
<span class="mi">5</span><span class="p">,</span> <span class="c1">//bDescriptorType (5 is endpoint)</span>
<span class="mh">0x05</span><span class="p">,</span> <span class="c1">//bEndpointAddress (0x05 is OUT5)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bmAttributes (0x03 is interrupt no synch, usage type data)</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="c1">//wMaxPacketSize (0x0020 is 1x32 bytes)</span>
<span class="mi">16</span><span class="p">,</span> <span class="c1">//bInterval (polling interval in frames 16 frames)</span>
<span class="c1">//Interface 2</span>
<span class="mi">9</span><span class="p">,</span> <span class="c1">//bLength (length of interface descriptor 9 bytes)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//bDescriptorType (4 is interface)</span>
<span class="mi">2</span><span class="p">,</span> <span class="c1">//bInterfaceNumber (This is interface 2)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//bAlternateSetting (used to select alternate setting. notused)</span>
<span class="mi">1</span><span class="p">,</span> <span class="c1">//bNumEndpoints (this interface has 4 endpoints)</span>
<span class="mh">0xFF</span><span class="p">,</span> <span class="c1">//bInterfaceClass (Vendor Defined is 255)</span>
<span class="mh">0x5D</span><span class="p">,</span> <span class="c1">//bInterfaceSubClass (93)</span>
<span class="mh">0x02</span><span class="p">,</span> <span class="c1">//bInterfaceProtocol (3)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//iInterface (Index of string descriptor for describing this notused)</span>
<span class="c1">//Common Descriptor. Seems that these come after every interface description?</span>
<span class="mi">9</span><span class="p">,</span><span class="mi">33</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">34</span><span class="p">,</span><span class="mi">134</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span>
<span class="c1">//Endpoint 6 IN</span>
<span class="mi">7</span><span class="p">,</span> <span class="c1">//bLength (length of ep6in descriptor 7 bytes)</span>
<span class="mi">5</span><span class="p">,</span> <span class="c1">//bDescriptorType (5 is endpoint)</span>
<span class="mh">0x86</span><span class="p">,</span> <span class="c1">//bEndpointAddress (0x86 is IN6)</span>
<span class="mh">0x03</span><span class="p">,</span> <span class="c1">//bmAttributes (0x03 is interrupt no synch, usage type data)</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="c1">//wMaxPacketSize (0x0020 is 1x32 bytes)</span>
<span class="mi">16</span><span class="p">,</span> <span class="c1">//bInterval (polling interval in frames 64 frames)+</span>
<span class="c1">//Interface 3</span>
<span class="c1">//This is the interface on which all the security handshaking takes place</span>
<span class="c1">//We don't use this but it could be used for man-in-the-middle stuff</span>
<span class="mi">9</span><span class="p">,</span> <span class="c1">//bLength (length of interface descriptor 9 bytes)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//bDescriptorType (4 is interface)</span>
<span class="mi">3</span><span class="p">,</span> <span class="c1">//bInterfaceNumber (This is interface 3)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//bAlternateSetting (used to select alternate setting. notused)</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">//bNumEndpoints (this interface has 0 endpoints ???)</span>
<span class="mh">0xFF</span><span class="p">,</span> <span class="c1">//bInterfaceClass (Vendor Defined is 255)</span>
<span class="mh">0xFD</span><span class="p">,</span> <span class="c1">//bInterfaceSubClass (253)</span>
<span class="mh">0x13</span><span class="p">,</span> <span class="c1">//bInterfaceProtocol (19)</span>
<span class="mi">4</span><span class="p">,</span> <span class="c1">//iInterface (Computer never asks for this, but an x360 would. so include one day?)</span>
<span class="c1">//Another interface another Common Descriptor</span>
<span class="mi">6</span><span class="p">,</span><span class="mi">65</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span>
<span class="cp">#endif // XINPUT_INTERFACE
</span></code></pre></div></div>
<p>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.</p>
<p><img src="/assets/images/fightstick/usb_desc_c_4.jpg" alt="usb_desc_c_4" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">usb_string_descriptor_struct</span> <span class="n">usb_string_xinput_security_descriptor</span> <span class="o">=</span> <span class="p">{</span>
<span class="mh">0xB2</span><span class="p">,</span> <span class="mh">0x03</span><span class="p">,</span> <span class="mh">0x58</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x78</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x63</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x4D</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x33</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x2C</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x56</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6E</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x2E</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x2C</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0xA9</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x32</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x35</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x4D</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x63</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x66</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x43</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6F</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6E</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x2E</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x41</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6C</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x6C</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x67</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span>
<span class="mh">0x2E</span><span class="p">,</span> <span class="mh">0x00</span>
<span class="p">};</span>
</code></pre></div></div>
<p>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.</p>
<p><img src="/assets/images/fightstick/usb_desc_c_5.jpg" alt="usb_desc_c_5" /></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef XINPUT_INTERFACE
</span><span class="p">{</span><span class="mh">0x0304</span><span class="p">,</span> <span class="mh">0x0409</span><span class="p">,</span> <span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">usb_string_xinput_security_descriptor</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span>
<span class="cp">#endif
</span></code></pre></div></div>Zack LittellTeensyduino is set up to define USB devices through the usb_desc.h file. So we arae going to want to add the following code to this file:Dinput VS Xinput2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/dinput-vs-xinput<p><img src="/assets/images/fightstick/dinputvsxinput.jpg" alt="Controller Fight" /><br />
There is likely to be quite a few misconceptions and errors in this area of my understanding. I am not a game developer and actually do very minimal PC application development. Originally games used a DLL named Dinput to communicate with controllers in their software. You could use Dinput.dll to probe a controller, learn its ins and outs, and then utilize it in your game. Microsoft deemed this too difficult a task for PC game developers. Since they were releasing a new controller, they really wanted to make it accessible to developers to work with. They decided to reinvent how developers access their controllers. The big issue that affects the hobbyist community is that it is no longer a HID device. Look at any game you play, it supports the XBOX 360 controller. Not being HID is the biggest downfall to Xinput devices (sometimes referred to as XUSB devices in Microsoft documentation). This creates a huge problem for us because without being HID you will need drivers for your device. Getting drivers signed is an expensive expenditure and not something most are willing to do for non-commercial projects.</p>
<p>So naturally the next thought is “Who cares? We will just stick to Dinput HID controllers!” That would work, if the whole gaming world didn’t adopt Xinput. For a long time, people supported both Xinput and Dinput in their games. Sadly, it is no longer that way. Actually many games incorrectly manage Dinput devices and cause errors to occur that make games unplayable. This is where the constant looking up or the constantly spinning left comes in. The Dinput device is actually assigned an extra axis on accident and that causes you to look whichever way it is assigned, indefinitely. Many examples of this are found on games that do not need a controller but behave incorrectly when you have one plugged in, Tribes: Ascend being a good example. So now you need to make your Dinput device look like an Xinput one. Up until this point you had one option, you would put software called X360CE (or other similar software) into your game directory, you would assign your controller’s inputs to similar ones on a 360 controller, and it would create a custom xinput.dll in the game folder that correctly mapped your controller for use.</p>
<p>Using X360CE was an easy option for us, but I just couldn’t do it. We worked hard on making sure these fightsticks turned out nice and I wasn’t about to settle for putting some software in all my game directories. That was when the idea was born. There has to be a way to imitate the XBOX 360 controller. I mean it’s just a USB device afterall, right? It was time to investigate!</p>Zack LittellThere is likely to be quite a few misconceptions and errors in this area of my understanding. I am not a game developer and actually do very minimal PC application development. Originally games used a DLL named Dinput to communicate with controllers in their software. You could use Dinput.dll to probe a controller, learn its ins and outs, and then utilize it in your game. Microsoft deemed this too difficult a task for PC game developers. Since they were releasing a new controller, they really wanted to make it accessible to developers to work with. They decided to reinvent how developers access their controllers. The big issue that affects the hobbyist community is that it is no longer a HID device. Look at any game you play, it supports the XBOX 360 controller. Not being HID is the biggest downfall to Xinput devices (sometimes referred to as XUSB devices in Microsoft documentation). This creates a huge problem for us because without being HID you will need drivers for your device. Getting drivers signed is an expensive expenditure and not something most are willing to do for non-commercial projects.Introduction to the Project2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/introduction-to-the-project<p><img src="/assets/images/fightstick/collage-2015-05-01-20_00_26.jpg" alt="Project Introduction" width="400px" /></p>
<p>The idea for this project came up when discussing the release of Mortal Kombat X. The guys at Mechanical Squid Factory (our gaming group) decided they wanted to branch out into the competitive fighting game scene. We started doing research into fighting games and that brought us to a forum called Shoryuken. The realization that a regular gamepad might not provide the best experience quickly set in. We decided that a fight stick would be just too expensive and thought keyboard would work out. The entire team played through the story of MKX and thought our fingers would never function again after contorting them to the keyboard. Soon enough we couldn’t take it anymore and pieced together fight sticks. Most of our parts were ordered from FocusAttack and the cases from Art at Tek-Innovations. As you can tell, the cases are laser cut by Art and they do have a 2 week turn around time. However, they really are beautiful when assembled. We then opted for a DIY solution to a controller board. This could save us $40-$50 since we only planned on using these on the PC anyways.</p>
<p>I began scouring through development boards. I had to meet just a few criteria but they were strict. I needed it to be small, support USB, and most of all, be CHEAP. Many of the boards just didn’t provide enough cost benefit over an existing solution. Finally I found an awesome little ARM cortex development board called the TeensyLC. It met all the criteria with flying colors and it had an ARM on it, which was an awesome bonus. What I did not know is the frustration that would follow after receiving these boards. There was nothing wrong with the TeensyLC, it was the bundling with arduino that caused most of the headaches. I am strong in my hate against arduino. I feel that it definitely has a place, but it has recently crept into places it does not belong. In the beginning, however, the arduino integration was actually a plus. I needed a joystick that plugged into a PC. It is pretty common for a device to have a HID joystick example if it supports HID USB. I went ahead and ordered 3 of them for the group.</p>
<p>What happened next is what I guess you could call scope creep. The project was finished and it was working great. Standard HID USB Joystick and it showed up with our name on it. Then someone uses it in a portion of the game that doesn’t just use the DPAD. Shouldn’t be an issue right? Wrong! The character continued to just look up constantly. I tried adding extra axes and setting them to center to fix it. I even tried changing all the bit lengths for the axes to the same that the XBOX uses, but nothing would work. We then started loading up other joystick games and realizing that most of them just didn’t work or if they did, you just constantly looked up or spun in a specific direction. With a little bit of google-fu I quickly learned that I had created a joystick compatible with Dinput and that was no longer a thing unfortunately. The industry was all about Xinput, which is a real bummer for hobbyist. That’s when the scope expanded and my struggles with adjusting my work flow to the Arduino IDE began.</p>
<p>You can find my code in my <a href="https://github.com/zlittell/MSF-FightStick">GIT repository</a>!</p>Zack LittellWrapping it all up!2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/project-wrapup<p>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.</p>
<p>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.</p>
<p>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!</p>Zack LittellWe 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.The Code2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/the-code<p>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:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifndef USBxinput_h_
#define USBxinput_h_
#include "usb_desc.h"
#if defined(XINPUT_INTERFACE)
#include <inttypes.h>
</span><span class="c1">// C language implementation</span>
<span class="cp">#ifdef __cplusplus
</span><span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
<span class="cp">#endif
</span><span class="kt">int</span> <span class="n">usb_xinput_recv</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">timeout</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">usb_xinput_available</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">usb_xinput_send</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">timeout</span><span class="p">);</span>
<span class="cp">#ifdef __cplusplus
</span><span class="p">}</span>
<span class="cp">#endif
</span><span class="c1">// C++ interface</span>
<span class="cp">#ifdef __cplusplus
</span><span class="n">class</span> <span class="n">usb_xinput_class</span>
<span class="p">{</span>
<span class="nl">public:</span>
<span class="kt">int</span> <span class="n">available</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span><span class="k">return</span> <span class="n">usb_xinput_available</span><span class="p">();</span> <span class="p">}</span>
<span class="kt">int</span> <span class="n">recv</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">timeout</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">usb_xinput_recv</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">timeout</span><span class="p">);</span> <span class="p">}</span>
<span class="kt">int</span> <span class="n">send</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">timeout</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">usb_xinput_send</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">timeout</span><span class="p">);</span> <span class="p">}</span>
<span class="p">};</span>
<span class="k">extern</span> <span class="n">usb_xinput_class</span> <span class="n">XInput</span><span class="p">;</span>
<span class="cp">#endif // __cplusplus
#endif // XINPUT_INTERFACE
#endif // USBxinput_h_
</span></code></pre></div></div>
<p>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.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include "usb_dev.h"
#include "usb_xinput.h"
#include "core_pins.h" // for yield(), millis()
#include <string.h> // for memcpy()
</span><span class="c1">//#include "HardwareSerial.h"</span>
<span class="cp">#ifdef XINPUT_INTERFACE // defined by usb_dev.h -> usb_desc.h
#if F_CPU >= 20000000
</span><span class="c1">//Function receives packets from the RX endpoint</span>
<span class="c1">//We will use this for receiving LED commands</span>
<span class="kt">int</span> <span class="nf">usb_xinput_recv</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">timeout</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">usb_packet_t</span> <span class="o">*</span><span class="n">rx_packet</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">begin</span> <span class="o">=</span> <span class="n">millis</span><span class="p">();</span>
<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">usb_configuration</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="n">rx_packet</span> <span class="o">=</span> <span class="n">usb_rx</span><span class="p">(</span><span class="n">XINPUT_RX_ENDPOINT</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">rx_packet</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">millis</span><span class="p">()</span> <span class="o">-</span> <span class="n">begin</span> <span class="o">></span> <span class="n">timeout</span> <span class="o">||</span> <span class="o">!</span><span class="n">timeout</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">yield</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">rx_packet</span><span class="o">-></span><span class="n">buf</span><span class="p">,</span> <span class="n">XINPUT_RX_SIZE</span><span class="p">);</span>
<span class="n">usb_free</span><span class="p">(</span><span class="n">rx_packet</span><span class="p">);</span>
<span class="k">return</span> <span class="n">XINPUT_RX_SIZE</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">//Function to check if packets are available</span>
<span class="c1">//to be received on the RX endpoint</span>
<span class="kt">int</span> <span class="nf">usb_xinput_available</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">uint32_t</span> <span class="n">count</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">usb_configuration</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">count</span> <span class="o">=</span> <span class="n">usb_rx_byte_count</span><span class="p">(</span><span class="n">XINPUT_RX_ENDPOINT</span><span class="p">);</span>
<span class="k">return</span> <span class="n">count</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Maximum number of transmit packets to queue so we don't starve other endpoints for memory</span>
<span class="cp">#define TX_PACKET_LIMIT 3
</span><span class="c1">//Function used to send packets out of the TX endpoint</span>
<span class="c1">//This is used to send button reports</span>
<span class="kt">int</span> <span class="nf">usb_xinput_send</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">timeout</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">usb_packet_t</span> <span class="o">*</span><span class="n">tx_packet</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">begin</span> <span class="o">=</span> <span class="n">millis</span><span class="p">();</span>
<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">usb_configuration</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">usb_tx_packet_count</span><span class="p">(</span><span class="n">XINPUT_TX_ENDPOINT</span><span class="p">)</span> <span class="o"><</span> <span class="n">TX_PACKET_LIMIT</span><span class="p">)</span> <span class="p">{</span>
<span class="n">tx_packet</span> <span class="o">=</span> <span class="n">usb_malloc</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">tx_packet</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">millis</span><span class="p">()</span> <span class="o">-</span> <span class="n">begin</span> <span class="o">></span> <span class="n">timeout</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">yield</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">tx_packet</span><span class="o">-></span><span class="n">buf</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">XINPUT_TX_SIZE</span><span class="p">);</span>
<span class="n">tx_packet</span><span class="o">-></span><span class="n">len</span> <span class="o">=</span> <span class="n">XINPUT_TX_SIZE</span><span class="p">;</span>
<span class="n">usb_tx</span><span class="p">(</span><span class="n">XINPUT_TX_ENDPOINT</span><span class="p">,</span> <span class="n">tx_packet</span><span class="p">);</span>
<span class="k">return</span> <span class="n">XINPUT_TX_SIZE</span><span class="p">;</span>
<span class="p">}</span>
<span class="cp">#endif // F_CPU
#endif // XINPUT_INTERFACE
</span></code></pre></div></div>
<p>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.</p>
<p>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().</p>
<p>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.</p>
<p>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.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//General Declarations</span>
<span class="cp">#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
</span>
<span class="c1">//LED STYLE DEFINES</span>
<span class="cp">#define NO_LED 0
#define ONBOARD_LED 1
#define EXTERNAL_LED 2
</span>
<span class="c1">//LED Pattern Defines</span>
<span class="cp">#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
</span>
<span class="c1">//BUTTON MASK DEFINES</span>
<span class="cp">#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
</span>
<span class="c1">//Byte location Definitions</span>
<span class="cp">#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
</span>
<span class="c1">//Pin Declarations</span>
<span class="cp">#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
</span>
<span class="c1">//Position of a button in the button status array</span>
<span class="cp">#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
</span></code></pre></div></div>
<p>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.</p>
<p><a href="/assets/pdfs/fightstick/fightstick-bitmask-examples-1.pdf">FightStick Bitmask Examples 1</a></p>
<p>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.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
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.
*/</span>
<span class="c1">//Includes</span>
<span class="cp">#include <Bounce.h>
#include "FightStick.h"
</span><span class="c1">//LED STYLE</span>
<span class="c1">//NO_LED = Do not use LED</span>
<span class="c1">//ONBOARD_LED = use on board LED</span>
<span class="c1">//EXTERNAL_LED = in the future add option to use outputs for 4 LEDS</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">LEDSTYLE</span> <span class="o">=</span> <span class="n">ONBOARD_LED</span><span class="p">;</span>
<span class="c1">//Global Variables</span>
<span class="n">byte</span> <span class="n">buttonStatus</span><span class="p">[</span><span class="n">NUMBUTTONS</span><span class="p">];</span> <span class="c1">//array Holds a "Snapshot" of the button status to parse and manipulate</span>
<span class="kt">uint8_t</span> <span class="n">TXData</span><span class="p">[</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x14</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">};</span> <span class="c1">//Holds USB transmit packet data</span>
<span class="kt">uint8_t</span> <span class="n">RXData</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">};</span> <span class="c1">//Holds USB receive packet data</span>
<span class="c1">//LED Toggle Tracking Global Variables</span>
<span class="kt">uint8_t</span> <span class="n">LEDState</span> <span class="o">=</span> <span class="n">LOW</span><span class="p">;</span> <span class="c1">//used to set the pin for the LED</span>
<span class="kt">uint32_t</span> <span class="n">previousMS</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">//used to store the last time LED was updated</span>
<span class="kt">uint8_t</span> <span class="n">LEDtracker</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">//used as an index to step through a pattern on interval</span>
<span class="c1">//LED Patterns</span>
<span class="kt">uint8_t</span> <span class="n">patternAllOff</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="kt">uint8_t</span> <span class="n">patternBlinkRotate</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="kt">uint8_t</span> <span class="n">patternPlayer1</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="kt">uint8_t</span> <span class="n">patternPlayer2</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="kt">uint8_t</span> <span class="n">patternPlayer3</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="kt">uint8_t</span> <span class="n">patternPlayer4</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="c1">//Variable to hold the current pattern selected by the host</span>
<span class="kt">uint8_t</span> <span class="n">patternCurrent</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="c1">//Setup Button Debouncing</span>
<span class="n">Bounce</span> <span class="n">joystickUP</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinUP</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">joystickDOWN</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinDN</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">joystickLEFT</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinLT</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">joystickRIGHT</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinRT</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button1</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB1</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button2</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB2</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button3</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB3</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button4</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB4</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button5</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB5</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button6</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB6</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button7</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB7</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">button8</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinB8</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">buttonSTART</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinST</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="n">Bounce</span> <span class="n">buttonSELECT</span> <span class="o">=</span> <span class="n">Bounce</span><span class="p">(</span><span class="n">pinSL</span><span class="p">,</span> <span class="n">MILLIDEBOUNCE</span><span class="p">);</span>
<span class="c1">//void Configure Inputs and Outputs</span>
<span class="kt">void</span> <span class="nf">setupPins</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">//Configure the direction of the pins</span>
<span class="c1">//All inputs with internal pullups enabled</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinUP</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinDN</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinLT</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinRT</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB1</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB2</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB3</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB4</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB5</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB6</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB7</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinB8</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinST</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinSL</span><span class="p">,</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span>
<span class="n">pinMode</span><span class="p">(</span><span class="n">pinOBLED</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
<span class="c1">//Set the LED to low to make sure it is off</span>
<span class="n">digitalWrite</span><span class="p">(</span><span class="n">pinOBLED</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">//Update the debounced button statuses</span>
<span class="c1">//We are looking for falling edges since the boards are built</span>
<span class="c1">//for common ground sticks</span>
<span class="kt">void</span> <span class="nf">buttonUpdate</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">joystickUP</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSUP</span><span class="p">]</span> <span class="o">=</span> <span class="n">joystickUP</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">joystickDOWN</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSDN</span><span class="p">]</span> <span class="o">=</span> <span class="n">joystickDOWN</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">joystickLEFT</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSLT</span><span class="p">]</span> <span class="o">=</span> <span class="n">joystickLEFT</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">joystickRIGHT</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSRT</span><span class="p">]</span> <span class="o">=</span> <span class="n">joystickRIGHT</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button1</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB1</span><span class="p">]</span> <span class="o">=</span> <span class="n">button1</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button2</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB2</span><span class="p">]</span> <span class="o">=</span> <span class="n">button2</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button3</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB3</span><span class="p">]</span> <span class="o">=</span> <span class="n">button3</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button4</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB4</span><span class="p">]</span> <span class="o">=</span> <span class="n">button4</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button5</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB5</span><span class="p">]</span> <span class="o">=</span> <span class="n">button5</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button6</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB6</span><span class="p">]</span> <span class="o">=</span> <span class="n">button6</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button7</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB7</span><span class="p">]</span> <span class="o">=</span> <span class="n">button7</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button8</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB8</span><span class="p">]</span> <span class="o">=</span> <span class="n">button8</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonSTART</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSST</span><span class="p">]</span> <span class="o">=</span> <span class="n">buttonSTART</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonSELECT</span><span class="p">.</span><span class="n">update</span><span class="p">())</span> <span class="p">{</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSSL</span><span class="p">]</span> <span class="o">=</span> <span class="n">buttonSELECT</span><span class="p">.</span><span class="n">fallingEdge</span><span class="p">();}</span>
<span class="p">}</span>
<span class="c1">//ProcessInputs</span>
<span class="c1">//Button layout on fight stick</span>
<span class="c1">// SL ST</span>
<span class="c1">//5 6 7 8</span>
<span class="c1">//1 2 3 4</span>
<span class="c1">//X360 Verson</span>
<span class="c1">// BK ST</span>
<span class="c1">//X Y RB LB</span>
<span class="c1">//A B RT LT</span>
<span class="kt">void</span> <span class="nf">processInputs</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">//Zero out button values</span>
<span class="c1">//Start at 2 so that you can keep the message type and packet size</span>
<span class="c1">//Then fill the rest with 0x00's</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span> <span class="n">i</span><span class="o"><</span><span class="mi">13</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x00</span><span class="p">;}</span>
<span class="c1">//Button Packet 1 (usb data array position 2)</span>
<span class="c1">//SOCD cleaner included</span>
<span class="c1">//Programmed behavior is UP+DOWN=UP and LEFT+RIGHT=NEUTRAL</span>
<span class="c1">//DPAD Up</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSUP</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_1</span><span class="p">]</span> <span class="o">|=</span> <span class="n">DPAD_UP_MASK</span><span class="p">;}</span>
<span class="c1">//DPAD Down</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSDN</span><span class="p">]</span> <span class="o">&&</span> <span class="o">!</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSUP</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_1</span><span class="p">]</span> <span class="o">|=</span> <span class="n">DPAD_DOWN_MASK</span><span class="p">;}</span>
<span class="c1">//DPAD Left</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSLT</span><span class="p">]</span> <span class="o">&&</span> <span class="o">!</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSRT</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_1</span><span class="p">]</span> <span class="o">|=</span> <span class="n">DPAD_LEFT_MASK</span><span class="p">;}</span>
<span class="c1">//DPAD Right</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSRT</span><span class="p">]</span> <span class="o">&&</span> <span class="o">!</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSLT</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_1</span><span class="p">]</span> <span class="o">|=</span> <span class="n">DPAD_RIGHT_MASK</span><span class="p">;}</span>
<span class="c1">//Button Start OR Select OR Both (XBOX Logo)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSST</span><span class="p">]</span><span class="o">&&</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSSL</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_2</span><span class="p">]</span> <span class="o">|=</span> <span class="n">LOGO_MASK</span><span class="p">;}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSST</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_1</span><span class="p">]</span> <span class="o">|=</span> <span class="n">START_MASK</span><span class="p">;}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSSL</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_1</span><span class="p">]</span> <span class="o">|=</span> <span class="n">BACK_MASK</span><span class="p">;}</span>
<span class="c1">//Button Packet 2 (usb data array position 3)</span>
<span class="c1">//Button 1</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB1</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_2</span><span class="p">]</span> <span class="o">|=</span> <span class="n">A_MASK</span><span class="p">;}</span>
<span class="c1">//Button 2</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB2</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_2</span><span class="p">]</span> <span class="o">|=</span> <span class="n">B_MASK</span><span class="p">;}</span>
<span class="c1">//Button 5</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB5</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_2</span><span class="p">]</span> <span class="o">|=</span> <span class="n">X_MASK</span><span class="p">;}</span>
<span class="c1">//Button 6</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB6</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_2</span><span class="p">]</span> <span class="o">|=</span> <span class="n">Y_MASK</span><span class="p">;}</span>
<span class="c1">//Button 7</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB7</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_2</span><span class="p">]</span> <span class="o">|=</span> <span class="n">RB_MASK</span><span class="p">;}</span>
<span class="c1">//Button 8</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB8</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">BUTTON_PACKET_2</span><span class="p">]</span> <span class="o">|=</span> <span class="n">LB_MASK</span><span class="p">;}</span>
<span class="c1">//Triggers (usb data array position 4 and 5)</span>
<span class="c1">//0xFF is full scale</span>
<span class="c1">//Button 3</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB3</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">LEFT_TRIGGER_PACKET</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xFF</span><span class="p">;}</span>
<span class="c1">//Button 4</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB4</span><span class="p">])</span> <span class="p">{</span><span class="n">TXData</span><span class="p">[</span><span class="n">RIGHT_TRIGGER_PACKET</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xFF</span><span class="p">;}</span>
<span class="p">}</span>
<span class="c1">//Select pattern</span>
<span class="c1">//Examines USB packet and sets the correct pattern</span>
<span class="c1">//according to the 3rd byte</span>
<span class="cm">/*
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
*/</span>
<span class="kt">void</span> <span class="nf">LEDPatternSelect</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">//All blinking or rotating</span>
<span class="k">if</span><span class="p">((</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">ALLBLINKING</span><span class="p">)</span><span class="o">||</span><span class="p">(</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">ROTATING</span><span class="p">))</span>
<span class="p">{</span>
<span class="c1">//Copy the pattern array into the current pattern</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">patternCurrent</span><span class="p">,</span> <span class="n">patternBlinkRotate</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
<span class="c1">//Reset the index to beginning of pattern</span>
<span class="n">LEDtracker</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">//Device is player 1</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">FLASHON1</span><span class="p">)</span><span class="o">||</span><span class="p">(</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">ON1</span><span class="p">))</span>
<span class="p">{</span>
<span class="c1">//Copy the pattern array into the current pattern</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">patternCurrent</span><span class="p">,</span> <span class="n">patternPlayer1</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
<span class="c1">//Reset the index to beginning of pattern</span>
<span class="n">LEDtracker</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">//Device is player 2</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">FLASHON2</span><span class="p">)</span><span class="o">||</span><span class="p">(</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">ON2</span><span class="p">))</span>
<span class="p">{</span>
<span class="c1">//Copy the pattern array into the current pattern</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">patternCurrent</span><span class="p">,</span> <span class="n">patternPlayer2</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
<span class="c1">//Reset the index to beginning of pattern</span>
<span class="n">LEDtracker</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">//Device is player 3</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">FLASHON3</span><span class="p">)</span><span class="o">||</span><span class="p">(</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">ON3</span><span class="p">))</span>
<span class="p">{</span>
<span class="c1">//Copy the pattern array into the current pattern</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">patternCurrent</span><span class="p">,</span> <span class="n">patternPlayer3</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
<span class="c1">//Reset the index to beginning of pattern</span>
<span class="n">LEDtracker</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">//Device is player 4</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">FLASHON4</span><span class="p">)</span><span class="o">||</span><span class="p">(</span><span class="n">RXData</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="n">ON4</span><span class="p">))</span>
<span class="p">{</span>
<span class="c1">//Copy the pattern array into the current pattern</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">patternCurrent</span><span class="p">,</span> <span class="n">patternPlayer4</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
<span class="c1">//Reset the index to beginning of pattern</span>
<span class="n">LEDtracker</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">//If pattern is not specified perform no pattern</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="c1">//Copy the pattern array into the current pattern</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">patternCurrent</span><span class="p">,</span> <span class="n">patternAllOff</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
<span class="c1">//Pattern is all 0's so we don't care where LEDtracker is at</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">//Cycle through the LED pattern</span>
<span class="kt">void</span> <span class="nf">LEDPatternDisplay</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//Grab the current time in mS that the program has been running</span>
<span class="kt">uint32_t</span> <span class="n">currentMS</span> <span class="o">=</span> <span class="n">millis</span><span class="p">();</span>
<span class="c1">//subtract the previous update time from the current time and see if interval has passed</span>
<span class="k">if</span> <span class="p">((</span><span class="n">currentMS</span> <span class="o">-</span> <span class="n">previousMS</span><span class="p">)</span><span class="o">></span><span class="n">interval</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//Set the led state correctly according to next part of pattern</span>
<span class="n">LEDState</span> <span class="o">=</span> <span class="n">patternCurrent</span><span class="p">[</span><span class="n">LEDtracker</span><span class="p">];</span>
<span class="c1">//update the previous time</span>
<span class="n">previousMS</span> <span class="o">=</span> <span class="n">currentMS</span><span class="p">;</span>
<span class="c1">//increment the pattern tracker</span>
<span class="n">LEDtracker</span><span class="o">++</span><span class="p">;</span>
<span class="c1">//write the state to the led</span>
<span class="n">digitalWrite</span><span class="p">(</span><span class="n">pinOBLED</span><span class="p">,</span> <span class="n">LEDState</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">//if we increased ledtracker to 10, it needs to rollover</span>
<span class="k">if</span> <span class="p">(</span><span class="n">LEDtracker</span><span class="o">==</span><span class="mi">10</span><span class="p">)</span> <span class="p">{</span><span class="n">LEDtracker</span><span class="o">=</span><span class="mi">0</span><span class="p">;}</span>
<span class="p">}</span>
<span class="c1">//Setup</span>
<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">setupPins</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">//Poll Buttons</span>
<span class="n">buttonUpdate</span><span class="p">();</span>
<span class="c1">//Process all inputs and load up the usbData registers correctly</span>
<span class="n">processInputs</span><span class="p">();</span>
<span class="c1">//Check for bootloader jump</span>
<span class="k">if</span> <span class="p">(</span><span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSUP</span><span class="p">]</span> <span class="o">&</span> <span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB1</span><span class="p">]</span> <span class="o">&</span> <span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSB5</span><span class="p">]</span> <span class="o">&</span> <span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSST</span><span class="p">]</span> <span class="o">&</span> <span class="n">buttonStatus</span><span class="p">[</span><span class="n">POSSL</span><span class="p">])</span>
<span class="p">{</span>
<span class="n">_reboot_Teensyduino_</span><span class="p">();</span>
<span class="p">}</span>
<span class="c1">//Update Buttons</span>
<span class="n">XInput</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">TXData</span><span class="p">,</span> <span class="n">USB_TIMEOUT</span><span class="p">);</span>
<span class="c1">//Check if packet available and parse to see if its an LED pattern packet</span>
<span class="k">if</span> <span class="p">(</span><span class="n">XInput</span><span class="p">.</span><span class="n">available</span><span class="p">()</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//Grab packet and store it in RXData array</span>
<span class="n">XInput</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="n">RXData</span><span class="p">,</span> <span class="n">USB_TIMEOUT</span><span class="p">);</span>
<span class="c1">//If the data is an LED pattern command parse it</span>
<span class="k">if</span><span class="p">(</span><span class="n">RXData</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">LEDPatternSelect</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">//Process the LED pattern if the style is onboard LED</span>
<span class="k">if</span> <span class="p">(</span><span class="n">LEDSTYLE</span> <span class="o">==</span> <span class="n">ONBOARD_LED</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">LEDPatternDisplay</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>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.</p>
<p>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.</p>
<p><a href="/assets/pdfs/fightstick/fightstick-bitmask-examples-2.pdf">FightStick Bitmask Examples 2</a></p>
<p>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.</p>
<p>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.</p>Zack LittellNow 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:Wiring up the TeensyLC2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/wiring-up-teensylc<p><img src="/assets/images/fightstick/teensylc_front_pinout.png" alt="TeensyLC Pinout" width="300px" />
<img src="/assets/images/fightstick/teensylc-my-pinout.jpg" alt="My Pinout" width="300px" /></p>
<p>Above is a crude drawing of the pin-out used for this fight stick. The board is based on active low inputs, so we switch them to ground. I used all three grounds available as thru-hole. I had to create a splice for ground on start and select. Using the three grounds allowed me to separate the wires into neat sleeves that ran to each row of buttons and the joystick. Next I will show you the layout of the buttons on the stick:</p>
<p><img src="/assets/images/fightstick/button-layout.png" alt="Button Layout" width="300px" /></p>
<p>Finally I will go ahead and finish this up with various shots of the stick and how it was wired up. We decided that creating the harnesses individually first would be easiest. So a harness was created for both rows, start select, and the joystick. Then they were cut at the correct length and soldered to the teensy board. The wires were kept clean by sleeving each harness with 1/4” PET Sleeving and using 3/8” Heat Shrink on the ends. For the wire we just used 22AWG stranded with 0.110”x0.020” Female Disconnects. Also remember to test the joystick with a voltmeter. The switches can be misleading and you need to remember that because of the pivot point a direction actually activates the micro switch on the opposite side.</p>
<p><img src="/assets/images/fightstick/img_20150501_161257376_hdr.jpg" alt="Teensy with Wire Harness" width="300px" />
<img src="/assets/images/fightstick/img_20150521_194217542.jpg" alt="Teensy Mounted" width="300px" /></p>Zack LittellThe XBOX 360 Controller2015-07-07T00:00:00-05:002015-07-07T00:00:00-05:00/fightstick/xbox360-controller<p><img src="/assets/images/fightstick/message-analyzer.jpg" alt="Message Analyzer" /></p>
<p>With the need for drivers, there was no way to roll my own solution. Asking everyone to put their 64-bit version of windows into test mode just to use this controller seemed a little insane. My only real option at this point was to imitate something that existed. It most likely would have been easier to reproduce a more simple PC only controller (ie the F310). The PC only controllers have a lot less interfaces and endpoints, but I already had 360 controllers and did not feel like buying anything new.</p>
<p>My favorite resource for how the controller operates is the <a href="http://free60.org/wiki/GamePad">Free60 gamepad</a> wiki page. The 360 controller is a HID controller with the HID report descriptor removed and the device class, subclass, and protocol set to 0xFF (255). This ensures that the HID driver does not attach to the device so the Microsoft driver can pick it up. The vendor ID is 0x45e (Microsoft) and the product ID is 0x028e. Once you set the device to the same VID and PID the driver will attach to the driver and attempt to enumerate it.</p>
<p>There are only two descriptors you need to worry about for the controller. This is because there is no HID report descriptor. There is the device descriptor, which tells the host things such as USB version, device class, device subclass, device protocol, max packet size for endpoint 0, VID, PID, etc. The other descriptor is the configuration descriptor, which tells the host about the device configuration, each interface, and their endpoints. The configuration descriptor listed on Free60 is not the entire descriptor. I had to pull one from the controller using Microsoft Message Analyzer. There is some padding included in the controller’s actual configuration and that padding needs to be there for the device to correctly enumerate. I will try to do my best at explaining the configuration descriptor. This will not be an in depth explanation of the entire descriptor. You should read the comments included in my code, <a href="http://www.beyondlogic.org/usbnutshell/usb1.shtml">USB in a Nutshell</a>, and consider picking up a copy of <a href="http://www.amazon.com/USB-Complete-Developers-Guide-Guides/dp/1931448280/ref=pd_sim_14_5?ie=UTF8&refRID=1EMXEMJW7C2CJ5G1G1FD">USB Complete</a>. A fair warning though, USB complete focuses mainly on the PC side of coding for USB devices. When I first studied USB for embedded devices I needed to create PC side software to configure the device over generic HID. Jan Axelson’s book was a tremendous resource in developing my PC USB library and understanding the underlying elements of USB.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// USB Configuration Descriptor. This huge descriptor tells all</span>
<span class="c1">// of the devices capbilities.</span>
<span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">config_descriptor</span><span class="p">[</span><span class="n">CONFIG_DESC_SIZE</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="c1">// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10</span>
<span class="mi">9</span><span class="p">,</span> <span class="c1">// bLength;</span>
<span class="mi">2</span><span class="p">,</span> <span class="c1">// bDescriptorType;</span>
<span class="n">LSB</span><span class="p">(</span><span class="n">CONFIG_DESC_SIZE</span><span class="p">),</span> <span class="c1">// wTotalLength</span>
<span class="n">MSB</span><span class="p">(</span><span class="n">CONFIG_DESC_SIZE</span><span class="p">),</span>
<span class="n">NUM_INTERFACE</span><span class="p">,</span> <span class="c1">// bNumInterfaces</span>
<span class="mi">1</span><span class="p">,</span> <span class="c1">// bConfigurationValue</span>
<span class="mi">0</span><span class="p">,</span> <span class="c1">// iConfiguration</span>
<span class="cp">#ifdef DEVICE_ATTRIBUTES
</span> <span class="n">DEVICE_ATTRIBUTES</span><span class="p">,</span> <span class="c1">// bmAttributes</span>
<span class="cp">#else
</span> <span class="mh">0xC0</span><span class="p">,</span>
<span class="cp">#endif
</span> <span class="cp">#ifdef DEVICE_POWER
</span> <span class="n">DEVICE_POWER</span><span class="p">,</span> <span class="c1">// bMaxPower</span>
<span class="cp">#else
</span> <span class="mi">50</span><span class="p">,</span>
<span class="cp">#endif
</span></code></pre></div></div>
<p>The configuration descriptor first tells the PC it is a configuration type descriptor (0x02) with a length of 9. It then tells us that this is describing configuration 1 and that there are 4 interfaces in this configuration. The device is bus powered and has a max power draw of 500mA. It describes the total length of the configuration descriptor as 153 which we can get from the original controller descriptor or by counting bytes. However, as I discussed earlier, you will notice that the descriptor listed at Free60 comes out to 93 or 94 bytes. This is because the padding is missing and trust me you need the padding. Interface 0 has an interface class of 0xFF, subclass 0x5D, and a protocol of 0x01. This interface is actually the only one that concerns us on the PC. It contains two endpoints, 0x81 (Endpoint 1 in) and 0x02 (Endpoint 2 out). The directions are based from the host’s point of view and are described in the MSB of the endpoint address. With setting the most significant bit to a 1 making the endpoint an IN. This means that all button reports going TO the PC with be sent on endpoint 0x81, while any LED patterns the PC sends the controller are being RECEIVED by the device on endpoint 0x02. The other endpoints are used for other peripheral such as headsets, chatpads, etc. The only other interface that might be of use to you is the last one, interface 4. It claims it has 0 endpoints, but this is the interface that handles the security handshake with the Infineon security chip. It is the only interface with a string descriptor. I have included this string descriptor just in case someone decides they want to attempt to replicate a man in the middle device similar to what <a href="http://brandonw.net/360bridge/">BrandonW</a> created. In emails between myself and Brandon, he indicated that the 360 will just stop communicating with the controller if this string descriptor for interface 4 doesn’t exist.</p>
<p>The next thing that we need to look at, is the way the controller sends button reports to the host. Once again, Free60 is on top of things with this. However, pay close attention to the indexes provided. The standard bit format of a byte goes: bit 7, bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, and bit 0. Remember this is sent from the device to the host on endpoint 1 in a 20 byte packet. Below is the button report broken down:</p>
<p><a href="/assets/pdfs/fightstick/button-report.pdf">Button Report PDF</a></p>
<p>As you write your code, you may wonder what the button names correlate to on an XBOX 360 controller. Here is a table to help you with that:</p>
<p><a href="/assets/pdfs/fightstick/button-to-xbox-conversion.pdf">Button to XBOX Conversion PDF</a></p>
<p>The last endpoint we really care about is endpoint 2 out. This comes into the device from the PC and contains messages. The only message we care about is message type 0x01, which is LED control. The packet will come in 3 bytes with byte 1 being message type and byte 2 as the length. The 3rd byte contains the pattern to be performed. Below are the different patterns that the controller can produce:</p>
<p><a href="/assets/pdfs/fightstick/led-report.pdf">LED Report PDF</a></p>Zack Littell