Communication Between HTC Vive Tracker and Android 2


The Vive tracker has been available to developers for a few months now, but there has not been much development with them. The Vive tracker targets accessories makers more than game developers. However, for there to be tracker support developers must integrate it into their VR experiences. The problem is that the Vive tracker only has positional tracking functionality without any custom hardware to communicate with the tracker via pogo pins or USB protocols.

I have the opportunity to develop with the Vive tracker and found myself stuck in a similar situation. So instead of trying to program a microcontroller to use the USB stack to communicate with the Vive tracker, I wrote an Android app to do the same thing. With the app, I was able to talk directly with the Vive tracker and send all the button presses that are available to a Vive wand controller.

 

My goal in this post is to go over how you can use Android to communicate with the Vive tracker, which eliminates the need for custom hardware to start development.

Recommended: It would be helpful if you familiarize yourself with the Android USB API before continuing.


Documentation

Much of what I will be going over is what is available in the HTC’s documentation for the Vive tracker. However, I will add code to show how things work. In addition, I made some discoveries along the way that I feel is important to mention, which is not in the documentation.

 

Vive Tracker USB Data Packet

The Vive tracker accepts USB data packet in the B3 and B4 format.

B3 Packet

B3 USB data structure

The B3 data packet indicates the host type (what the Vive tracker is representing) and whether the tracker can be charging while in use.

            byte[] B3_data = {
                    (byte) 179,     // 0xB3
                    3,              // # of incoming bytes
                    3,              // Host type: Accessory
                    0,              // charge enabled; RESERVED
                    0,              // OS type; RESERVED
            };

 

B4 Packet

B4 USB data packet structure

The B4 data packet indicates the button input(s). You can even specify the analog values for the trigger and touchpad.

            byte[] B4_data = {
                    (byte) 180,         // 0xB4; type: button inputs
                    10,                 // # of incoming bytes
                    0x00,               // tag index
                    TRACKER_TRIGGER,    // trigger press
                    0x00,                    // Touch pad X value LSB; sums up to [-32768, 32768]
                    0x00,                    // Touch pad X value MSB;
                    0x00,                    // touch pad y value LSB; sums up to [-32768, 32768]
                    0x00,                    // touch pad y value MSB;
                    0x00,                    // analog trigger LSB; sums up to [0, 65535]
                    0x00,                    // Analog trigger MSB; sums up to [0, 65535]
                    0x00,                    // reserved for battery
                    0x00,                    // reserved for battery
            };
    // Vive Tracker Data type
    static final int TRACKER_TRIGGER 		= 1 << 0;
    static final int TRACKER_BUMPER 		= 1 << 1;
    static final int TRACKER_MENU 		    = 1 << 2;
    static final int TRACKER_STEAM 		    = 1 << 3;
    static final int TRACKER_PAD_PRESS 	    = 1 << 4;
    static final int TRACKER_PAD_TOUCH 	    = 1 << 5;
    static final int TRACKER_RESERVED_1 	= 1 << 6;
    static final int TRACKER_RESERVED_2 	= 1 << 7;

 

Vive Tracker USB Information

Through the Android USB API, I was able to obtain a lot of information about the Vive tracker. For example, I was able to find out the product ID, device ID, vendor, ID, and the number of interfaces. The Vive tracker has three interfaces and you would be communicating with the third interface with all your B3 and B4 data packets.

Here is some information about the Vive tracker:

 

Finding the Vive Tracker

In order to find the Vive tracker, you will need to use the USB manager part of the Android API. From the information about the Vive tracker, you can do an explicit filter for the Vive tracker.

    private void checkForViveTracker() {
        UsbManager usb_manager = (UsbManager) getSystemService(Context.USB_SERVICE);
        HashMap<String, UsbDevice> device_list = usb_manager.getDeviceList();
        Iterator<UsbDevice> device_list_ptr = device_list.values().iterator();
        
        while (device_list_ptr.hasNext()) {
            UsbDevice device = device_list_ptr.next();

            String device_info = deviceInfo(device);

            if (device.getProductName().toLowerCase().equals("vrc")) {
                debugLog("Found vive tracker");
                htc_vive_tracker = device;
            }
        }
    }

 

Connecting to the Vive Tracker

When you do find the Vive tracker, you can create a connection between the tracker and your Android device. One important thing to note is that immediately after the connection is established, the Vive tracker must receive a B3 packet. If that does not happen, the tracker stops reporting its position.

    private boolean connectToViveTracker() {
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        usbManager.requestPermission(htc_vive_tracker, pendingIntent);

        if (vive_tracker_connection == null) {
            vive_tracker_connection = usbManager.openDevice(htc_vive_tracker);

            boolean connection_status = (vive_tracker_connection == null) ? false : true;

            // immediately after connection, send a report packet to maintain tracking
            boolean report_feature_status = sendB3Packet();
            if (connection_status && report_feature_status) {
                debugLog("Successfully connected to vive tracker");
                return true;
            } else {
                debugLog("Failed to connect to vive tracker");
                if (!report_feature_status) {
                    debugLog("Failed to send init B3 Report Feature\n");
                }
                return false;
            }
        } else {
            debugLog("Vive tracker is already connected");
            return true;
        }
    }

 

Sending Data Packet

Remember that the Vive tracker has three interfaces and that the third interface is what you want to be communicating with. Since the Android API is using 0 index, the third interface is number 2.

In order to write data packet to the interface, you must first claim the interface. Once you are done writing, release the interface.

    private boolean sendB3Packet() {
        if (htc_vive_tracker != null &&
                vive_tracker_connection != null) {
            byte[] B3_data = {
                    (byte) 179,     // 0xB3
                    3,              // # of incoming bytes
                    3,              // Host type: Accessory
                    0,              // charge enabled; RESERVED
                    0,              // OS type; RESERVED
            };

            UsbInterface write_interface = htc_vive_tracker.getInterface(2);
            vive_tracker_connection.claimInterface(write_interface, true);

            int result = vive_tracker_connection.controlTransfer(0x21, 0x09, 0x0300, 2, B3_data, B3_data.length, 0);
            vive_tracker_connection.releaseInterface(write_interface);

            if (result >= 0) {
                debugLog("Sent " + result + " bytes to vive tracker.\n");
                return true;
            } else {
                debugLog("Sent " + result + " bytes to vive tracker.\n");
                return false;
            }
        } else {
            return false;
        }
    }
    public boolean sendTriggerPressPkt() {
        if (htc_vive_tracker != null &&
                vive_tracker_connection != null) {
            byte[] B4_data = {
                    (byte) 180,         // 0xB4; type: button inputs
                    10,                 // # of incoming bytes
                    0x00,               // tag index
                    TRACKER_TRIGGER,    // trigger press
                    0x00,                    // Touch pad X value LSB; sums up to [-32768, 32768]
                    0x00,                    // Touch pad X value MSB;
                    0x00,                    // touch pad y value LSB; sums up to [-32768, 32768]
                    0x00,                    // touch pad y value MSB;
                    0x00,                    // analog trigger LSB; sums up to [0, 65535]
                    0x00,                    // Analog trigger MSB; sums up to [0, 65535]
                    0x00,                    // reserved for battery
                    0x00,                    // reserved for battery
            };

            UsbInterface write_interface = htc_vive_tracker.getInterface(2);
            vive_tracker_connection.claimInterface(write_interface, true);

            int result = vive_tracker_connection.controlTransfer(0x21, 0x09, 0x0300, 2, B4_data, B4_data.length, 0);
            vive_tracker_connection.releaseInterface(write_interface);

            vive_tracker_reset_handler.postDelayed(send_reset_packet_runnable, 2000);   // after 2 seconds stop reporting

            if (result >= 0) {
                debugLog("Sent " + result + " bytes to vive tracker.\n");
                return true;
            } else {
                debugLog("Sent " + result + " bytes to vive tracker.\n");
                return false;
            }
        } else {
            return false;
        }
    }

 

Interesting Discoveries

  • I found that to have the Vive tracker working reliably, I needed to send a B3 packet along with every B4 packet. There must be at least a 10 ms delay between the B3 and B4 packet as well.
  • The tracker keeps sending the last data packet repeatedly, so you must send a “reset” packet to the tracker to indicate no input(s). A reset packet is a B4 packet with all inputs zeroed out.
  • Touchpad press does not work unless Touchpad Contact is also true. Touchpad Contact by itself does work. With newer firmware available via SteamVR for the Vive tracker, touchpad press works by itself through the pogo pins. The received input is considered touchpad press and touchpad contact together.
  • When the connection to the tracker is first established, the tracker must receive a B3 data packet to maintain positional tracking.

 

Source Code For Sample Project

 

I have a sample Android project that you can download and compile with Android studio to help you get started. The app supports connecting to the Vive tracker in addition to sending a touchpad press or trigger press.

 


I hope you found this post helpful. If you found this post helpful, share it with others so they can benefit too.

 

Are you a game or VR developer that was thinking about adding support for the Vive tracker, but find it difficult to because of hardware? If so, did my Android solution help or change your mind with supporting the Vive tracker? Have you worked with the Vive tracker and got it to work with custom hardware? Did this post, help you understand the steps necessary to communicate with the Vive tracker?

 

Feel free to leave a comment, send me a tweet, or send me an email at steven@brightdevelopers.com. To stay in touch, you can follow me on Twitter.


Resources

Peter Hollander’s documentation about the Vive tracker is what inspired me to write an Android app to work with the tracker. The documentation includes information that HTC missed in their Vive Tracker Developer Documentation. If you want to create custom hardware or go deeper in Vive tracker development, I recommend you check out Peter’s documentation.


About Steven To

Steven To is a software developer that specializes in mobile development with a background in computer engineering. Beyond his passion for software development, he also has an interest in Virtual Reality, Augmented Reality, Artificial Intelligence, Personal Development, and Personal Finance. If he is not writing software, then he is out learning something new.

2 thoughts on “Communication Between HTC Vive Tracker and Android

  • Peter S. Hollander

    Good write up! I haven’t found that you need to send A B3 packet with every B4, but I also haven’t been using Android for my USB control. Also, check out the touchpad issues with the SteamVR beta installed – An update has rolled out to remove filtering on the touchpad analogue and digital bits! (However a recent firmware update to the Tracker may negate / flip some of that).

    Keep up the documentation!

Comments are closed.