diff --git a/PS4Parser.cpp b/PS4Parser.cpp index ca6adce4..1e6bf207 100644 --- a/PS4Parser.cpp +++ b/PS4Parser.cpp @@ -17,6 +17,18 @@ #include "PS4Parser.h" +enum DPADEnum { + DPAD_UP = 0x0, + DPAD_UP_RIGHT = 0x1, + DPAD_RIGHT = 0x2, + DPAD_RIGHT_DOWN = 0x3, + DPAD_DOWN = 0x4, + DPAD_DOWN_LEFT = 0x5, + DPAD_LEFT = 0x6, + DPAD_LEFT_UP = 0x7, + DPAD_OFF = 0x8, +}; + // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller @@ -114,3 +126,28 @@ void PS4Parser::Parse(uint8_t len, uint8_t *buf) { if (ps4Output.reportChanged) sendOutputReport(&ps4Output); // Send output report } + +void PS4Parser::Reset() { + uint8_t i; + for (i = 0; i < sizeof(ps4Data.hatValue); i++) + ps4Data.hatValue[i] = 127; // Center value + ps4Data.btn.val = 0; + oldButtonState.val = 0; + for (i = 0; i < sizeof(ps4Data.trigger); i++) + ps4Data.trigger[i] = 0; + for (i = 0; i < sizeof(ps4Data.xy)/sizeof(ps4Data.xy[0]); i++) { + for (uint8_t j = 0; j < sizeof(ps4Data.xy[0].finger)/sizeof(ps4Data.xy[0].finger[0]); j++) + ps4Data.xy[i].finger[j].touching = 1; // The bit is cleared if the finger is touching the touchpad + } + + ps4Data.btn.dpad = DPAD_OFF; + oldButtonState.dpad = DPAD_OFF; + buttonClickState.dpad = 0; + oldDpad = 0; + + ps4Output.bigRumble = ps4Output.smallRumble = 0; + ps4Output.r = ps4Output.g = ps4Output.b = 0; + ps4Output.flashOn = ps4Output.flashOff = 0; + ps4Output.reportChanged = false; +}; + diff --git a/PS4Parser.h b/PS4Parser.h index 5af13e80..0ee8851e 100644 --- a/PS4Parser.h +++ b/PS4Parser.h @@ -120,18 +120,6 @@ struct PS4Output { bool reportChanged; // The data is send when data is received from the controller } __attribute__((packed)); -enum DPADEnum { - DPAD_UP = 0x0, - DPAD_UP_RIGHT = 0x1, - DPAD_RIGHT = 0x2, - DPAD_RIGHT_DOWN = 0x3, - DPAD_DOWN = 0x4, - DPAD_DOWN_LEFT = 0x5, - DPAD_LEFT = 0x6, - DPAD_LEFT_UP = 0x7, - DPAD_OFF = 0x8, -}; - /** This class parses all the data sent by the PS4 controller */ class PS4Parser { public: @@ -366,29 +354,7 @@ protected: void Parse(uint8_t len, uint8_t *buf); /** Used to reset the different buffers to their default values */ - void Reset() { - uint8_t i; - for (i = 0; i < sizeof(ps4Data.hatValue); i++) - ps4Data.hatValue[i] = 127; // Center value - ps4Data.btn.val = 0; - oldButtonState.val = 0; - for (i = 0; i < sizeof(ps4Data.trigger); i++) - ps4Data.trigger[i] = 0; - for (i = 0; i < sizeof(ps4Data.xy)/sizeof(ps4Data.xy[0]); i++) { - for (uint8_t j = 0; j < sizeof(ps4Data.xy[0].finger)/sizeof(ps4Data.xy[0].finger[0]); j++) - ps4Data.xy[i].finger[j].touching = 1; // The bit is cleared if the finger is touching the touchpad - } - - ps4Data.btn.dpad = DPAD_OFF; - oldButtonState.dpad = DPAD_OFF; - buttonClickState.dpad = 0; - oldDpad = 0; - - ps4Output.bigRumble = ps4Output.smallRumble = 0; - ps4Output.r = ps4Output.g = ps4Output.b = 0; - ps4Output.flashOn = ps4Output.flashOff = 0; - ps4Output.reportChanged = false; - }; + void Reset(); /** * Send the output to the PS4 controller. This is implemented in PS4BT.h and PS4USB.h. diff --git a/README.md b/README.md index 553238f1..48a45b76 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Help yourself by helping us support you! Many thousands of hours have been spent * [Xbox ONE Library](#xbox-one-library) * [Wii library](#wii-library) * [PS Buzz Library](#ps-buzz-library) + * [HID Libraries](#hid-libraries) * [Interface modifications](#interface-modifications) * [FAQ](#faq) @@ -307,6 +308,10 @@ More information about the controller can be found at the following sites: * http://www.developerfusion.com/article/84338/making-usb-c-friendly/ * https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c +### HID Libraries + +HID devices are also supported by the library. However these require you to write your own driver. A few example are provided in the [examples/HID](examples/HID) directory. Including an example for the [SteelSeries SRW-S1 Steering Wheel](examples/HID/SRWS1/SRWS1.ino). + # Interface modifications The shield is using SPI for communicating with the MAX3421E USB host controller. It uses the SCK, MISO and MOSI pins via the ICSP on your board. diff --git a/examples/HID/SRWS1/SRWS1.cpp b/examples/HID/SRWS1/SRWS1.cpp new file mode 100644 index 00000000..97e53a73 --- /dev/null +++ b/examples/HID/SRWS1/SRWS1.cpp @@ -0,0 +1,49 @@ +/* Copyright (C) 2016 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "SRWS1.h" + +void SRWS1::ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + if (HIDUniversal::VID != STEELSERIES_VID || HIDUniversal::PID != STEELSERIES_SRWS1_PID) // Make sure the right device is actually connected + return; +#if 0 + if (len && buf) { + Notify(PSTR("\r\n"), 0x80); + for (uint8_t i = 0; i < len; i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + } +#endif + memcpy(&srws1Data, buf, min(len, sizeof(srws1Data))); + + static SRWS1DataButtons oldButtonState; + if (srws1Data.btn.val != oldButtonState.val) { // Check if anything has changed + buttonClickState.val = srws1Data.btn.val & ~oldButtonState.val; // Update click state variable + oldButtonState.val = srws1Data.btn.val; + } +} + +// See: https://github.com/torvalds/linux/blob/master/drivers/hid/hid-steelseries.c +void SRWS1::setLeds(uint16_t leds) { + uint8_t buf[3]; + buf[0] = 0x40; // Report ID + buf[1] = leds & 0xFF; + buf[2] = (leds >> 8) & 0x7F; + pUsb->outTransfer(bAddress, epInfo[ hidInterfaces[0].epIndex[epInterruptOutIndex] ].epAddr, sizeof(buf), buf); +} + diff --git a/examples/HID/SRWS1/SRWS1.h b/examples/HID/SRWS1/SRWS1.h new file mode 100644 index 00000000..934b1aee --- /dev/null +++ b/examples/HID/SRWS1/SRWS1.h @@ -0,0 +1,95 @@ +/* Copyright (C) 2016 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef __srws1_h__ +#define __srws1_h__ + +#include + +#define STEELSERIES_VID 0x1038 +#define STEELSERIES_SRWS1_PID 0x1410 + +enum DPADEnum { + DPAD_UP = 0x0, + DPAD_UP_RIGHT = 0x1, + DPAD_RIGHT = 0x2, + DPAD_RIGHT_DOWN = 0x3, + DPAD_DOWN = 0x4, + DPAD_DOWN_LEFT = 0x5, + DPAD_LEFT = 0x6, + DPAD_LEFT_UP = 0x7, + DPAD_OFF = 0xF, +}; + +union SRWS1DataButtons { + struct { + uint8_t dpad : 4; + uint8_t dummy : 3; + uint8_t select : 1; + + uint8_t back : 1; + uint8_t lookLeft : 1; + uint8_t lights : 1; + uint8_t lookBack : 1; + uint8_t rearBrakeBalance : 1; + uint8_t frontBrakeBalance : 1; + uint8_t requestPit : 1; + uint8_t leftGear : 1; + + uint8_t camera : 1; + uint8_t lookRight : 1; + uint8_t boost : 1; + uint8_t horn : 1; + uint8_t hud : 1; + uint8_t launchControl : 1; + uint8_t speedLimiter : 1; + uint8_t rightGear : 1; + } __attribute__((packed)); + uint32_t val : 24; +} __attribute__((packed)); + +struct SRWS1Data { + int16_t tilt; // Range [-1800:1800] + uint16_t rightTrigger : 12; // Range [0:1023] i.e. only 10 bits + uint16_t leftTrigger : 12; // Range [0:1023] i.e. only 10 bits + SRWS1DataButtons btn; + uint8_t assists : 4; + uint8_t steeringSensitivity : 4; + uint8_t assistValues : 4; +} __attribute__((packed)); + +class SRWS1 : public HIDUniversal { +public: + SRWS1(USB *p) : HIDUniversal(p) {}; + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == STEELSERIES_VID && HIDUniversal::PID == STEELSERIES_SRWS1_PID; + }; + void setLeds(uint16_t leds); + SRWS1Data srws1Data; + SRWS1DataButtons buttonClickState; + +private: + void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); // Called by the HIDUniversal library + uint8_t OnInitSuccessful() { // Called by the HIDUniversal library on success + if (HIDUniversal::VID != STEELSERIES_VID || HIDUniversal::PID != STEELSERIES_SRWS1_PID) // Make sure the right device is actually connected + return 1; + setLeds(0); + return 0; + }; +}; + +#endif diff --git a/examples/HID/SRWS1/SRWS1.ino b/examples/HID/SRWS1/SRWS1.ino new file mode 100644 index 00000000..c83936fd --- /dev/null +++ b/examples/HID/SRWS1/SRWS1.ino @@ -0,0 +1,180 @@ +/* + Example sketch for the SteelSeries SRW-S1 Steering Wheel - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com +*/ + +#include +#include "SRWS1.h" + +USB Usb; +SRWS1 srw1(&Usb); + +bool printTilt; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); // Halt + } + Serial.println(F("\r\nSteelSeries SRW-S1 Steering Wheel example started")); +} + +void loop() { + Usb.Task(); + + if (srw1.connected()) { + if (printTilt) { // Show tilt angle using the LEDs + srw1.setLeds(1 << map(srw1.srws1Data.tilt, -1800, 1800, 0, 14)); // Turn on a LED according to tilt value + Serial.println(srw1.srws1Data.tilt); + } else { // Show strobe light effect + static uint32_t timer; + if (millis() - timer > 12) { + timer = millis(); // Reset timer + + static uint16_t leds = 0; + //PrintHex (leds, 0x80); Serial.println(); + srw1.setLeds(leds); // Update LEDs + + static bool dirUp = true; + if (dirUp) { + leds <<= 1; + if (leds == 0x8000) // All are actually turned off, as there is only 15 LEDs + dirUp = false; // If we have reached the end i.e. all LEDs are off, then change direction + else if (!(leds & 0x8000)) // If last bit is not set, then set the lowest bit + leds |= 1; // Set lowest bit + } else { + leds >>= 1; + if (leds == 0) // Check if all LEDs are off + dirUp = true; // If all LEDs are off, then repeat the sequence + else if (!(leds & 0x1)) // If last bit is not set, then set the top bit + leds |= 1 << 15; // Set top bit + } + } + } + + if (srw1.srws1Data.leftTrigger) { + Serial.print(F("L2: ")); + Serial.println(srw1.srws1Data.leftTrigger); + } + if (srw1.srws1Data.rightTrigger) { + Serial.print(F("R2: ")); + Serial.println(srw1.srws1Data.rightTrigger); + } + + if (srw1.buttonClickState.select) { + srw1.buttonClickState.select = 0; // Clear event + Serial.println(F("Select")); + printTilt = !printTilt; // Print tilt value & show it using the LEDs as well + } + + if (srw1.buttonClickState.back) { + srw1.buttonClickState.back = 0; // Clear event + Serial.println(F("Back")); + } + if (srw1.buttonClickState.lookLeft) { + srw1.buttonClickState.lookLeft = 0; // Clear event + Serial.println(F("Look Left")); + } + if (srw1.buttonClickState.lights) { + srw1.buttonClickState.lights = 0; // Clear event + Serial.println(F("Lights")); + } + if (srw1.buttonClickState.lookBack) { + srw1.buttonClickState.lookBack = 0; // Clear event + Serial.println(F("Look Back")); + } + if (srw1.buttonClickState.rearBrakeBalance) { + srw1.buttonClickState.rearBrakeBalance = 0; // Clear event + Serial.println(F("R. Brake Balance")); + } + if (srw1.buttonClickState.frontBrakeBalance) { + srw1.buttonClickState.frontBrakeBalance = 0; // Clear event + Serial.println(F("F. Brake Balance")); + } + if (srw1.buttonClickState.requestPit) { + srw1.buttonClickState.requestPit = 0; // Clear event + Serial.println(F("Request Pit")); + } + if (srw1.buttonClickState.leftGear) { + srw1.buttonClickState.leftGear = 0; // Clear event + Serial.println(F("Left Gear")); + } + + if (srw1.buttonClickState.camera) { + srw1.buttonClickState.camera = 0; // Clear event + Serial.println(F("Camera")); + } + if (srw1.buttonClickState.lookRight) { + srw1.buttonClickState.lookRight = 0; // Clear event + Serial.println(F("Look right")); + } + if (srw1.buttonClickState.boost) { + srw1.buttonClickState.boost = 0; // Clear event + Serial.println(F("Boost")); + } + if (srw1.buttonClickState.horn) { + srw1.buttonClickState.horn = 0; // Clear event + Serial.println(F("Horn")); + } + if (srw1.buttonClickState.hud) { + srw1.buttonClickState.hud = 0; // Clear event + Serial.println(F("HUD")); + } + if (srw1.buttonClickState.launchControl) { + srw1.buttonClickState.launchControl = 0; // Clear event + Serial.println(F("Launch Control")); + } + if (srw1.buttonClickState.speedLimiter) { + srw1.buttonClickState.speedLimiter = 0; // Clear event + Serial.println(F("Speed Limiter")); + } + if (srw1.buttonClickState.rightGear) { + srw1.buttonClickState.rightGear = 0; // Clear event + Serial.println(F("Right gear")); + } + + if (srw1.srws1Data.assists) Serial.println(srw1.srws1Data.assists); + if (srw1.srws1Data.steeringSensitivity) Serial.println(srw1.srws1Data.steeringSensitivity); + if (srw1.srws1Data.assistValues) Serial.println(srw1.srws1Data.assistValues); + + switch (srw1.srws1Data.btn.dpad) { + case DPAD_UP: + Serial.println(F("Up")); + break; + case DPAD_UP_RIGHT: + Serial.println(F("UP & right")); + break; + case DPAD_RIGHT: + Serial.println(F("Right")); + break; + case DPAD_RIGHT_DOWN: + Serial.println(F("Right & down")); + break; + case DPAD_DOWN: + Serial.println(F("Down")); + break; + case DPAD_DOWN_LEFT: + Serial.println(F("Down & left")); + break; + case DPAD_LEFT: + Serial.println(F("Left")); + break; + case DPAD_LEFT_UP: + Serial.println(F("Left & up")); + break; + case DPAD_OFF: + break; + default: + Serial.print(F("Unknown state: ")); + PrintHex (srw1.srws1Data.btn.dpad, 0x80); + Serial.println(); + break; + } + } +} +