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

REDDIT USER /U/MATZMANN666 HAS CREATED AN ARDUINO LIBRARY, SEE BOTTOM OF POST FOR MORE INFORMATION

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





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).

ADDRESS: 0xB3

UPCOMING_DATA_COUNT (4)

0 – HOST_TYPE (1=PC, 2=PHONE, 3=ACCESSORY)

1 – RESERVED (CHARGE ENABLE, not sure if interfacing with this byte has any visible function)

2 – RESERVED (OS TYPE, TBD)

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)

{ 0xB3, 4, HOST_TYPE, RESERVED, RESERVED, LPF }

ADDRESS: 0xB4

UPCOMING_DATA_COUNT (10)

0 – NO VISIBLE FUNCTION (TAG INDEX)

1 – BUTTONS:

1.0 – TRIGGER (Following a SteamVR driver update, this value is independent of the analogue trigger byte)

1.1 – GRIP

1.2 – APPLICATION MENU

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?)

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 (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)

{ 0xB4, 10, 0x00, BUTTONS, PAD_X_LSB, PAD_X_MSB, PAD_Y_LSB, PAD_Y_MSB, 0x00, TRIGGER, RESERVED, RESERVED }



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

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:

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: 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 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 :)