Vive Tracker – (Relatively) Maintained Documentation

Vive Tracker – (Relatively) Maintained Documentation

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

Hi friends – I’m maintaining this post as I continue working with the Vive Tracker, and will be doing my best to keep up with HTC’s evolving firmware and capabilities.  I focus primarily on USB control of the Tracker, and do not go into interfacing with the pogo pins.  If this is something you all request documentation for, then I’ll take the time to add it.

Please note that if for whatever reason you need to reference my original documentation from March of 2017, it will be archived at this address.  Let’s get into it, shall we?

Below is various levels of abstraction on how I altered the Vive Tracker’s inputs.  Documentation on this page is relevant as of 26 October, 2017.  I started my programming on a breadboarded Atmel AVR (the family of microcontrollers commonly 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 in order for USB communication to work.  Later, I designed a PCB with the same chip, and now a 3.3-to-5V boost converter to control the Tracker in one condensed circuit.  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.  It would appear that developers can permit the Tracker to charge off the accessory while in-communication over USB, but I have yet to confirm this feature.

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.  Referencing pages 26 – 29 (.pdf file pages 29 – 32) of the HTC Vive Tracker Developer Guidlines v1.5, this is the format for shipping out an HID Feature Report with 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 (more on defining bytes below).

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


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

2. Byte Definitions

The following are HTC’s byte definitions, as outlined by their developer guidelines linked above.  I also add my own notes on the usefulness of various bytes (or lack-thereof).

1 – RESERVED (CHARGE ENABLE, not sure if interfacing with this byte has any visible function)
3 – LPF (Low Pass Filter configuration, 0=184Hz, 1=5Hz, 2=10Hz, 3=20Hz.  Lower frequency configurations ignore slower vibrations of the Tracker IMU, such as from accessory haptics)


1.0 – TRIGGER (Following a SteamVR driver update, this value is independent of the analogue trigger byte)
1.1 – GRIP
1.3 – SYSTEM (Yes, it will open the dashboard)
1.4 – TOUCHPAD (Following the same SteamVR driver update, this bit no longer requires TOUCHPAD_CONTACT to be high)
1.5 – TOUCHPAD_CONTACT (Does not require touchpad axis values)
1.6 – RESERVED (But whyyy?)
2 – TOUCHPAD_X_LSB (LSB = Least Significant Byte)
3 – TOUCHPAD_X_MSB (MSB = Most Significant Byte)
6 – NO VISIBLE FUNCTION (Technically TRIGGER_LSB, this byte is irrelevant because SteamVR only accepts an 8-bit analogue trigger value and therefore this byte is ignored)
7 – TRIGGER (Technically TRIGGER_MSB, values remain unaffected by TRIGGER button)
8 – RESERVED (Technically BATTERY_LSB, we don’t have access to these bytes)
9 – RESERVED (Technically BATTERY_MSB)

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 (Trust me, this is coming from someone that would rather write their own scripts for basic math functions so everything is in my control – you’re going to want that library).  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[6] = {0xB3, 3, 0x03, 0x00, 0x00, 0};
.       uint8_t payload0xB4[12] = {0xB4, 10, 0x00, 1 << 2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00};
.       WriteNextReport(payload0xB3, 0x00, REPORT_TYPE_FEATURE, 6);
.       USB_USBTask();
.       WriteNextReport(payload0xB4, 0x00, REPORT_TYPE_FEATURE, 12);
.       USB_USBTask();
.   }


This is the bare-bones firmware I needed on my AT90USB647 to get the Tracker to work reliably.  I can assure you it is far from clean.  For example, you do not need to send the 0xB3 payload every time you update data; only once after the initial device enumeration handshake completes.

The other code I had to change was in the LUFA Library 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 (this was the missing puzzle piece for weeks when the Tracker was first released!  Change one number and everything finally works; what a feeling)

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 documentation.  Like I said at the top, let me know if there’s aspects about the Tracker that the community needs documentation on, and I’ll look into what I can learn.  I hope some folks out there find this information useful :)

35 thoughts on “Vive Tracker – (Relatively) Maintained 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 ;)

  11. Thank you for your documentation, it has been very helpful.

    However, I am wondering if it is possible to poll the sensor data from the tracker via the arduino usb interface.
    The USB host library disables polling.
    Is it possible to enable it, and is there some documentation on it? Or rather has somebody already done it?

    1. Hi Chris,

      I’ve gotten this question a few times now. Polling sensor data isn’t something that I’ve seen documented yet, but I don’t think we should assume it can’t be done. Ultimately, it’s accessible on a host PC to any SteamVR Tracked object plugged in, so I would assume there is some way (albeit rather convoluted) to access that same information on a USB Host-enabled Arduino or equivalent. I unfortunately can’t offer more than speculation, however.

      If you’d be interested in doing your own research, feel free to look into extracting data on the PC via lighthouse_console.exe (found in …\Steam\steamapps\common\SteamVR\tools\lighthouse\bin\win32). Aside from typing “help” once you’ve plugged in a SteamVR device, further documentation on the use of the console can be found in the SteamVR Tracking HDK documentation. If you aren’t already, I recommend becoming a licensee by following the process outlined here (it’s free!):

      Do keep us informed with your development! I think we’d all be eager to play with the raw data coming off of these devices without being restricted to a computer :)


Leave a Reply

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