USB Human Interface Device (HID)

For this HID assignment, I wanted to build a steering wheel for computer racing games. Almost all of the computer racing games uses left and right keys for steering and up key for gas with a few additional keys that varies for other purposes depending on the game. So I decided to map 4-5 keys from keyboard to my control surface using this keyboard library. I used tilt detection of an accelerometer’s x-axis value to control the steering and a few switches for gas and other functions.

IMG_0472.JPG


User flow:

User step on the foot pedal switch to apply gas, and turn the steering wheel to control the direction, and press the button on the right side of the wheel to boost and the one on the left for jump.

Screen+Shot+2019-03-13+at+9.02.44+PM.jpg

Process:

When picking the best sensor for the purpose of this project, I looked into the difference between gyroscope and accelerometer. Accelerometer senses linear movement, rate of change of the linear velocity of an object in (m/s2), and is good for tilt detection or vibration. Gyroscopes measures rotation from the balanced position, angular velocity or speed of rotation in (°/s).  Gyroscope would not measure anything when there is no rotation on a particular axis, but accelerometer measures the total acceleration including the static acceleration from gravity it would experience even when it’s not moving. My controller needs to sense the tilt of the wheel so that even if it stays still it keeps sending left or right key press as long as it’s tilted from the neutral position, therefore, accelerometer would fit for the purpose.

Given that this is an application-specific controller for racing games, I picked the game Distance for my controller. I played the game million times just with my keyboard to feel the way a player would press and release the directional keys, and I concluded that the difference between a small turn and a sharp turn is the frequency a key is pressed and released. Short intermittent presses mean small adjustment of direction, whereas longer presses mean sharper turns. Therefore, to reflect different degree of turns, I had to adjust the key holding time and key releasing time till the next press by adding delays to my program. I tilted my accelerometer on the breadboard when I tested the game as I would with a steering wheel, after many trials and errors adjusting the delay values, I decided that there needs to be an exponential relationship between acceleration and key press delay with a curve that looks like the right. So I ended up with 3 sets of values and used a plotter to get a function of key press delay against acceleration. I had my keyboard viewer opened all the time so I could see how the keyPress and keyRelease pattern was changing as I turned the wheel.

Breadboard View

Breadboard View



Code: link to Github



Fabrication:

I found a big motor from the junk shelf that would serve the purpose of stabilizing my controls on the table with its heavy weight as well as holding up my steering wheel’s on its rotating axis with the shaft. I applied a PVC tube on top of the shaft and drilled holes on it so wires on the buttons could run through it and go into the box. I also stuck some clay either side of the motor and have a stick installed perpendicular to the shaft that houses the accelerometer and stops the wheel from turning when it hits the clay. Later I learned that I could have used a slip ring to run my wires through the shaft so that they don’t get tangled when the system keeps rotating in the same direction. I laser cut a stack of cardboard for my steering wheels and used a circular plate to attach it to the tube.

IMG_0456.JPG
IMG_0457.JPG
IMG_0460.JPG


Challenge:

Software wise, my biggest challenge was to calibrate the amount of turns and get the steering wheel to send a range of turns achieved by adjusting the frequency of intermittent key press and key press time. After I finished fabrication, there seemed to be an issue with my accelerometer. Every time I uploaded my code, after a random amount of time, accelerometer would stop getting readings and my steering would stop working. I excluded all the hardware issues and figured out that it’s the delays in the software that caused the problem. I learned the lesson that it is not a very good practice to have too many delays in the program especially when it’s inside loops of conditional statements. One solution was to use millis(), another is to use a Scheduler. I tried the scheduler so I could run multiple loops at the same time without the delays affecting each other, but for some reason the program only runs the main loop but never enters the other loops.