HID Gyroscope Device/Gamepad

Status
Not open for further replies.
Hello all,

Looking for some advice on a project I am about to begin. I have not really programmed much since college, and decided to just jump right back in with this project. I figured if I break it in to chunks, it will be more manageable + it will be easier to relearn along the way.

I am going to be creating an open-source simple (at least hardware-wise) gyro peripheral that consists of a Teensy LC and an ST ISM330DHCX 9DoF IMU (breakoutboard from Adafruit: https://www.adafruit.com/product/4502) communicating via I2C.

While that part shouldn't be too difficult, I also aim to have the raw gyroscope and accelerometer data transferred to a PC. I am aiming to have the device show up as an HID compliant device, but I am not sure which type, or if I can configure it as an HID Gamepad and still transmit raw gyro/accel data. Does anyone have any advice on how to go about this?

My current project programming plan:
1) Get IMU communicating via I2C with the Teensy -verify with serial outputs
2) Trim down code to just output raw gyro/accel data
3) Create HID - specific code (i.e. HID report descriptor, rename Teensy, VID/PID, etc) ---> I anticipate this taking the most time, since this involves concepts I am not familiar with.

I have found these pages describing USB HID, and some using Teensy as guidance:
https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/
https://blog.hamaluik.ca/posts/making-a-custom-teensy3-hid-joystick/

On a different note, Adafruit has libraries for that IMU, and they are linked with their own I2C library. Would it be better to this route, or should I modify code to get the IMU library to work with the Teensy I2C library, since it apparently is more efficient/faster?

If anyone has any advice/feedback/information they could, that would be greatly appreciated!
 
I was looking through the USB Usage tables / descriptors - not the easiest to understand the relationship. The table for the Generic Desktop Page shows that Usage ID's 33-35 are Rx,Ry,Rz and correspond to rotations about the X, Y, and Z axes and Usage ID's 40-46 correspond to vectors, and as well as other ID's corresponding to quaternions later on.

So if I am vaguely understanding right, I can assign

//Usage_Page(Generic Desktop)
//Usage(Gamepad)
//Collection(Physical)
//Usage(Rx)
//Usage(Ry)
//Usage(Rz)
//Usage(Vx)
//Usage(Vy)
//Usage(Vz)

etc. and that should transmit correctly? Theoretically speaking. (i know there is a tool to create the HID descriptors, just trying to relay my thoughts well)
 
Theoretically I guess I would have to look at it better myself and dig into the usage tables. Think you are on the right path though
 
I've used maybe 15 adafruit libraries, and I've never had a problem with them getting started. Occasionally I've had to re-write my own to replace them where I've not been happy with the performance, but more often than not they work out the box as I need.
Speed can be a concern, but the question is what sample rate do you need for this? 100Hz perhaps? That's not very fast to a microcontroller.
Also running the I2C faster doesn't change that you'll have to service it every 0.01s (presuming you don't buffer the data), it just might trim down how long you spend servicing it. So making sure whatever else you're doing isn't going to get stuck (like if you were waiting for a message from the host) is usually more important than shaving a few micros off your time. If you're just going to spit this data straight out over USB then my modest experience would be that this is going to be fine as is. You can always use the micros() function to get some timing information and maybe trim it later.

I think your steps are very sensible. For step 1, you can use the example libraries from adafruit just to check it's working how you want. For step 2, you can just find the closest example and cut everything you don't need out, also you might want to change how the data is sent over serial. Usually, example libraries send things using print() to be human-readable over the monitor, but this is often a waste of processing power as your micro turns a number into a string, which isn't really useful for your computer. Having a think about how you're going to send a number over USB so that the host can use it is not difficult, but worth doing well. I often like to send the data exactly as it comes out of the register example -

Say the IMU has a 10bit register for X acceleration, where each bit is 0.25g, and it currently has a value of 0x07 which is 1.75g (I'm just making the numbers up!). You can convert this to a float, then print() the float which converts it to a string, then on your host convert back to a float - or you can just send the 0x07 over the bus and do one conversion on the host. Saving yourself two expensive float to string operations, rounding errors, and at least 2bytes over USB.
Hopefully, that explains what I'm getting at.

Finally, why do you need to have a HID-compliant device? I've always just used the teensy as is. Presumably, you're writing some software elsewhere to work with this data over the USB, so if you're writing the software do you need the HID compliance?
 
I often like to send the data exactly as it comes out of the register example -

Say the IMU has a 10bit register for X acceleration, where each bit is 0.25g, and it currently has a value of 0x07 which is 1.75g (I'm just making the numbers up!). You can convert this to a float, then print() the float which converts it to a string, then on your host convert back to a float - or you can just send the 0x07 over the bus and do one conversion on the host. Saving yourself two expensive float to string operations, rounding errors, and at least 2bytes over USB.
Hopefully, that explains what I'm getting at.

Finally, why do you need to have a HID-compliant device? I've always just used the teensy as is. Presumably, you're writing some software elsewhere to work with this data over the USB, so if you're writing the software do you need the HID compliance?

Essentially, I would not need to do the conversion math to get exact values in deg/s,g, or the quaternion values, I just need to send the readings coming in to the bit register - assign the data for each axes as an uint_8? (though that is only 8, not 10 bits of data, right?)

I want this to show up as an HID-compliant device for ease of use and interaction with preexisting controller remapping software. What this simple hardware configuration will let users do is add gyroscope functions to Xbox controllers (gyro aiming). This can be done via reWASD, which allows users to "group" input devices together and have them appear as a single output. I did not create that software, but since it supports many different peripherals I figured the best thing to do on my end would be to have my device appear as an HID compliant gamepad. Potentially SteamInput could also support it, though I am not sure how their controller remapping stuff works or if it would support two devices like that (but I can use my Xbox controller to emulate keyboard and mouse stuff with it, and at the same time use my kb&m in game, so I would think it would be feasible).

I also would like any future person to just be able to solder the 4 wires required, upload the code, and have it be recognized by the software properly.
 
Ahh, I see. I didn't realise you were working with some software, so my comments about sending the data don't apply sadly.
If you want it to work with that reWASD, you'll need to find out what format it wants its data in and push that out over USB. I imagine there is a standard for gyroscopic games controllers that you can match.

As far as HID compliance, maybe or maybe not. You can select "keyboard + mouse + joystick" as an option for the teensy device type. I've never used this, but I've used teensy as a Midi device, and it's always just shown up in Serato (DJ software) and "teensy midi device" and worked without a fuss. As I understand it, the gyro is another mouse input, so I would
a) see if reWASD will recognise and connect to the teensy
b) if yes, add a simple bit of code (like just moving the mouse up slowly) to see if this input is usable
c) if yes, begin to look at turning IMU data into mouse inputs
 
Status
Not open for further replies.
Back
Top