Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for HTC Vive trackers. #16

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
= USB Host Library for Arduino =

The USBHost library allows an Arduino Due board to appear as a USB host, enabling it to communicate with peripherals like USB mice and keyboards.
The USBHost library allows an Arduino Due board to appear as a USB host, enabling it to communicate with peripherals like USB mice, keyboards and HTC Vive trackers (requires at least tracker firmware v.20).

For an USBHost library supporting Vive tracker for Arduino SAMD boards see [here](https://github.com/matzman666/USBHost-samd).

For more information about this library please visit us at
http://www.arduino.cc/en/Reference/USBHost
Expand Down
36 changes: 36 additions & 0 deletions examples/ViveTracker/ViveTracker.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Vive Tracker Example
*
* Shows how to send button
*/

// Require vive tracker controller library
#include <ViveTrackerController.h>

// Initialize USB Host Controller
USBHost usb;

// Attach vive tracker controller to USB
ViveTrackerController tracker(usb);


void setup() {
Serial.begin(115200);
Serial.println("Program started");
delay(200);
}


void loop() {
tracker.Task(); // Process usb and vive tracker controller tasks
if (tracker.isConnected()) {
// Send accessory state to vive tracker.
uint8_t buttons = VIVETRACKER_BUTTON_GRIP | VIVETRACKER_BUTTON_MENU;
int16_t padX = 1234;
int16_t padY = -333;
uint8_t trigger = 200;
uint16_t batteryLevel = 0;
tracker.setTrackerStatus(buttons, padX, padY, trigger, batteryLevel);
}
delay(300);
}
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=USBHost
version=1.0.5
version=1.0.6
author=Arduino
maintainer=Arduino <[email protected]>
sentence=Allows the communication with USB peripherals like mice, keyboards, and thumbdrives.
Expand Down
5 changes: 5 additions & 0 deletions src/Usb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -723,23 +723,28 @@ void USBHost::Task(void)
{
case UHD_STATE_ERROR:
// Illegal state
//Serial.write("UHD_STATE_ERROR\n");
usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL;
lowspeed = 0;
break;

case UHD_STATE_DISCONNECTED:
//Serial.write("UHD_STATE_DISCONNECTED\n");
// Disconnected state
if ((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED)
{
//Serial.write("UHD_STATE_DISCONNECTED2\n");
usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;
lowspeed = 0;
}
break;

case UHD_STATE_CONNECTED:
//Serial.write("UHD_STATE_CONNECTED\n");
// Attached state
if ((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED)
{
//Serial.write("UHD_STATE_CONNECTED2\n");
delay = millis() + USB_SETTLE_DELAY;
usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE;
//FIXME TODO: lowspeed = 0 ou 1; already done by hardware?
Expand Down
189 changes: 189 additions & 0 deletions src/ViveTrackerController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* ViveTrackerController.h
*
* Created on: 05.04.2017
* Author: matzman
*/

#ifndef VIVETRACKERCONTROLLER_H_
#define VIVETRACKERCONTROLLER_H_

#include <hidboot.h>


/* Vive Tracker Button Bitmasks */
#define VIVETRACKER_BUTTON_TRIGGER (1 << 0)
#define VIVETRACKER_BUTTON_GRIP (1 << 1)
#define VIVETRACKER_BUTTON_MENU (1 << 2)
#define VIVETRACKER_BUTTON_SYSTEM (1 << 3)
#define VIVETRACKER_BUTTON_PADTRIGGERED (1 << 4)
#define VIVETRACKER_BUTTON_PADTOUCHED (1 << 5)

#define VIVETRACKER_LPF_DEFAULT 0
#define VIVETRACKER_LPF_184HZ 0
#define VIVETRACKER_LPF_5HZ 1
#define VIVETRACKER_LPF_10HZ 2
#define VIVETRACKER_LPF_20HZ 3


/*
* The format of the USB HID feature reports is unfortunately not correct in HTC's documentation.
* Fortunately Peter S. Hollander was able to find out the correct format of the feature reports,
* and he was kind enough to share the details on his blog:
* http://www.talariavr.com/blog/vive-tracker-initial-documentation/
*/

/**
* \struct Vive Tracker Feature Report 0xB3
*/
struct __attribute__((__packed__)) ViveTrackerFeatureReportB3 {
uint8_t address = 0xB3;
uint8_t payloadSize = 4;
uint8_t hostType;
uint8_t chargeEnable;
uint8_t osType;
uint8_t lpfConfig;
ViveTrackerFeatureReportB3(uint8_t hostType = 3, uint8_t chargeEnable = 0, uint8_t osType = 0, uint8_t lpfConfig = VIVETRACKER_LPF_DEFAULT)
: hostType(hostType), chargeEnable(chargeEnable), osType(osType), lpfConfig(lpfConfig) {}
};

/**
* \struct Vive Tracker Feature Report 0xB4
*/
struct __attribute__((__packed__)) ViveTrackerFeatureReportB4 {
uint8_t address = 0xB4;
uint8_t payloadSize = 10;
uint8_t tagIndex = 0;
uint8_t buttons;
int16_t padX;
int16_t padY;
uint16_t trigger;
uint16_t batteryLevel;
ViveTrackerFeatureReportB4(uint8_t buttons = 0, int16_t padX = 0, int16_t padY = 0, uint16_t trigger = 0, uint16_t batteryLevel = 0)
: buttons(buttons), padX(padX), padY(padY), trigger(trigger), batteryLevel(batteryLevel) {}
} ;

/**
* \class Customized HID Boot definition for Vive Tracker
*/
class ViveTrackerBoot : public HIDBoot<HID_PROTOCOL_NONE, HID_NONE_SUBCLASS> {
public:
using HIDBoot<HID_PROTOCOL_NONE, HID_NONE_SUBCLASS>::HIDBoot;

virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed) override {
auto retval = HIDBoot<HID_PROTOCOL_NONE, HID_NONE_SUBCLASS>::Init(parent, port, lowspeed);
if (!retval) {
_isConnected = true;
}
return retval;
}

virtual uint32_t Release() override {
auto retval = HIDBoot<HID_PROTOCOL_NONE, HID_NONE_SUBCLASS>::Release();
if (!retval) {
_isConnected = false;
}
return retval;
}

virtual uint32_t Poll() override {
return 0; // The Vive Controller does not send any data
}

bool isConnected() {
return _isConnected;
}

private:
bool _isConnected = false;
};


/**
* \class Vive Tracker Controller
*
* Represents the public API for the Vive Tracker
*/
class ViveTrackerController {
public:
ViveTrackerController(USBHost &usb, uint8_t hostType = 3, uint8_t chargeEnabled = 0, uint8_t osType = 0)
: usb(usb), hostTracker(&usb), _hostType(hostType), _chargeEnabled(chargeEnabled), _osType(osType) {}

/**
* Returns whether a vive tracker is connected and initialized.
*/
bool isConnected() {
return hostTracker.isConnected() && _isInitialized;
}

/**
* Sends the accessory state to the vive tracker.
*
* Regarding the trigger parameter: HTC's documentations states that a 16-bit value is used to represent the trigger.
* But the current firmware only uses an 8-bit value.
*
* \param buttons Bitmask representing the pressed buttons.
* \param padX X-axis of the trackpad (from -32768 to 32767)
* \param padY Y-axis of the trackpad (from -32768 to 32767)
* \param trigger Trigger state (from 0 to 255)
* \param batteryLevel Currently not used, should be set to 0.
* \param lowspeed USB device speed.
*
* \return 0 on success, error code otherwise.
*/
uint32_t setTrackerStatus(uint8_t buttons = 0, int16_t padX = 0, int16_t padY = 0, uint8_t trigger = 0, uint16_t batteryLevel = 0) {
if (isConnected()) {
ViveTrackerFeatureReportB4 report(buttons, padX, padY, trigger << 8, batteryLevel);
return hostTracker.SetReport(0, 2, 3, 0, sizeof(ViveTrackerFeatureReportB4), (uint8_t*)&report);
}
return -1;
}

uint32_t setChargeEnabled(uint8_t enabled, bool sendUsbReport = true) {
_chargeEnabled = enabled;
if (sendUsbReport) {
return sendUsbReportB3(_hostType, _chargeEnabled, _osType, VIVETRACKER_LPF_DEFAULT);
} else {
return 0;
}
}

uint32_t setLpfConfig(uint8_t lpfConfig) {
return sendUsbReportB3(_hostType, _chargeEnabled, _osType, lpfConfig);
}

/**
* Process usb and vive tracker controller tasks.
*
* Should be repeatedly called in the main-loop.
*/
void Task() {
usb.Task();
if (hostTracker.isConnected() && !_isInitialized && _initConnection() == 0) {
_isInitialized = true;
} else if (!hostTracker.isConnected()) {
_isInitialized = false;
}
}

private:
USBHost &usb;
ViveTrackerBoot hostTracker;
bool _isInitialized = false;
uint8_t _hostType;
uint8_t _chargeEnabled;
uint8_t _osType;

uint32_t _initConnection() {
return sendUsbReportB3(_hostType, _chargeEnabled, _osType, VIVETRACKER_LPF_DEFAULT);
}

uint32_t sendUsbReportB3(uint8_t hostType, uint8_t chargeEnabled, uint8_t osType, uint8_t lpfConfig) {
ViveTrackerFeatureReportB3 report(hostType, chargeEnabled, osType, lpfConfig);
return hostTracker.SetReport(0, 2, 3, 0, sizeof(ViveTrackerFeatureReportB3), (uint8_t*)&report);

}
};


#endif /* VIVETRACKERCONTROLLER_H_ */
1 change: 1 addition & 0 deletions src/hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ e-mail : [email protected]
#define HID_INTF 0x03

/* HID Interface Class SubClass Codes */
#define HID_NONE_SUBCLASS 0x00
#define HID_BOOT_INTF_SUBCLASS 0x01

/* HID Interface Class Protocol Codes */
Expand Down
29 changes: 15 additions & 14 deletions src/hidboot.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class KeyboardReportParser : public HIDReportParser
/**
* \class HIDBoot definition.
*/
template <const uint8_t BOOT_PROTOCOL>
template <const uint8_t BOOT_PROTOCOL, const uint8_t HID_SUBCLASS = HID_BOOT_INTF_SUBCLASS>
class HIDBoot : public HID
{
EpInfo epInfo[totalEndpoints];
Expand Down Expand Up @@ -206,11 +206,12 @@ class HIDBoot : public HID
virtual void EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
};


/**
* \brief HIDBoot class constructor.
*/
template <const uint8_t BOOT_PROTOCOL>
HIDBoot<BOOT_PROTOCOL>::HIDBoot(USBHost *p) :
template <const uint8_t BOOT_PROTOCOL, const uint8_t HID_SUBCLASS>
HIDBoot<BOOT_PROTOCOL, HID_SUBCLASS>::HIDBoot(USBHost *p) :
HID(p),
pRptParser(NULL),
qNextPollTime(0),
Expand All @@ -225,8 +226,8 @@ HIDBoot<BOOT_PROTOCOL>::HIDBoot(USBHost *p) :
/**
* \brief Initialize HIDBoot class.
*/
template <const uint8_t BOOT_PROTOCOL>
void HIDBoot<BOOT_PROTOCOL>::Initialize()
template <const uint8_t BOOT_PROTOCOL, const uint8_t HID_SUBCLASS>
void HIDBoot<BOOT_PROTOCOL, HID_SUBCLASS>::Initialize()
{
for (uint32_t i = 0; i < totalEndpoints; ++i)
{
Expand All @@ -251,8 +252,8 @@ void HIDBoot<BOOT_PROTOCOL>::Initialize()
*
* \return 0 on success, error code otherwise.
*/
template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t lowspeed)
template <const uint8_t BOOT_PROTOCOL, const uint8_t HID_SUBCLASS>
uint32_t HIDBoot<BOOT_PROTOCOL, HID_SUBCLASS>::Init(uint32_t parent, uint32_t port, uint32_t lowspeed)
{
const uint32_t constBufSize = sizeof(USB_DEVICE_DESCRIPTOR);

Expand Down Expand Up @@ -360,7 +361,7 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t l
{
ConfigDescParser<
USB_CLASS_HID,
HID_BOOT_INTF_SUBCLASS,
HID_SUBCLASS,
BOOT_PROTOCOL,
CP_MASK_COMPARE_ALL> confDescrParser(this);

Expand Down Expand Up @@ -443,8 +444,8 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Init(uint32_t parent, uint32_t port, uint32_t l
* \param proto Protocol version used.
* \param pep Pointer to endpoint descriptor.
*/
template <const uint8_t BOOT_PROTOCOL>
void HIDBoot<BOOT_PROTOCOL>::EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *pep)
template <const uint8_t BOOT_PROTOCOL, const uint8_t HID_SUBCLASS>
void HIDBoot<BOOT_PROTOCOL, HID_SUBCLASS>::EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *pep)
{
// If the first configuration satisfies, the others are not considered.
if (bNumEP > 1 && conf != bConfNum)
Expand Down Expand Up @@ -493,8 +494,8 @@ void HIDBoot<BOOT_PROTOCOL>::EndpointXtract(uint32_t conf, uint32_t iface, uint3
*
* \return Always 0.
*/
template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Release()
template <const uint8_t BOOT_PROTOCOL, const uint8_t HID_SUBCLASS>
uint32_t HIDBoot<BOOT_PROTOCOL, HID_SUBCLASS>::Release()
{
// Free allocated host pipes
UHD_Pipe_Free(epInfo[epInterruptInIndex].hostPipeNum);
Expand All @@ -519,8 +520,8 @@ uint32_t HIDBoot<BOOT_PROTOCOL>::Release()
*
* \return 0 on success, error code otherwise.
*/
template <const uint8_t BOOT_PROTOCOL>
uint32_t HIDBoot<BOOT_PROTOCOL>::Poll()
template <const uint8_t BOOT_PROTOCOL, const uint8_t HID_SUBCLASS>
uint32_t HIDBoot<BOOT_PROTOCOL, HID_SUBCLASS>::Poll()
{
uint32_t rcode = 0;

Expand Down