From b0b255cd954533761d050e8de4b0b4e8a4adcf66 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Thu, 15 Mar 2018 09:52:01 +0100 Subject: [PATCH 1/3] Added support for rumble effect on Xbox One controllers Fixes #363 --- XBOXONE.cpp | 67 ++++++++++++++++++++++++++----- XBOXONE.h | 12 ++++++ examples/Xbox/XBOXONE/XBOXONE.ino | 13 ++++++ 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/XBOXONE.cpp b/XBOXONE.cpp index 2c9cb392..4e5b3d84 100644 --- a/XBOXONE.cpp +++ b/XBOXONE.cpp @@ -410,22 +410,69 @@ uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) { return rcode; } +// The Xbox One packets are described at: https://github.com/quantus/xbox-one-controller-protocol void XBOXONE::onInit() { // A short buzz to show the controller is active uint8_t writeBuf[11]; + + // Activate rumble writeBuf[0] = 0x09; - writeBuf[1] = 0x08; - writeBuf[2] = 0x00; - writeBuf[3] = 0x09; - writeBuf[4] = 0x00; - writeBuf[5] = 0x0f; - writeBuf[6] = 0x04; - writeBuf[7] = 0x04; - writeBuf[8] = 0x20; - writeBuf[9] = 0x20; - writeBuf[10] = 0x80; + writeBuf[1] = 0x08; // 0x20 bit and all bits of 0x07 prevents rumble effect + writeBuf[2] = 0x00; // This may have something to do with how many times effect is played + + // Single rumble effect + writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has) + writeBuf[4] = 0x00; // Mode + writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R) + writeBuf[6] = 0x04; // lT force + writeBuf[7] = 0x04; // rT force + writeBuf[8] = 0x20; // L force + writeBuf[9] = 0x20; // R force + writeBuf[10] = 0x80; // Length of pulse XboxCommand(writeBuf, 11); if(pFuncOnInit) pFuncOnInit(); // Call the user function } + +void XBOXONE::setRumbleOff() { + uint8_t writeBuf[12]; + + // Activate rumble + writeBuf[0] = 0x09; + writeBuf[1] = 0x08; // 0x20 bit and all bits of 0x07 prevents rumble effect + writeBuf[2] = 0x00; // This may have something to do with how many times effect is played + + // Continuous rumble effect + writeBuf[3] = 0x08; // Substructure (what substructure rest of this packet has) + writeBuf[4] = 0x00; // Mode + writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R) + writeBuf[6] = 0x00; // lT force + writeBuf[7] = 0x00; // rT force + writeBuf[8] = 0x00; // L force + writeBuf[9] = 0x00; // R force + writeBuf[10] = 0x00; // Length of pulse + writeBuf[11] = 0x00; // Period between pulses + XboxCommand(writeBuf, 12); +} + +void XBOXONE::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor) { + uint8_t writeBuf[12]; + + // Activate rumble + writeBuf[0] = 0x09; + writeBuf[1] = 0x08; // 0x20 bit and all bits of 0x07 prevents rumble effect + writeBuf[2] = 0x00; // This may have something to do with how many times effect is played + + // Continuous rumble effect + writeBuf[3] = 0x08; // Substructure (what substructure rest of this packet has) + writeBuf[4] = 0x00; // Mode + writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R) + writeBuf[6] = leftTrigger; // lT force + writeBuf[7] = rightTrigger; // rT force + writeBuf[8] = leftMotor; // L force + writeBuf[9] = rightMotor; // R force + writeBuf[10] = 0x80; // Length of pulse + writeBuf[11] = 0x00; // Period between pulses + XboxCommand(writeBuf, 12); +} diff --git a/XBOXONE.h b/XBOXONE.h index 131a1fbd..2064a33f 100644 --- a/XBOXONE.h +++ b/XBOXONE.h @@ -156,6 +156,18 @@ public: void attachOnInit(void (*funcOnInit)(void)) { pFuncOnInit = funcOnInit; }; + + /** Used to set the rumble off. */ + void setRumbleOff(); + + /** + * Used to turn on rumble continuously. + * @param leftTrigger Left trigger force. + * @param rightTrigger Right trigger force. + * @param leftMotor Left motor force. + * @param rightMotor Right motor force. + */ + void setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor); /**@}*/ /** True if a Xbox ONE controller is connected. */ diff --git a/examples/Xbox/XBOXONE/XBOXONE.ino b/examples/Xbox/XBOXONE/XBOXONE.ino index 218f4918..0bc6174b 100644 --- a/examples/Xbox/XBOXONE/XBOXONE.ino +++ b/examples/Xbox/XBOXONE/XBOXONE.ino @@ -63,6 +63,19 @@ void loop() { Serial.println(); } + // Set rumble effect + static uint16_t oldL2Value, oldR2Value; + if (Xbox.getButtonPress(L2) != oldL2Value || Xbox.getButtonPress(R2) != oldR2Value) { + oldL2Value = Xbox.getButtonPress(L2); + oldR2Value = Xbox.getButtonPress(R2); + uint8_t leftRumble = map(oldL2Value, 0, 65535, 0, 255); // Map the trigger values into a byte + uint8_t rightRumble = map(oldR2Value, 0, 65535, 0, 255); + if (leftRumble > 0 || rightRumble > 0) + Xbox.setRumbleOn(leftRumble, rightRumble, leftRumble, rightRumble); + else + Xbox.setRumbleOff(); + } + if (Xbox.getButtonClick(UP)) Serial.println(F("Up")); if (Xbox.getButtonClick(DOWN)) From 25572863b7dfb5230e9b5345e7b4d479c49d4468 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Thu, 15 Mar 2018 20:26:43 +0100 Subject: [PATCH 2/3] Use a counter when sending Xbox commands This is needed in order for rumble to work This is how it is done in the Linux kernel - see: https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c --- XBOXONE.cpp | 44 +++++++++++++++++++++++++------------------- XBOXONE.h | 1 + 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/XBOXONE.cpp b/XBOXONE.cpp index 4e5b3d84..b1011570 100644 --- a/XBOXONE.cpp +++ b/XBOXONE.cpp @@ -179,10 +179,11 @@ uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) { delay(200); // let things settle // Initialize the controller for input + cmdCounter = 0; // Reset the counter used when sending out the commands uint8_t writeBuf[5]; writeBuf[0] = 0x05; writeBuf[1] = 0x20; - writeBuf[2] = 0x00; + // Byte 2 is set in "XboxCommand" writeBuf[3] = 0x01; writeBuf[4] = 0x00; rcode = XboxCommand(writeBuf, 5); @@ -402,6 +403,7 @@ int16_t XBOXONE::getAnalogHat(AnalogHatEnum a) { /* Xbox Controller commands */ uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) { + data[2] = cmdCounter++; // Increment the output command counter uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_ONE_OUTPUT_PIPE ].epAddr, nbytes, data); #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nXboxCommand, Return: "), 0x80); @@ -413,12 +415,12 @@ uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) { // The Xbox One packets are described at: https://github.com/quantus/xbox-one-controller-protocol void XBOXONE::onInit() { // A short buzz to show the controller is active - uint8_t writeBuf[11]; + uint8_t writeBuf[13]; // Activate rumble writeBuf[0] = 0x09; - writeBuf[1] = 0x08; // 0x20 bit and all bits of 0x07 prevents rumble effect - writeBuf[2] = 0x00; // This may have something to do with how many times effect is played + writeBuf[1] = 0x00; + // Byte 2 is set in "XboxCommand" // Single rumble effect writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has) @@ -429,50 +431,54 @@ void XBOXONE::onInit() { writeBuf[8] = 0x20; // L force writeBuf[9] = 0x20; // R force writeBuf[10] = 0x80; // Length of pulse - XboxCommand(writeBuf, 11); + writeBuf[11] = 0x00; // Off period + writeBuf[12] = 0x00; // Repeat count + XboxCommand(writeBuf, 13); if(pFuncOnInit) pFuncOnInit(); // Call the user function } void XBOXONE::setRumbleOff() { - uint8_t writeBuf[12]; + uint8_t writeBuf[13]; // Activate rumble writeBuf[0] = 0x09; - writeBuf[1] = 0x08; // 0x20 bit and all bits of 0x07 prevents rumble effect - writeBuf[2] = 0x00; // This may have something to do with how many times effect is played + writeBuf[1] = 0x00; + // Byte 2 is set in "XboxCommand" // Continuous rumble effect - writeBuf[3] = 0x08; // Substructure (what substructure rest of this packet has) + writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has) writeBuf[4] = 0x00; // Mode writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R) writeBuf[6] = 0x00; // lT force writeBuf[7] = 0x00; // rT force writeBuf[8] = 0x00; // L force writeBuf[9] = 0x00; // R force - writeBuf[10] = 0x00; // Length of pulse - writeBuf[11] = 0x00; // Period between pulses - XboxCommand(writeBuf, 12); + writeBuf[10] = 0x00; // On period + writeBuf[11] = 0x00; // Off period + writeBuf[12] = 0x00; // Repeat count + XboxCommand(writeBuf, 13); } void XBOXONE::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor) { - uint8_t writeBuf[12]; + uint8_t writeBuf[13]; // Activate rumble writeBuf[0] = 0x09; - writeBuf[1] = 0x08; // 0x20 bit and all bits of 0x07 prevents rumble effect - writeBuf[2] = 0x00; // This may have something to do with how many times effect is played + writeBuf[1] = 0x00; + // Byte 2 is set in "XboxCommand" // Continuous rumble effect - writeBuf[3] = 0x08; // Substructure (what substructure rest of this packet has) + writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has) writeBuf[4] = 0x00; // Mode writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R) writeBuf[6] = leftTrigger; // lT force writeBuf[7] = rightTrigger; // rT force writeBuf[8] = leftMotor; // L force writeBuf[9] = rightMotor; // R force - writeBuf[10] = 0x80; // Length of pulse - writeBuf[11] = 0x00; // Period between pulses - XboxCommand(writeBuf, 12); + writeBuf[10] = 0xFF; // On period + writeBuf[11] = 0x00; // Off period + writeBuf[12] = 0xFF; // Repeat count + XboxCommand(writeBuf, 13); } diff --git a/XBOXONE.h b/XBOXONE.h index 2064a33f..fad3785c 100644 --- a/XBOXONE.h +++ b/XBOXONE.h @@ -229,6 +229,7 @@ private: bool R2Clicked; uint8_t readBuf[XBOX_ONE_EP_MAXPKTSIZE]; // General purpose buffer for input data + uint8_t cmdCounter; void readReport(); // Used to read the incoming data From a37d0c0ea51df151e1cc980026f6e074e05ac532 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Thu, 15 Mar 2018 21:25:39 +0100 Subject: [PATCH 3/3] The returned trigger values are only 10-bits --- examples/Xbox/XBOXONE/XBOXONE.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Xbox/XBOXONE/XBOXONE.ino b/examples/Xbox/XBOXONE/XBOXONE.ino index 0bc6174b..b49f4076 100644 --- a/examples/Xbox/XBOXONE/XBOXONE.ino +++ b/examples/Xbox/XBOXONE/XBOXONE.ino @@ -68,8 +68,8 @@ void loop() { if (Xbox.getButtonPress(L2) != oldL2Value || Xbox.getButtonPress(R2) != oldR2Value) { oldL2Value = Xbox.getButtonPress(L2); oldR2Value = Xbox.getButtonPress(R2); - uint8_t leftRumble = map(oldL2Value, 0, 65535, 0, 255); // Map the trigger values into a byte - uint8_t rightRumble = map(oldR2Value, 0, 65535, 0, 255); + uint8_t leftRumble = map(oldL2Value, 0, 1023, 0, 255); // Map the trigger values into a byte + uint8_t rightRumble = map(oldR2Value, 0, 1023, 0, 255); if (leftRumble > 0 || rightRumble > 0) Xbox.setRumbleOn(leftRumble, rightRumble, leftRumble, rightRumble); else