From d51d2388187c18d426895074b99b406d210d0c45 Mon Sep 17 00:00:00 2001 From: matzman666 Date: Thu, 6 Apr 2017 18:22:47 +0200 Subject: [PATCH 1/5] Made the USB HID subclass configurable. --- src/hid.h | 1 + src/hidboot.h | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/hid.h b/src/hid.h index 2191ddf..3b6ef2e 100644 --- a/src/hid.h +++ b/src/hid.h @@ -88,6 +88,7 @@ e-mail : support@circuitsathome.com #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 */ diff --git a/src/hidboot.h b/src/hidboot.h index 66f630f..98ee98e 100644 --- a/src/hidboot.h +++ b/src/hidboot.h @@ -173,7 +173,7 @@ class KeyboardReportParser : public HIDReportParser /** * \class HIDBoot definition. */ -template +template class HIDBoot : public HID { EpInfo epInfo[totalEndpoints]; @@ -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 -HIDBoot::HIDBoot(USBHost *p) : +template +HIDBoot::HIDBoot(USBHost *p) : HID(p), pRptParser(NULL), qNextPollTime(0), @@ -225,8 +226,8 @@ HIDBoot::HIDBoot(USBHost *p) : /** * \brief Initialize HIDBoot class. */ -template -void HIDBoot::Initialize() +template +void HIDBoot::Initialize() { for (uint32_t i = 0; i < totalEndpoints; ++i) { @@ -251,8 +252,8 @@ void HIDBoot::Initialize() * * \return 0 on success, error code otherwise. */ -template -uint32_t HIDBoot::Init(uint32_t parent, uint32_t port, uint32_t lowspeed) +template +uint32_t HIDBoot::Init(uint32_t parent, uint32_t port, uint32_t lowspeed) { const uint32_t constBufSize = sizeof(USB_DEVICE_DESCRIPTOR); @@ -360,7 +361,7 @@ uint32_t HIDBoot::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); @@ -443,8 +444,8 @@ uint32_t HIDBoot::Init(uint32_t parent, uint32_t port, uint32_t l * \param proto Protocol version used. * \param pep Pointer to endpoint descriptor. */ -template -void HIDBoot::EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) +template +void HIDBoot::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) @@ -493,8 +494,8 @@ void HIDBoot::EndpointXtract(uint32_t conf, uint32_t iface, uint3 * * \return Always 0. */ -template -uint32_t HIDBoot::Release() +template +uint32_t HIDBoot::Release() { // Free allocated host pipes UHD_Pipe_Free(epInfo[epInterruptInIndex].hostPipeNum); @@ -519,8 +520,8 @@ uint32_t HIDBoot::Release() * * \return 0 on success, error code otherwise. */ -template -uint32_t HIDBoot::Poll() +template +uint32_t HIDBoot::Poll() { uint32_t rcode = 0; From cd46a4aad3ceb81b9bde4af90b275febd82b2bf1 Mon Sep 17 00:00:00 2001 From: matzman666 Date: Thu, 6 Apr 2017 18:24:03 +0200 Subject: [PATCH 2/5] Added support for the HTC Vive tracker. --- src/ViveTrackerController.h | 153 ++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/ViveTrackerController.h diff --git a/src/ViveTrackerController.h b/src/ViveTrackerController.h new file mode 100644 index 0000000..4fd779f --- /dev/null +++ b/src/ViveTrackerController.h @@ -0,0 +1,153 @@ +/* + * ViveTrackerController.h + * + * Created on: 05.04.2017 + * Author: matzman + */ + +#ifndef VIVETRACKERCONTROLLER_H_ +#define VIVETRACKERCONTROLLER_H_ + +#include + + +/* 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) + + +/** + * \struct Vive Tracker Feature Report 0xB3 + */ +struct __attribute__((__packed__)) ViveTrackerFeatureReportB3 { + uint8_t address = 0xB3; + uint8_t payloadSize = 3; + uint8_t hostType; + uint8_t chargeEnable; + uint8_t osType; + ViveTrackerFeatureReportB3(uint8_t hostType = 3, uint8_t chargeEnable = 0, uint8_t osType = 0) + : hostType(hostType), chargeEnable(chargeEnable), osType(osType) {} +}; + +/** + * \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 { +public: + using HIDBoot::HIDBoot; + + virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed) override { + auto retval = HIDBoot::Init(parent, port, lowspeed); + if (!retval) { + _isConnected = true; + } + return retval; + } + + virtual uint32_t Release() override { + auto retval = HIDBoot::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) : usb(usb), hostTracker(&usb) {} + + /** + * 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; + } + + /** + * 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; + + uint32_t _initConnection(uint8_t hostType = 3, uint8_t chargeEnable = 0, uint8_t osType = 0) { + ViveTrackerFeatureReportB3 report(hostType, chargeEnable, osType); + return hostTracker.SetReport(0, 2, 3, 0, sizeof(ViveTrackerFeatureReportB3), (uint8_t*)&report); + } +}; + + +#endif /* VIVETRACKERCONTROLLER_H_ */ From 3a658c0d67d2289069ceb5892f2a329d8fe6f2be Mon Sep 17 00:00:00 2001 From: matzman666 Date: Thu, 6 Apr 2017 18:25:55 +0200 Subject: [PATCH 3/5] Added example application for the Vive tracker and updated readme. --- README.adoc | 2 +- examples/ViveTracker/ViveTracker.ino | 36 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 examples/ViveTracker/ViveTracker.ino diff --git a/README.adoc b/README.adoc index dc0a9ac..de7e68e 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,6 @@ = 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. For more information about this library please visit us at http://www.arduino.cc/en/Reference/USBHost diff --git a/examples/ViveTracker/ViveTracker.ino b/examples/ViveTracker/ViveTracker.ino new file mode 100644 index 0000000..606558a --- /dev/null +++ b/examples/ViveTracker/ViveTracker.ino @@ -0,0 +1,36 @@ +/* + * Vive Tracker Example + * + * Shows how to send button + */ + +// Require vive tracker controller library +#include + +// 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); +} From 8fc7d4b688269d51b786ead9cc07604a5e95c2a2 Mon Sep 17 00:00:00 2001 From: matzman666 Date: Sat, 8 Apr 2017 19:56:31 +0200 Subject: [PATCH 4/5] Gave credits to Peter S. Hollander for finding out the correct format of the USB feature reports. Improved the example. --- examples/ViveTracker/ViveTracker.ino | 2 +- src/ViveTrackerController.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/ViveTracker/ViveTracker.ino b/examples/ViveTracker/ViveTracker.ino index 606558a..b3d0ae7 100644 --- a/examples/ViveTracker/ViveTracker.ino +++ b/examples/ViveTracker/ViveTracker.ino @@ -32,5 +32,5 @@ void loop() { uint16_t batteryLevel = 0; tracker.setTrackerStatus(buttons, padX, padY, trigger, batteryLevel); } - delay(300); + delay(10); // According to HTC's documentation the interval to send data should be at least 10 ms. } diff --git a/src/ViveTrackerController.h b/src/ViveTrackerController.h index 4fd779f..fa7bb66 100644 --- a/src/ViveTrackerController.h +++ b/src/ViveTrackerController.h @@ -20,6 +20,13 @@ #define VIVETRACKER_BUTTON_PADTOUCHED (1 << 5) +/* + * 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 */ From 0686cef6750f4bde90bc0bf77adc02aa47130301 Mon Sep 17 00:00:00 2001 From: matzman666 Date: Sun, 26 Nov 2017 02:42:06 +0100 Subject: [PATCH 5/5] Refractored how usb message fields are handled. Added support for the low-pass filter added in tracker firmware v.20. --- README.adoc | 4 ++- examples/ViveTracker/ViveTracker.ino | 2 +- library.properties | 2 +- src/Usb.cpp | 5 ++++ src/ViveTrackerController.h | 41 ++++++++++++++++++++++++---- 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/README.adoc b/README.adoc index de7e68e..4e27fac 100644 --- a/README.adoc +++ b/README.adoc @@ -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, keyboards and HTC Vive trackers. +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 diff --git a/examples/ViveTracker/ViveTracker.ino b/examples/ViveTracker/ViveTracker.ino index b3d0ae7..606558a 100644 --- a/examples/ViveTracker/ViveTracker.ino +++ b/examples/ViveTracker/ViveTracker.ino @@ -32,5 +32,5 @@ void loop() { uint16_t batteryLevel = 0; tracker.setTrackerStatus(buttons, padX, padY, trigger, batteryLevel); } - delay(10); // According to HTC's documentation the interval to send data should be at least 10 ms. + delay(300); } diff --git a/library.properties b/library.properties index 755bffd..0d21c90 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=USBHost -version=1.0.5 +version=1.0.6 author=Arduino maintainer=Arduino sentence=Allows the communication with USB peripherals like mice, keyboards, and thumbdrives. diff --git a/src/Usb.cpp b/src/Usb.cpp index ce74b48..7b35f11 100644 --- a/src/Usb.cpp +++ b/src/Usb.cpp @@ -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? diff --git a/src/ViveTrackerController.h b/src/ViveTrackerController.h index fa7bb66..cdc05b3 100644 --- a/src/ViveTrackerController.h +++ b/src/ViveTrackerController.h @@ -19,6 +19,12 @@ #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. @@ -32,12 +38,13 @@ */ struct __attribute__((__packed__)) ViveTrackerFeatureReportB3 { uint8_t address = 0xB3; - uint8_t payloadSize = 3; + uint8_t payloadSize = 4; uint8_t hostType; uint8_t chargeEnable; uint8_t osType; - ViveTrackerFeatureReportB3(uint8_t hostType = 3, uint8_t chargeEnable = 0, uint8_t osType = 0) - : hostType(hostType), chargeEnable(chargeEnable), osType(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) {} }; /** @@ -99,7 +106,8 @@ class ViveTrackerBoot : public HIDBoot { */ class ViveTrackerController { public: - ViveTrackerController(USBHost &usb) : usb(usb), hostTracker(&usb) {} + 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. @@ -131,6 +139,19 @@ class ViveTrackerController { 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. * @@ -149,10 +170,18 @@ class ViveTrackerController { 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 _initConnection(uint8_t hostType = 3, uint8_t chargeEnable = 0, uint8_t osType = 0) { - ViveTrackerFeatureReportB3 report(hostType, chargeEnable, osType); + 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); + } };