Vive Tracker – Quick & Dirty Documentation

Vive Tracker – Quick & Dirty Documentation

*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *

*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *

Hi friends – So I’ve spent the last few days familiarizing myself with (and troubleshooting) USB control of the Vive Tracker.  I wanted to simply get this information recorded quickly as to enable other developers to send data packets and edit controller state information of the Vive Tracker, and so because I’m prioritizing speed I can guarantee it’s not going to read gracefully.  I intend to rewrite this in a more properly documented report sometime in the future (I seem to be in a workflow of ~one blog post every other month).

So, getting into the documentation: Below is various levels of abstraction on how I altered the Vive Tracker’s inputs.  All documentation is relevant as of 27 March, 2017; it will likely change in the coming months!  I programmed on an Atmel AVR (the family of microcontrollers used in Arduino) AT90USB647 chip at 8 MHz and 3.3V with an external 5V power supply to the USB lines.  It should be noted that the Vive Tracker requires a 5V source to initiate USB communications.  Additionally worth noting, the Tracker does not externally supply power, which means accessory makers at this time will need to implement a secondary battery into their USB-driving designs (this means to use a Vive Tracker USB accessory, users will need to charge both the Tracker and the accessory separately, and accessory manufacturing costs will increase due to the need of an independent battery).

1. The Feature Report

See Pages 50 – 52 (.pdf file pages 60 – 62) of the USB HID Specification 1.11 for reference on HID Set_Report requests.  This is the format for shipping out an HID Feature Report, values specific to the Vive Tracker payloads:

bmRequestType: 0b00100001      // See Page 50 of HID Spec for bit descriptions
bRequest:      0x09            // Value for SET_REPORT
wValue:        0x0300          // MSB=Report Type (0x03, value for Feature report)
                               // LSB=Report ID (0x00, report IDs not used – Page 51)
wIndex:        2               // HTC’s selected Descriptor Index (I think)
wLength:       sizeof(payload) // Size in bytes of the coming payload (Data field, next line)
Data:          {Data_Set_ADDR, Length_of_Following_Data, bData[0], bData[1], … , bData[n]}

MSB = Most Significant Byte, LSB = Least Significant Byte.

Below is an abstracted example for defining an accessory connection and sending a payload declaring the trigger held down halfway and menu button pressed.  Please note that as of 27 March, 2017 the bit & byte values on Pages 25 – 26 of the HTC Vive Tracker Developer Guidelines v1.3 are incompletely, and in some cases incorrectly, defined (rectified later, below).

bmRequestType: 0b00100001
bRequest:      0x09
wValue:        0x0300
wIndex:        2
wLength:       5
Data:          {0xB3, 3, 0x03, 0x00, 0x00}


bmRequestType: 0b00100001
bRequest:      0x09
wValue:        0x0300
wIndex:        2
wLength:       12
Data:          {0xB4, 10, 0x00, 0b00000100, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00}

2. Rectified Byte Definitions

The manual fails to mention or make clear that the Data_Set_Address and Data_Count need to be included in the feature report.  Bytes in my examples are read left-to-right, indexed at zero.  Bits are read right-to-left, also indexed at zero.

1 – RESERVED (CHARGE ENABLE, can’t be edited yet – Tracker will always leech power from host)
{ 0xB3, 3, HOST_TYPE, 0x00, 0x00 }


1.0 – TRIGGER (OVERRIDES ANALOGUE TRIGGER BYTE TO BE 0xFF, or at least I can confirm this occurs within Unity)
1.1 – GRIP
1.3 – SYSTEM (Yes, it will open the dashboard)
2 – TOUCHPAD_X_LSB (LSB = Least Significant Byte)
3 – TOUCHPAD_X_MSB (MSB = Most Significant Byte)
7 – TRIGGER (SHOULD BE TRIGGER_MSB, OVERRIDES TRIGGER BIT OF BUTTON BYTE IF WRITTEN TO 0xFF.  Since byte 6 is unused we lose a whole byte of possible data transfer.)
{ 0xB4, 10, 0x00, BUTTONS, PAD_X_LSB, PAD_X_MSB, PAD_Y_LSB, PAD_Y_MSB, 0x00, TRIGGER, 0x00, 0x00 }

3. Library Interfacing

USB protocols are not something you can just throw together in an afternoon – you’re going to want a library to handle all that overhead.  For the code snippet below, I used the Lightweight USB Framework for AVRs (LUFA) library by Dean Camera, building upon the GenericHIDHost demo found in …/LUFA 151115/Demos/Host/LowLevel/GenericHIDHost; coded in C++.

First I edited the makefile to reflect my chip.  This blog post by Joonas Pihlajamaa really helped me get started.  Below are the values I changed:

  MCU = at90usb647
F_CPU = 8000000


I then went into GenericHIDHost.h and removed any references to on-board LEDs or a serial output monitor, as my custom board did not have a definitions file for these things.  Then within GenericHIDHost.c, this was the main function to perform the same task that was described at the end of Section 1 of this post:

int main(void)
.   SetupHardware();
.   GlobalInterruptEnable();
.   for(;;)
.   {
.       uint8_t payload0xB3[5] = {0xB3, 3, 0x03, 0x00, 0x00};
.       uint8_t payload0xB4[12] = {0xB4, 10, 0x00, 1 << 2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00};
.       WriteNextReport(payload0xB3, 0x00, REPORT_TYPE_FEATURE, 5);
.       USB_USBTask();
.       WriteNextReport(payload0xB4, 0x00, REPORT_TYPE_FEATURE, 12);
.       USB_USBTask();
.   }


I’ve not yet tested how necessary it is to write the 0xB3 report every frame, but I do know this bit got the Tracker to work reliably.  The other code I had to change was in the function WriteNextReport at the end of GenericHIDHost.c.  Apparently wIndex was set to 0 by default, however HTC’s Vive Tracker requires wIndex to be 2.

USB_ControlRequest = (USB_Request_Header_t)
.bmRequestType = 0b00100001,
.bRequest      = 0x09,
.wValue        = 0x0300,
.wIndex        = 2,
.wLength       = ReportLength,

4. A Low(er)-Level Look

Solely from picking apart Dean’s library, these would seem to be the primary steps that need to be performed in order to successfully communicate with the Vive Tracker:

– Initiate USB as host
– Enumerate the attached USB device
– Read and process the device’s configuration descriptor
– Set the device to its initial configuration (Dean remarks that it’s unlikely to have more than one configuration)
>   bmRequestType: 0b10000000
>   bRequest: 0x09
>   wValue: 1
>   wIndex: 0
>   wLength: 0
>   Data: NULL
– At this stage, all the setup is now complete!
– To send a report:
>   Prepare the bus/pipe
>   Feed it the header (bmRequestType, bRequest, wValue, etc.)
>   Feed it the datastream (starting with {0xB_, etc.} )
>   Return the bus/pipe to its initial state
– Perform any other needed USB host tasks


This section isn’t 100% solidified for me, simply because Dean did such a great job at constructing this library that once I was able to format the payload correctly for the Tracker, I had no more need to dive into the low-level for troubleshooting.  If you’re using AVRs for your microcontroller, I highly recommend Dean’s LUFA library!

I am by no means proficient in USB HID communications, but if you run into any road blocks then feel free to reach out to me, perhaps I’ll be able to offer some advice as a result of my own troubleshooting.

I made note of this at the top – if you think that it would be valuable for me to develop this into an library for USB Host-capable Arduino boards, please comment your interest below.  It could definitely help out if you explain what you would hope to use said library to do with the Vive Tracker.

EDIT: Reddit user /u/matzmann666 has wonderfully created a library for the Arduino Due ARM-based microcontroller!  Check out their GitHub page here:

And lastly, I’d like to send a thousand thanks to Dario Laverde from HTC, he was a great help in troubleshooting a lot of the feature report content, and I wouldn’t have Vive Trackers to work with if it weren’t for him.  Thank you, Dario!

Alright, that’s all I’ve got for this quick and dirty documentation.  Next time you hear from me should be a little more developed, but I really wanted to get this info out there fast!  I hope some folks out there find it useful :)

33 thoughts on “Vive Tracker – Quick & Dirty Documentation

  1. yes, would love a Vive Tracker Arduino library! found your blog looking for more details on the Tracker USB protocol exactly because I want to make a custom controller this way.

  2. Great to see you working on tracking , I haven’t checked back in a while. So want to see this get to market. I’m launching a VRcade , this weekend and would love it to feature for our customers to use. Don’t like treadmills, foot tracking is the future. Keep on tracking. :)


  3. Thank you for documenting this! It helped me create an Android app that can use the tracker to pretend its trigger has been activated for as long as I press a button on the touch screen.

    It looks like you need to send the host report only once, and pretty quickly after the tracker is connected via USB – otherwise SteamVR will show the tracker as connected, but not tracking. Both host types seem to be working for me, not sure what the difference is.

    The activity report seems to be completely independent.

    Thanks again!

  4. I would also be interested in an Arduino Vive Tracker library! Looking forward to your updates, I am about to venture down this road as well. Hopefully I can contribute to the cause as well. Thanks again for your work on this!

  5. Peter, how did you figure out that 0x0F was a half-press for the trigger?

    I tried testing this today using existing code that outputs analog values (between 0-1.0) for a regular Vive controller but sending 0x0F for TRIGGER_MSB caused Unity to output a value of 1.

    1. Hi Matt – Be sure to be mindful of your buttons byte (byte 1 in my “documentation”). If you have the trigger set to 1 in the buttons byte, then that will override any analogue trigger values! If that isn’t the issue, feel free to further describe the situation; I’ll see if I can be of assistance.
      Awesome writeup on Medium, by the way :)

      1. Took another shot at it today and you’re right, I must have been sending the button data as well.

        Is there a chance you have a typo above and the actual value for a half way down trigger is 0x7F? that’s what I was seeing. Since we’re limited to TRIGGER_MSB only, we get a range of 0-255.

        1. Ah yes, my apologies! As the title says, this documentation was quick & dirty, so it would seem you found some of the dirty parts, haha. 0x7F returns 0.4980392 on the trigger, and 0x80 returns 0.501968. I’ve corrected that in the post – thank you!

          I’m trying to figure out how the trackpads are packaged, and they’re proving to be something that isn’t your usual two’s compliment -32768 to 32767 range… Will report back as I figure that one out.

          1. Hi, thank you so much for the info.

            Have you worked out the trackpads already?

            I work with the same trackpads and I was thinking that if your documentation was incomplete perhaps I can send you the docs I used to get the trackpads communicating with my micro over spi. It gives the ranges of raw values and all that.

            1. Hi Jack! Please send me a mail at We’re currently doing some experiments with SPI communications and the touchpad, and any other input / experience would be greatly helpful.

              As for the Vive Tracker, we talked with Valve about the Trackpad noise we were receiving, and it turns out that there was some filtering going on with the SteamVR driver. They kindly removed said filters for the Vive Tracker, so now we receive clean and raw data :) I should be updating this post to reflect that in the next few weeks.

  6. Peter, thanks for this writeup. I thought I saw somewhere that it’s not possible to send data via a serial COM port using the pogo pins, is that true? I have an accessory that I’m making which needs to emulate a specific USB HID device (with a specific HID descriptor), and right now I can pretty easily achieve that using a Teensy — except that I have to have the tracker mounted to my accessory as well as have my accessory wired to my PC via a USB cable.

    Also, the Teensy is receiving data over USB, as well as sending it over USB (sending via COM port, receiving via software/emulated Serial). I’d love to have the Teensy use the pogo pins for sending/receiving data while emulating a USB HID device, but I’m guessing the Vive Tracker can’t do all of that.

    I was thinking about possibly using a Bluetooth master/slave combo (Teensy w/bluetooth dongle host connected to my PC, and Teensy w/bluetooth module client connected to my accessory) but I thought that might introduce a bit of lag which could ruin the overall experience.

    In the end, since I want to reduce the number of dependencies and lower costs, I might just end up going with having it wired to the headset via the additional USB port in the headset, and have the wire routed through the headset’s strap like the other wires are. It wouldn’t require battery and bluetooth, but it wouldn’t be wireless.

    I think a cleaner solution would be to use the Triad Semiconductor kit to make a fully custom tracked object. But I’m not ready for that yet!

    1. Hi James,

      If you need to send a custom HID descriptor, then to my knowledge you unfortunately are not able to send that via the Vive Tracker. The pogo pins are for binary button presses and receiving haptic pulses.

      Introducing Bluetooth shouldn’t present much lag – the time it takes for for the Vive Tracker to render motion is something around 18 ms (source: comments section). The latency that Bluetooth Low Energy (not Bluetooth Classic!) introduces is only 6ms. So long as your calculations aren’t crazy intense, Bluetooth LE will arrive with plenty of time to process and spare.

      Going wired will cause a lot less headache for initial prototyping, but I definitely favour wireless when it comes to final implementations of dynamic hardware.

      Making custom tracked objects could certainly get you around these problems, however there would be a lot of overhead and need to edit some of Valve’s firmware (read: a lot _more_ overhead). I’d recommend starting out wired to prove the concept, then integrate some Bluetooth LE once you solidify your design :)

  7. As I understand correctly that new Library in arduino to support vive tracker is only for accessory makers? So you can’t get position of sensor?

    1. Affirmative, Luke. To my knowledge, nobody has yet tried reading the HID report off of the Vive Tracker and then decode the positional data from it. I do not know if this is possible without a SteamVR handshake, but it is *certainly* something worth exploring!

      1. Thnx for the message. Yes that’s right, I think how to make handshake with usb and tracker without SteamVR, I can’t imagine that right now. I want to bypass that. Maybe OSVR is nice alternative to Steam. Or create own tracker which will send info from photodiodes.

  8. First off, thank you so much for posting this. It’s been kind of difficult finding this kind of information so far regarding building a custom controller.

    That said, do you have any recommendations on which particular Arduino board to use?

    I’m thinking the Gemma or the Micro would probably be most appropriate for the use case, but this would be my first Arduino board, so I defer to your experience.

    1. Just noticed you mention that /u/matzmann666 created the Arduino library off of the Due. Is that a requirement or just what they happened to use?

  9. Thanks for the write up.

    Have you had any success sending to a peripheral from the game engine via the trackers USB? For instance, telling an LED to illuminate from in game.

    1. We’ll be experimenting with this in some upcoming prototypes. I anticipate it’ll be done using the Tracker’s haptic pulse output, potentially coding various signals into it. I don’t believe there are any other pipes that would allow for more general communication back to the accessory from the game engine beyond the haptic pulses though.

  10. hello, Im little bit disapointed because i made an hid joystick with a pic18 and so i assume that he is working as a device not a host. It work really well on my pc and now I want to get the value of all axis through my vive tracker. how can I do ?
    should I just put an external battery to my device ?

    1. So you’ll need a way to communicate to the Vive Tracker as a USB host, and you have correctly assumed that the pic18 is currently a USB device. This can be achieved in two easy ways: 1) Purchase an Arduino Due and use the library I link to at the end of my post. 2) Use an Arduino USB host shield with most other Arduinos and implement Vive Tracker control based on this and HTC’s documentation. As far as transferring values from your HID joystick axes to the Vive Tracker goes, that should be as easy as packaging them into 16-bit signed integers and sending the x- and y-axes through the Vive Tracker’s respective bytes. Let me know if you have any further questions.

    1. I’m so glad that this documentation was able to help you! Android is a far more convenient tool than a custom circuit, haha. Would you mind adding credit to this page in your post? It’s always nice to leave a paper trail ;)

Leave a Reply

Your email address will not be published. Required fields are marked *