Merge pull request #213 from felis/SRWS1

Added support for the SteelSeries SRW-S1 Steering Wheel
This commit is contained in:
Kristian Sloth Lauszus 2016-03-22 00:15:15 +01:00
commit e61191f83f
6 changed files with 367 additions and 35 deletions

View file

@ -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;
};

View file

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

View file

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

View file

@ -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<uint8_t > (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);
}

View file

@ -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 <hiduniversal.h>
#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

View file

@ -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 <SPI.h>
#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<uint16_t > (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<uint8_t > (srw1.srws1Data.btn.dpad, 0x80);
Serial.println();
break;
}
}
}