Vive Tracker Documentation Archive
This page is outdated. I’ve kept it on the site in the event anyone needs it for archival reference, but please direct your attention to www.talariavr.com/blog/vive-tracker-initial-documentation for up-to-date information.
The following post was published on 27 March, 2017. It was briefly updated on 7 April, 2017.
* * * * * * * * * * * * * * * * * * * *
UPDATE:
REDDIT USER /U/MATZMANN666 HAS CREATED AN ARDUINO LIBRARY, SEE BOTTOM OF POST FOR MORE INFORMATION
* * * * * * * * * * * * * * * * * * * *
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:
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).
bRequest: 0x09
wValue: 0x0300
wIndex: 2
wLength: 5
Data: {0xB3, 3, 0x03, 0x00, 0x00}
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.
UPCOMING_DATA_COUNT (3)
0 – HOST_TYPE (2=PHONE, 3=ACCESSORY)
1 – RESERVED (CHARGE ENABLE, can’t be edited yet – Tracker will always leech power from host)
2 – RESERVED (OS TYPE, TBD)
{ 0xB3, 3, HOST_TYPE, 0x00, 0x00 }
UPCOMING_DATA_COUNT (10)
0 – NO VISIBLE FUNCTION (TAG INDEX)
1 – BUTTONS:
1.0 – TRIGGER (OVERRIDES ANALOGUE TRIGGER BYTE TO BE 0xFF, or at least I can confirm this occurs within Unity)
1.1 – GRIP
1.2 – APPLICATION MENU
1.3 – SYSTEM (Yes, it will open the dashboard)
1.4 – TOUCHPAD BUTTON (ONLY WORKS IF TOUCHPAD_CONTACT IS WRITTEN TO 1)
1.5 – TOUCHPAD_CONTACT (DOES NOT REQUIRE TOUCHPAD AXIS VALUES)
1.6 – RESERVED
1.7 – RESERVED
2 – TOUCHPAD_X_LSB (LSB = Least Significant Byte)
3 – TOUCHPAD_X_MSB (MSB = Most Significant Byte)
4 – TOUCHPAD_Y_LSB (LSB)
5 – TOUCHPAD_Y_MSB (MSB)
6 – NO VISIBLE FUNCTION (SHOULD BE TRIGGER_LSB)
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.)
8 – RESERVED (BATTERY_LSB?)
9 – RESERVED (BATTERY_MSB?)
{ 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:
BOARD = USER
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:
{
. 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.
{
.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:
– 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: github.com/matzman666/USBHost
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 :)