mirror of
https://github.com/felis/USB_Host_Shield_2.0.git
synced 2024-03-22 11:31:26 +01:00
PS5 controller is now working via USB
This commit is contained in:
parent
fba77f9119
commit
ee7bf6e5a0
8 changed files with 1135 additions and 0 deletions
160
PS5Parser.cpp
Normal file
160
PS5Parser.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/* Copyright (C) 2021 Kristian Sloth Lauszus. 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 Sloth Lauszus
|
||||||
|
Web : https://lauszus.com
|
||||||
|
e-mail : lauszus@gmail.com
|
||||||
|
|
||||||
|
Thanks to Joseph Duchesne for the initial code.
|
||||||
|
Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PS5Parser.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 PS5 Controller
|
||||||
|
|
||||||
|
bool PS5Parser::checkDpad(ButtonEnum b) {
|
||||||
|
switch (b) {
|
||||||
|
case UP:
|
||||||
|
return ps5Data.btn.dpad == DPAD_LEFT_UP || ps5Data.btn.dpad == DPAD_UP || ps5Data.btn.dpad == DPAD_UP_RIGHT;
|
||||||
|
case RIGHT:
|
||||||
|
return ps5Data.btn.dpad == DPAD_UP_RIGHT || ps5Data.btn.dpad == DPAD_RIGHT || ps5Data.btn.dpad == DPAD_RIGHT_DOWN;
|
||||||
|
case DOWN:
|
||||||
|
return ps5Data.btn.dpad == DPAD_RIGHT_DOWN || ps5Data.btn.dpad == DPAD_DOWN || ps5Data.btn.dpad == DPAD_DOWN_LEFT;
|
||||||
|
case LEFT:
|
||||||
|
return ps5Data.btn.dpad == DPAD_DOWN_LEFT || ps5Data.btn.dpad == DPAD_LEFT || ps5Data.btn.dpad == DPAD_LEFT_UP;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PS5Parser::getButtonPress(ButtonEnum b) {
|
||||||
|
if (b <= LEFT) // Dpad
|
||||||
|
return checkDpad(b);
|
||||||
|
else
|
||||||
|
return ps5Data.btn.val & (1UL << pgm_read_byte(&PS5_BUTTONS[(uint8_t)b]));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PS5Parser::getButtonClick(ButtonEnum b) {
|
||||||
|
uint32_t mask = 1UL << pgm_read_byte(&PS5_BUTTONS[(uint8_t)b]);
|
||||||
|
bool click = buttonClickState.val & mask;
|
||||||
|
buttonClickState.val &= ~mask; // Clear "click" event
|
||||||
|
return click;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t PS5Parser::getAnalogButton(ButtonEnum b) {
|
||||||
|
if (b == L2) // These are the only analog buttons on the controller
|
||||||
|
return ps5Data.trigger[0];
|
||||||
|
else if (b == R2)
|
||||||
|
return ps5Data.trigger[1];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t PS5Parser::getAnalogHat(AnalogHatEnum a) {
|
||||||
|
return ps5Data.hatValue[(uint8_t)a];
|
||||||
|
}
|
||||||
|
|
||||||
|
void PS5Parser::Parse(uint8_t len, uint8_t *buf) {
|
||||||
|
if (len > 1 && buf) {
|
||||||
|
#ifdef PRINTREPORT
|
||||||
|
Notify(PSTR("\r\n"), 0x80);
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
D_PrintHex<uint8_t > (buf[i], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (buf[0] == 0x01) // Check report ID
|
||||||
|
memcpy(&ps5Data, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(ps5Data)));
|
||||||
|
else if (buf[0] == 0x11) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data
|
||||||
|
if (len < 4) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nReport is too short: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (len, 0x80);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(&ps5Data, buf + 3, min((uint8_t)(len - 3), MFK_CASTUINT8T sizeof(ps5Data)));
|
||||||
|
} else {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nUnknown report id: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (buf[0], 0x80);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps5Data.btn.val != oldButtonState.val) { // Check if anything has changed
|
||||||
|
buttonClickState.val = ps5Data.btn.val & ~oldButtonState.val; // Update click state variable
|
||||||
|
oldButtonState.val = ps5Data.btn.val;
|
||||||
|
|
||||||
|
// The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself
|
||||||
|
uint8_t newDpad = 0;
|
||||||
|
if (checkDpad(UP))
|
||||||
|
newDpad |= 1 << UP;
|
||||||
|
if (checkDpad(RIGHT))
|
||||||
|
newDpad |= 1 << RIGHT;
|
||||||
|
if (checkDpad(DOWN))
|
||||||
|
newDpad |= 1 << DOWN;
|
||||||
|
if (checkDpad(LEFT))
|
||||||
|
newDpad |= 1 << LEFT;
|
||||||
|
if (newDpad != oldDpad) {
|
||||||
|
buttonClickState.dpad = newDpad & ~oldDpad; // Override values
|
||||||
|
oldDpad = newDpad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message_counter++;
|
||||||
|
|
||||||
|
if (ps5Output.reportChanged || leftTrigger.reportChanged || rightTrigger.reportChanged)
|
||||||
|
sendOutputReport(&ps5Output); // Send output report
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PS5Parser::Reset() {
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < sizeof(ps5Data.hatValue); i++)
|
||||||
|
ps5Data.hatValue[i] = 127; // Center value
|
||||||
|
ps5Data.btn.val = 0;
|
||||||
|
oldButtonState.val = 0;
|
||||||
|
for (i = 0; i < sizeof(ps5Data.trigger); i++)
|
||||||
|
ps5Data.trigger[i] = 0;
|
||||||
|
for (i = 0; i < sizeof(ps5Data.xy.finger)/sizeof(ps5Data.xy.finger[0]); i++)
|
||||||
|
ps5Data.xy.finger[i].touching = 1; // The bit is cleared if the finger is touching the touchpad
|
||||||
|
|
||||||
|
ps5Data.btn.dpad = DPAD_OFF;
|
||||||
|
oldButtonState.dpad = DPAD_OFF;
|
||||||
|
buttonClickState.dpad = 0;
|
||||||
|
oldDpad = 0;
|
||||||
|
|
||||||
|
leftTrigger.Reset();
|
||||||
|
rightTrigger.Reset();
|
||||||
|
|
||||||
|
ps5Output.bigRumble = ps5Output.smallRumble = 0;
|
||||||
|
ps5Output.microphoneLed = 0;
|
||||||
|
ps5Output.disableLeds = 0;
|
||||||
|
ps5Output.playerLeds = 0;
|
||||||
|
ps5Output.r = ps5Output.g = ps5Output.b = 0;
|
||||||
|
ps5Output.reportChanged = false;
|
||||||
|
};
|
405
PS5Parser.h
Normal file
405
PS5Parser.h
Normal file
|
@ -0,0 +1,405 @@
|
||||||
|
/* Copyright (C) 2021 Kristian Sloth Lauszus. 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 Sloth Lauszus
|
||||||
|
Web : https://lauszus.com
|
||||||
|
e-mail : lauszus@gmail.com
|
||||||
|
|
||||||
|
Thanks to Joseph Duchesne for the initial code.
|
||||||
|
Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ps5parser_h_
|
||||||
|
#define _ps5parser_h_
|
||||||
|
|
||||||
|
#include "Usb.h"
|
||||||
|
#include "controllerEnums.h"
|
||||||
|
#include "PS5Trigger.h"
|
||||||
|
|
||||||
|
/** Buttons on the controller */
|
||||||
|
const uint8_t PS5_BUTTONS[] PROGMEM = {
|
||||||
|
UP, // UP
|
||||||
|
RIGHT, // RIGHT
|
||||||
|
DOWN, // DOWN
|
||||||
|
LEFT, // LEFT
|
||||||
|
|
||||||
|
0x0C, // SHARE
|
||||||
|
0x0D, // OPTIONS
|
||||||
|
0x0E, // L3
|
||||||
|
0x0F, // R3
|
||||||
|
|
||||||
|
0x0A, // L2
|
||||||
|
0x0B, // R2
|
||||||
|
0x08, // L1
|
||||||
|
0x09, // R1
|
||||||
|
|
||||||
|
0x07, // TRIANGLE
|
||||||
|
0x06, // CIRCLE
|
||||||
|
0x05, // CROSS
|
||||||
|
0x04, // SQUARE
|
||||||
|
|
||||||
|
0x10, // PS
|
||||||
|
0x11, // TOUCHPAD
|
||||||
|
0x12, // MICROPHONE
|
||||||
|
};
|
||||||
|
|
||||||
|
union PS5Buttons {
|
||||||
|
struct {
|
||||||
|
uint8_t dpad : 4;
|
||||||
|
uint8_t square : 1;
|
||||||
|
uint8_t cross : 1;
|
||||||
|
uint8_t circle : 1;
|
||||||
|
uint8_t triangle : 1;
|
||||||
|
|
||||||
|
uint8_t l1 : 1;
|
||||||
|
uint8_t r1 : 1;
|
||||||
|
uint8_t l2 : 1;
|
||||||
|
uint8_t r2 : 1;
|
||||||
|
uint8_t share : 1;
|
||||||
|
uint8_t menu : 1;
|
||||||
|
uint8_t l3 : 1;
|
||||||
|
uint8_t r3 : 1;
|
||||||
|
|
||||||
|
uint8_t ps : 1;
|
||||||
|
uint8_t touchpad : 1;
|
||||||
|
uint8_t mic : 1;
|
||||||
|
uint8_t dummy : 5;
|
||||||
|
} __attribute__((packed));
|
||||||
|
uint32_t val : 24;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ps5TouchpadXY {
|
||||||
|
struct {
|
||||||
|
uint8_t counter : 7; // Increments every time a finger is touching the touchpad
|
||||||
|
uint8_t touching : 1; // The top bit is cleared if the finger is touching the touchpad
|
||||||
|
uint16_t x : 12;
|
||||||
|
uint16_t y : 12;
|
||||||
|
} __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
union PS5Status {
|
||||||
|
struct {
|
||||||
|
// first byte
|
||||||
|
uint8_t headphone : 1;
|
||||||
|
uint8_t dummy : 2; // Seems to change when a jack is plugged in. First bit stays on when a mic is plugged in.
|
||||||
|
uint8_t usb : 1; // charging
|
||||||
|
uint8_t dummy2: 4;
|
||||||
|
|
||||||
|
// second byte
|
||||||
|
uint8_t mic : 1;
|
||||||
|
uint8_t dummy4 : 3;
|
||||||
|
} __attribute__((packed));
|
||||||
|
uint16_t val;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct PS5Data {
|
||||||
|
/* Button and joystick values */
|
||||||
|
uint8_t hatValue[4]; // 0-3 bytes
|
||||||
|
uint8_t trigger[2]; // 4-5
|
||||||
|
|
||||||
|
uint8_t dummy; // 6 unknown
|
||||||
|
|
||||||
|
PS5Buttons btn; // 7-9
|
||||||
|
|
||||||
|
uint8_t dummy2[5]; // 0xA-0xD unknown
|
||||||
|
|
||||||
|
/* Gyro and accelerometer values */
|
||||||
|
int16_t gyroX, gyroZ, gyroY; // 0x0F - 0x14
|
||||||
|
int16_t accX, accZ, accY; // 0x15-0x1A
|
||||||
|
|
||||||
|
uint8_t dummy3[5]; // 0x1B - 0x1F unknown
|
||||||
|
|
||||||
|
// 0x20 - 0x23 touchpad point 1
|
||||||
|
// 0x24 - 0x27 touchpad point 2
|
||||||
|
ps5TouchpadXY xy;
|
||||||
|
|
||||||
|
uint8_t dummy4; //0x28 unknown
|
||||||
|
|
||||||
|
uint8_t rightTriggerFeedback; // 0x29
|
||||||
|
uint8_t leftTriggerFeedback; // 0x2A
|
||||||
|
|
||||||
|
uint8_t dummy5[10]; // 0x2B - 0x34 unknown
|
||||||
|
|
||||||
|
// status bytes 0x35-0x36
|
||||||
|
PS5Status status;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct PS5Output {
|
||||||
|
uint8_t bigRumble, smallRumble; // Rumble
|
||||||
|
uint8_t microphoneLed;
|
||||||
|
uint8_t disableLeds;
|
||||||
|
uint8_t playerLeds;
|
||||||
|
uint8_t r, g, b; // RGB for lightbar
|
||||||
|
bool reportChanged; // The data is send when data is received from the controller
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/** This class parses all the data sent by the PS5 controller */
|
||||||
|
class PS5Parser {
|
||||||
|
public:
|
||||||
|
PS5Trigger leftTrigger;
|
||||||
|
PS5Trigger rightTrigger;
|
||||||
|
|
||||||
|
/** Constructor for the PS5Parser class. */
|
||||||
|
PS5Parser() : leftTrigger(), rightTrigger() {
|
||||||
|
Reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @name PS5 Controller functions */
|
||||||
|
/**
|
||||||
|
* getButtonPress(ButtonEnum b) will return true as long as the button is held down.
|
||||||
|
*
|
||||||
|
* While getButtonClick(ButtonEnum b) will only return it once.
|
||||||
|
*
|
||||||
|
* So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
|
||||||
|
* but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
|
||||||
|
* @param b ::ButtonEnum to read.
|
||||||
|
* @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
|
||||||
|
*/
|
||||||
|
bool getButtonPress(ButtonEnum b);
|
||||||
|
bool getButtonClick(ButtonEnum b);
|
||||||
|
/**@}*/
|
||||||
|
/** @name PS5 Controller functions */
|
||||||
|
/**
|
||||||
|
* Used to get the analog value from button presses.
|
||||||
|
* @param b The ::ButtonEnum to read.
|
||||||
|
* The supported buttons are:
|
||||||
|
* ::L2 and ::R2.
|
||||||
|
* @return Analog value in the range of 0-255.
|
||||||
|
*/
|
||||||
|
uint8_t getAnalogButton(ButtonEnum b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to read the analog joystick.
|
||||||
|
* @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
|
||||||
|
* @return Return the analog value in the range of 0-255.
|
||||||
|
*/
|
||||||
|
uint8_t getAnalogHat(AnalogHatEnum a);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the x-coordinate of the touchpad. Position 0 is in the top left.
|
||||||
|
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||||
|
*/
|
||||||
|
uint16_t getX(uint8_t finger = 0) {
|
||||||
|
return ps5Data.xy.finger[finger].x;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the y-coordinate of the touchpad. Position 0 is in the top left.
|
||||||
|
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||||
|
* @return Returns the y-coordinate of the finger.
|
||||||
|
*/
|
||||||
|
uint16_t getY(uint8_t finger = 0) {
|
||||||
|
return ps5Data.xy.finger[finger].y;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whenever the user is toucing the touchpad.
|
||||||
|
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||||
|
* @return Returns true if the specific finger is touching the touchpad.
|
||||||
|
*/
|
||||||
|
bool isTouching(uint8_t finger = 0) {
|
||||||
|
return !(ps5Data.xy.finger[finger].touching); // The bit is cleared when a finger is touching the touchpad
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This counter increments every time a finger touches the touchpad.
|
||||||
|
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||||
|
* @return Return the value of the counter, note that it is only a 7-bit value.
|
||||||
|
*/
|
||||||
|
uint8_t getTouchCounter(uint8_t finger = 00) {
|
||||||
|
return ps5Data.xy.finger[finger].counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the angle of the controller calculated using the accelerometer.
|
||||||
|
* @param a Either ::Pitch or ::Roll.
|
||||||
|
* @return Return the angle in the range of 0-360.
|
||||||
|
*/
|
||||||
|
float getAngle(AngleEnum a) {
|
||||||
|
if (a == Pitch)
|
||||||
|
return (atan2f(ps5Data.accY, ps5Data.accZ) + PI) * RAD_TO_DEG;
|
||||||
|
else
|
||||||
|
return (atan2f(ps5Data.accX, ps5Data.accZ) + PI) * RAD_TO_DEG;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS5 controller.
|
||||||
|
* @param s The sensor to read.
|
||||||
|
* @return Returns the raw sensor reading.
|
||||||
|
*/
|
||||||
|
int16_t getSensor(SensorEnum s) {
|
||||||
|
switch(s) {
|
||||||
|
case gX:
|
||||||
|
return ps5Data.gyroX;
|
||||||
|
case gY:
|
||||||
|
return ps5Data.gyroY;
|
||||||
|
case gZ:
|
||||||
|
return ps5Data.gyroZ;
|
||||||
|
case aX:
|
||||||
|
return ps5Data.accX;
|
||||||
|
case aY:
|
||||||
|
return ps5Data.accY;
|
||||||
|
case aZ:
|
||||||
|
return ps5Data.accZ;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the battery level of the PS5 controller.
|
||||||
|
* @return The battery level in the range 0-15.
|
||||||
|
*/
|
||||||
|
/*uint8_t getBatteryLevel() {
|
||||||
|
return ps5Data.status.battery;
|
||||||
|
};*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to check if an USB cable is connected to the PS5 controller.
|
||||||
|
* @return Returns true if an USB cable is connected.
|
||||||
|
*/
|
||||||
|
bool getUsbStatus() {
|
||||||
|
return ps5Data.status.usb;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to check if an audio jack cable is connected to the PS5 controller.
|
||||||
|
* @return Returns true if an audio jack cable is connected.
|
||||||
|
*/
|
||||||
|
bool getAudioStatus() {
|
||||||
|
return ps5Data.status.headphone;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to check if a microphone is connected to the PS5 controller.
|
||||||
|
* @return Returns true if a microphone is connected.
|
||||||
|
*/
|
||||||
|
bool getMicStatus() {
|
||||||
|
return ps5Data.status.mic;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Turn both rumble and the LEDs off. */
|
||||||
|
void setAllOff() {
|
||||||
|
setRumbleOff();
|
||||||
|
setLedOff();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Set rumble off. */
|
||||||
|
void setRumbleOff() {
|
||||||
|
setRumbleOn(0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn on rumble.
|
||||||
|
* @param mode Either ::RumbleHigh or ::RumbleLow.
|
||||||
|
*/
|
||||||
|
void setRumbleOn(RumbleEnum mode) {
|
||||||
|
if (mode == RumbleLow)
|
||||||
|
setRumbleOn(0x00, 0xFF);
|
||||||
|
else
|
||||||
|
setRumbleOn(0xFF, 0x00);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn on rumble.
|
||||||
|
* @param bigRumble Value for big motor.
|
||||||
|
* @param smallRumble Value for small motor.
|
||||||
|
*/
|
||||||
|
void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) {
|
||||||
|
ps5Output.bigRumble = bigRumble;
|
||||||
|
ps5Output.smallRumble = smallRumble;
|
||||||
|
ps5Output.reportChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Turn all LEDs off. */
|
||||||
|
void setLedOff() {
|
||||||
|
setLed(0, 0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to set the color using RGB values.
|
||||||
|
* @param r,g,b RGB value.
|
||||||
|
*/
|
||||||
|
void setLed(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
ps5Output.r = r;
|
||||||
|
ps5Output.g = g;
|
||||||
|
ps5Output.b = b;
|
||||||
|
ps5Output.reportChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to set the color using the predefined colors in ::ColorsEnum.
|
||||||
|
* @param color The desired color.
|
||||||
|
*/
|
||||||
|
void setLed(ColorsEnum color) {
|
||||||
|
setLed((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Turn all player LEDs off. */
|
||||||
|
void setPlayerLedOff() {
|
||||||
|
setPlayerLed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to set five player LEDs.
|
||||||
|
* @param mask Bit mask to set the five player LEDs. The first 5 bits represent a LED each.
|
||||||
|
*/
|
||||||
|
void setPlayerLed(uint8_t mask) {
|
||||||
|
ps5Output.playerLeds = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Use to turn the microphone LED off. */
|
||||||
|
void setMicLedOff() {
|
||||||
|
setMicLed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to turn the microphone LED on/off.
|
||||||
|
* @param on Turn the microphone LED on/off.
|
||||||
|
*/
|
||||||
|
void setMicLed(bool on) {
|
||||||
|
ps5Output.microphoneLed = on ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the incoming message count. */
|
||||||
|
uint16_t getMessageCounter(){
|
||||||
|
return message_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Used to parse data sent from the PS5 controller.
|
||||||
|
* @param len Length of the data.
|
||||||
|
* @param buf Pointer to the data buffer.
|
||||||
|
*/
|
||||||
|
void Parse(uint8_t len, uint8_t *buf);
|
||||||
|
|
||||||
|
/** Used to reset the different buffers to their default values */
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the output to the PS5 controller. This is implemented in PS5BT.h and PS5USB.h.
|
||||||
|
* @param output Pointer to PS5Output buffer;
|
||||||
|
*/
|
||||||
|
virtual void sendOutputReport(PS5Output *output) = 0;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool checkDpad(ButtonEnum b); // Used to check PS5 DPAD buttons
|
||||||
|
|
||||||
|
PS5Data ps5Data;
|
||||||
|
PS5Buttons oldButtonState, buttonClickState;
|
||||||
|
PS5Output ps5Output;
|
||||||
|
uint8_t oldDpad;
|
||||||
|
uint16_t message_counter = 0;
|
||||||
|
};
|
||||||
|
#endif
|
94
PS5Trigger.cpp
Normal file
94
PS5Trigger.cpp
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* @file PS5Trigger.cpp
|
||||||
|
* @author Ludwig Füchsl, adapted for USB_Host_Library SAMD by Joseph Duchesne
|
||||||
|
* @brief Based on Ludwig Füchsl's DualSense Windows driver https://github.com/Ohjurot/DualSense-Windows
|
||||||
|
* @date 2020-11-25
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020 Ludwig Füchsl
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* Minor updates by Kristian Sloth Lauszus.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PS5Trigger.h"
|
||||||
|
|
||||||
|
void PS5Trigger::processTrigger(uint8_t* buffer) {
|
||||||
|
// Switch on effect
|
||||||
|
switch (data.effectType) {
|
||||||
|
// Continious
|
||||||
|
case EffectType::ContinuousResitance:
|
||||||
|
// Mode
|
||||||
|
buffer[0x00] = 0x01;
|
||||||
|
// Parameters
|
||||||
|
buffer[0x01] = data.Continuous.startPosition;
|
||||||
|
buffer[0x02] = data.Continuous.force;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Section
|
||||||
|
case EffectType::SectionResitance:
|
||||||
|
// Mode
|
||||||
|
buffer[0x00] = 0x02;
|
||||||
|
// Parameters
|
||||||
|
buffer[0x01] = data.Section.startPosition;
|
||||||
|
buffer[0x02] = data.Section.endPosition;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// EffectEx
|
||||||
|
case EffectType::EffectEx:
|
||||||
|
// Mode
|
||||||
|
buffer[0x00] = 0x02 | 0x20 | 0x04;
|
||||||
|
// Parameters
|
||||||
|
buffer[0x01] = 0xFF - data.EffectEx.startPosition;
|
||||||
|
// Keep flag
|
||||||
|
if (data.EffectEx.keepEffect)
|
||||||
|
buffer[0x02] = 0x02;
|
||||||
|
// Forces
|
||||||
|
buffer[0x04] = data.EffectEx.beginForce;
|
||||||
|
buffer[0x05] = data.EffectEx.middleForce;
|
||||||
|
buffer[0x06] = data.EffectEx.endForce;
|
||||||
|
// Frequency
|
||||||
|
buffer[0x09] = data.EffectEx.frequency / 2;
|
||||||
|
if(buffer[0x09] < 1) buffer[0x09] = 1; // minimum frequency
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Calibrate
|
||||||
|
case EffectType::Calibrate:
|
||||||
|
// Mode
|
||||||
|
buffer[0x00] = 0xFC;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// No resistance / default
|
||||||
|
case EffectType::NoResitance:
|
||||||
|
default:
|
||||||
|
// All zero
|
||||||
|
buffer[0x00] = 0x00;
|
||||||
|
buffer[0x01] = 0x00;
|
||||||
|
buffer[0x02] = 0x00;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reportChanged = false;
|
||||||
|
}
|
168
PS5Trigger.h
Normal file
168
PS5Trigger.h
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
* @file PS5Trigger.h
|
||||||
|
* @author Ludwig Füchsl, adapted for USB_Host_Library SAMD by Joseph Duchesne
|
||||||
|
* @brief Based on Ludwig Füchsl's DualSense Windows driver https://github.com/Ohjurot/DualSense-Windows
|
||||||
|
* @version 0.1
|
||||||
|
* @date 2020-11-25
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020 Ludwig Füchsl
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* Minor updates by Kristian Sloth Lauszus.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ps5trigger_h_
|
||||||
|
#define _ps5trigger_h_
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
class PS5Trigger {
|
||||||
|
private:
|
||||||
|
// Type of trigger effect
|
||||||
|
typedef enum _EffectType : uint8_t {
|
||||||
|
NoResitance = 0x00, // No resistance is applied
|
||||||
|
ContinuousResitance = 0x01, // Continuous Resitance is applied
|
||||||
|
SectionResitance = 0x02, // Seciton resistance is appleyed
|
||||||
|
EffectEx = 0x26, // Extended trigger effect
|
||||||
|
Calibrate = 0xFC, // Calibrate triggers
|
||||||
|
} EffectType;
|
||||||
|
|
||||||
|
// Trigger effect
|
||||||
|
typedef struct _EffectData {
|
||||||
|
// Trigger effect type
|
||||||
|
EffectType effectType;
|
||||||
|
|
||||||
|
// Union for effect parameters
|
||||||
|
union {
|
||||||
|
// Union one raw data
|
||||||
|
uint8_t _u1_raw[6];
|
||||||
|
|
||||||
|
// For type == ContinuousResitance
|
||||||
|
struct {
|
||||||
|
uint8_t startPosition; // Start position of resistance
|
||||||
|
uint8_t force; // Force of resistance
|
||||||
|
uint8_t _pad[4]; // PAD / UNUSED
|
||||||
|
} __attribute__((packed)) Continuous;
|
||||||
|
|
||||||
|
// For type == SectionResitance
|
||||||
|
struct {
|
||||||
|
uint8_t startPosition; // Start position of resistance
|
||||||
|
uint8_t endPosition; // End position of resistance (>= start)
|
||||||
|
uint8_t _pad[4]; // PAD / UNUSED
|
||||||
|
} __attribute__((packed)) Section;
|
||||||
|
|
||||||
|
// For type == EffectEx
|
||||||
|
struct {
|
||||||
|
uint8_t startPosition; // Position at witch the effect starts
|
||||||
|
bool keepEffect; // Wher the effect should keep playing when trigger goes beyond 255
|
||||||
|
uint8_t beginForce; // Force applied when trigger >= (255 / 2)
|
||||||
|
uint8_t middleForce; // Force applied when trigger <= (255 / 2)
|
||||||
|
uint8_t endForce; // Force applied when trigger is beyond 255
|
||||||
|
uint8_t frequency; // Vibration frequency of the trigger
|
||||||
|
} __attribute__((packed)) EffectEx;
|
||||||
|
} __attribute__((packed));
|
||||||
|
} __attribute__((packed)) EffectData;
|
||||||
|
|
||||||
|
EffectData data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool reportChanged = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Apply the trigger data to a PS5 update buffer
|
||||||
|
*
|
||||||
|
* @param buffer The buffer at the start offset for this trigger data
|
||||||
|
*/
|
||||||
|
void processTrigger(uint8_t* buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear force feedback on trigger without report changed
|
||||||
|
*/
|
||||||
|
void Reset() {
|
||||||
|
data.effectType = EffectType::NoResitance;
|
||||||
|
|
||||||
|
reportChanged = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear force feedback on trigger
|
||||||
|
*/
|
||||||
|
void clearTriggerForce() {
|
||||||
|
data.effectType = EffectType::NoResitance;
|
||||||
|
|
||||||
|
reportChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set continuous force feedback on trigger
|
||||||
|
* @param start 0-255 trigger pull to start resisting
|
||||||
|
* @param force The force amount
|
||||||
|
*/
|
||||||
|
void setTriggerForce(uint8_t start, uint8_t force) {
|
||||||
|
if (force == 0)
|
||||||
|
data.effectType = EffectType::NoResitance;
|
||||||
|
else {
|
||||||
|
data.effectType = EffectType::ContinuousResitance;
|
||||||
|
data.Continuous.startPosition = start;
|
||||||
|
data.Continuous.force = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
reportChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set section force feedback on trigger
|
||||||
|
* @param start trigger pull to start resisting
|
||||||
|
* @param end trigger pull to stop resisting
|
||||||
|
*/
|
||||||
|
void setTriggerForceSection(uint8_t start, uint8_t end) {
|
||||||
|
data.effectType = EffectType::SectionResitance;
|
||||||
|
data.Section.startPosition = start;
|
||||||
|
data.Section.endPosition = end;
|
||||||
|
|
||||||
|
reportChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set effect force feedback on trigger
|
||||||
|
* @param start trigger pull to start resisting
|
||||||
|
* @param keep Keep effect active after max trigger pull
|
||||||
|
* @param begin_force 0-255 force at start position
|
||||||
|
* @param mid_force 0-255 force half way between start and max pull
|
||||||
|
* @param end_force 0-255 force at max pull
|
||||||
|
* @param frequency Vibration frequency of the trigger
|
||||||
|
*/
|
||||||
|
void setTriggerForceEffect(uint8_t start, bool keep, uint8_t begin_force, uint8_t mid_force, uint8_t end_force, uint8_t frequency) {
|
||||||
|
data.effectType = EffectType::SectionResitance;
|
||||||
|
data.EffectEx.startPosition = start;
|
||||||
|
data.EffectEx.keepEffect = keep;
|
||||||
|
data.EffectEx.beginForce = begin_force;
|
||||||
|
data.EffectEx.middleForce = mid_force;
|
||||||
|
data.EffectEx.endForce = end_force;
|
||||||
|
data.EffectEx.frequency = frequency;
|
||||||
|
|
||||||
|
reportChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
154
PS5USB.h
Normal file
154
PS5USB.h
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/* Copyright (C) 2021 Kristian Sloth Lauszus. 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 Sloth Lauszus
|
||||||
|
Web : https://lauszus.com
|
||||||
|
e-mail : lauszus@gmail.com
|
||||||
|
|
||||||
|
Thanks to Joseph Duchesne for the initial port. Data structure mapping partially based
|
||||||
|
on values from Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ps5usb_h_
|
||||||
|
#define _ps5usb_h_
|
||||||
|
|
||||||
|
#include "hiduniversal.h"
|
||||||
|
#include "PS5Parser.h"
|
||||||
|
|
||||||
|
#define PS5_VID 0x054C // Sony Corporation
|
||||||
|
#define PS5_PID 0x0CE6 // PS5 Controller
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements support for the PS5 controller via USB.
|
||||||
|
* It uses the HIDUniversal class for all the USB communication.
|
||||||
|
*/
|
||||||
|
class PS5USB : public HIDUniversal, public PS5Parser {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor for the PS5USB class.
|
||||||
|
* @param p Pointer to the USB class instance.
|
||||||
|
*/
|
||||||
|
PS5USB(USB *p) :
|
||||||
|
HIDUniversal(p) {
|
||||||
|
PS5Parser::Reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to check if a PS5 controller is connected.
|
||||||
|
* @return Returns true if it is connected.
|
||||||
|
*/
|
||||||
|
bool connected() {
|
||||||
|
return HIDUniversal::isReady() && HIDUniversal::VID == PS5_VID && HIDUniversal::PID == PS5_PID;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to call your own function when the device is successfully initialized.
|
||||||
|
* @param funcOnInit Function to call.
|
||||||
|
*/
|
||||||
|
void attachOnInit(void (*funcOnInit)(void)) {
|
||||||
|
pFuncOnInit = funcOnInit;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @name HIDUniversal implementation */
|
||||||
|
/**
|
||||||
|
* Used to parse USB HID data.
|
||||||
|
* @param hid Pointer to the HID class.
|
||||||
|
* @param is_rpt_id Only used for Hubs.
|
||||||
|
* @param len The length of the incoming data.
|
||||||
|
* @param buf Pointer to the data buffer.
|
||||||
|
*/
|
||||||
|
virtual void ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) {
|
||||||
|
if (HIDUniversal::VID == PS5_VID && HIDUniversal::PID == PS5_PID)
|
||||||
|
PS5Parser::Parse(len, buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a device is successfully initialized.
|
||||||
|
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
|
||||||
|
* This is useful for instance if you want to set the LEDs in a specific way.
|
||||||
|
*/
|
||||||
|
virtual uint8_t OnInitSuccessful() {
|
||||||
|
if (HIDUniversal::VID == PS5_VID && HIDUniversal::PID == PS5_PID) {
|
||||||
|
PS5Parser::Reset();
|
||||||
|
if (pFuncOnInit)
|
||||||
|
pFuncOnInit(); // Call the user function
|
||||||
|
else
|
||||||
|
setLed(Blue);
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @name PS5Parser implementation */
|
||||||
|
virtual void sendOutputReport(PS5Output *output) { // Source: https://github.com/chrippa/ds4drv
|
||||||
|
// PS4 Source: https://github.com/chrippa/ds4drv
|
||||||
|
// PS5 values from https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/
|
||||||
|
// and Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows
|
||||||
|
uint8_t buf[48];
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
buf[0x00] = 0x02; // report type
|
||||||
|
buf[0x01] = 0xFF; // feature flags 1
|
||||||
|
buf[0x02]= 0xF7; // feature flags 2
|
||||||
|
buf[0x03] = output->smallRumble; // Small Rumble
|
||||||
|
buf[0x04] = output->bigRumble; // Big rumble
|
||||||
|
|
||||||
|
// 5-7 headphone, speaker, mic volume, audio flags
|
||||||
|
|
||||||
|
buf[0x09] = (uint8_t)output->microphoneLed;
|
||||||
|
|
||||||
|
// 0x0A mute flags
|
||||||
|
|
||||||
|
// Adaptive Triggers: 0x0B-0x14 right, 0x15 unknown, 0x16-0x1F left
|
||||||
|
rightTrigger.processTrigger(&buf[0x0B]); // right
|
||||||
|
leftTrigger.processTrigger(&buf[0x16]); // left
|
||||||
|
|
||||||
|
// 0x20-0x24 unknown
|
||||||
|
// 0x25 trigger motor effect strengths
|
||||||
|
// 0x26 speaker volume
|
||||||
|
|
||||||
|
// player LEDs
|
||||||
|
buf[0x27] = 0x03; // led brightness, pulse
|
||||||
|
buf[0x2A] = output->disableLeds ? 0x01 : 0x2; // led pulse option
|
||||||
|
// buf[0x2B] LED brightness, 0 = full, 1= medium, 2 = low
|
||||||
|
buf[0x2C] = output->playerLeds; // 5 white player LEDs
|
||||||
|
|
||||||
|
// lightbar
|
||||||
|
buf[0x2D] = output->r; // Red
|
||||||
|
buf[0x2E] = output->g; // Green
|
||||||
|
buf[0x2F] = output->b; // Blue
|
||||||
|
|
||||||
|
output->reportChanged = false;
|
||||||
|
|
||||||
|
// The PS5 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed
|
||||||
|
|
||||||
|
pUsb->outTransfer(bAddress, epInfo[ hidInterfaces[0].epIndex[epInterruptOutIndex] ].epAddr, sizeof(buf), buf);
|
||||||
|
};
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @name USBDeviceConfig implementation */
|
||||||
|
/**
|
||||||
|
* Used by the USB core to check what this driver support.
|
||||||
|
* @param vid The device's VID.
|
||||||
|
* @param pid The device's PID.
|
||||||
|
* @return Returns true if the device's VID and PID matches this driver.
|
||||||
|
*/
|
||||||
|
virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
|
||||||
|
return (vid == PS5_VID && pid == PS5_PID);
|
||||||
|
};
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
private:
|
||||||
|
void (*pFuncOnInit)(void); // Pointer to function called in onInit()
|
||||||
|
};
|
||||||
|
#endif
|
|
@ -150,6 +150,7 @@ enum ButtonEnum {
|
||||||
MENU = 5,
|
MENU = 5,
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
|
/**@{*/
|
||||||
/** PS Buzz controllers */
|
/** PS Buzz controllers */
|
||||||
RED = 0,
|
RED = 0,
|
||||||
YELLOW = 1,
|
YELLOW = 1,
|
||||||
|
@ -157,6 +158,11 @@ enum ButtonEnum {
|
||||||
ORANGE = 3,
|
ORANGE = 3,
|
||||||
BLUE = 4,
|
BLUE = 4,
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
|
/**@{*/
|
||||||
|
/** PS5 buttons */
|
||||||
|
MICROPHONE = 18,
|
||||||
|
/**@}*/
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Joysticks on the PS3 and Xbox controllers. */
|
/** Joysticks on the PS3 and Xbox controllers. */
|
||||||
|
|
146
examples/PS5USB/PS5USB.ino
Normal file
146
examples/PS5USB/PS5USB.ino
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
Example sketch for the PS5 USB library - developed by Kristian Sloth Lauszus
|
||||||
|
For more information visit the Github repository: github.com/felis/USB_Host_Shield_2.0 or
|
||||||
|
send me an e-mail: lauszus@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <PS5USB.h>
|
||||||
|
|
||||||
|
// Satisfy the IDE, which needs to see the include statment in the ino too.
|
||||||
|
#ifdef dobogusinclude
|
||||||
|
#include <spi4teensy3.h>
|
||||||
|
#endif
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
USB Usb;
|
||||||
|
PS5USB PS5(&Usb);
|
||||||
|
|
||||||
|
bool printAngle, printTouch;
|
||||||
|
uint16_t lastMessageCounter = -1;
|
||||||
|
uint8_t player_led_mask = 0;
|
||||||
|
bool microphone_led = false;
|
||||||
|
|
||||||
|
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.print(F("\r\nPS5 USB Library Started"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Usb.Task();
|
||||||
|
|
||||||
|
if (PS5.connected() && lastMessageCounter != PS5.getMessageCounter()) {
|
||||||
|
lastMessageCounter = PS5.getMessageCounter();
|
||||||
|
|
||||||
|
if (PS5.getAnalogHat(LeftHatX) > 137 || PS5.getAnalogHat(LeftHatX) < 117 || PS5.getAnalogHat(LeftHatY) > 137 || PS5.getAnalogHat(LeftHatY) < 117 || PS5.getAnalogHat(RightHatX) > 137 || PS5.getAnalogHat(RightHatX) < 117 || PS5.getAnalogHat(RightHatY) > 137 || PS5.getAnalogHat(RightHatY) < 117) {
|
||||||
|
Serial.print(F("\r\nLeftHatX: "));
|
||||||
|
Serial.print(PS5.getAnalogHat(LeftHatX));
|
||||||
|
Serial.print(F("\tLeftHatY: "));
|
||||||
|
Serial.print(PS5.getAnalogHat(LeftHatY));
|
||||||
|
Serial.print(F("\tRightHatX: "));
|
||||||
|
Serial.print(PS5.getAnalogHat(RightHatX));
|
||||||
|
Serial.print(F("\tRightHatY: "));
|
||||||
|
Serial.print(PS5.getAnalogHat(RightHatY));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PS5.getAnalogButton(L2) || PS5.getAnalogButton(R2)) { // These are the only analog buttons on the PS5 controller
|
||||||
|
Serial.print(F("\r\nL2: "));
|
||||||
|
Serial.print(PS5.getAnalogButton(L2));
|
||||||
|
Serial.print(F("\tR2: "));
|
||||||
|
Serial.print(PS5.getAnalogButton(R2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the left trigger to resist at the right trigger's level
|
||||||
|
PS5.leftTrigger.setTriggerForce(PS5.getAnalogButton(R2), 255);
|
||||||
|
|
||||||
|
if (PS5.getButtonClick(PS))
|
||||||
|
Serial.print(F("\r\nPS"));
|
||||||
|
if (PS5.getButtonClick(TRIANGLE)) {
|
||||||
|
Serial.print(F("\r\nTriangle"));
|
||||||
|
PS5.setRumbleOn(RumbleLow);
|
||||||
|
}
|
||||||
|
if (PS5.getButtonClick(CIRCLE)) {
|
||||||
|
Serial.print(F("\r\nCircle"));
|
||||||
|
PS5.setRumbleOn(RumbleHigh);
|
||||||
|
}
|
||||||
|
if (PS5.getButtonClick(CROSS)) {
|
||||||
|
Serial.print(F("\r\nCross"));
|
||||||
|
|
||||||
|
// Set the player LEDs
|
||||||
|
player_led_mask = (player_led_mask << 1) | 1;
|
||||||
|
if (player_led_mask > 0x1F)
|
||||||
|
player_led_mask = 0;
|
||||||
|
PS5.setPlayerLed(player_led_mask); // The bottom 5 bits set player LEDs
|
||||||
|
}
|
||||||
|
if (PS5.getButtonClick(SQUARE)) {
|
||||||
|
Serial.print(F("\r\nSquare"));
|
||||||
|
PS5.setRumbleOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PS5.getButtonClick(UP)) {
|
||||||
|
Serial.print(F("\r\nUp"));
|
||||||
|
PS5.setLed(Red);
|
||||||
|
} if (PS5.getButtonClick(RIGHT)) {
|
||||||
|
Serial.print(F("\r\nRight"));
|
||||||
|
PS5.setLed(Blue);
|
||||||
|
} if (PS5.getButtonClick(DOWN)) {
|
||||||
|
Serial.print(F("\r\nDown"));
|
||||||
|
PS5.setLed(Yellow);
|
||||||
|
} if (PS5.getButtonClick(LEFT)) {
|
||||||
|
Serial.print(F("\r\nLeft"));
|
||||||
|
PS5.setLed(Green);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PS5.getButtonClick(L1))
|
||||||
|
Serial.print(F("\r\nL1"));
|
||||||
|
if (PS5.getButtonClick(L3))
|
||||||
|
Serial.print(F("\r\nL3"));
|
||||||
|
if (PS5.getButtonClick(R1))
|
||||||
|
Serial.print(F("\r\nR1"));
|
||||||
|
if (PS5.getButtonClick(R3))
|
||||||
|
Serial.print(F("\r\nR3"));
|
||||||
|
|
||||||
|
if (PS5.getButtonClick(SHARE))
|
||||||
|
Serial.print(F("\r\nShare"));
|
||||||
|
if (PS5.getButtonClick(OPTIONS)) {
|
||||||
|
Serial.print(F("\r\nOptions"));
|
||||||
|
printAngle = !printAngle;
|
||||||
|
}
|
||||||
|
if (PS5.getButtonClick(TOUCHPAD)) {
|
||||||
|
Serial.print(F("\r\nTouchpad"));
|
||||||
|
printTouch = !printTouch;
|
||||||
|
}
|
||||||
|
if (PS5.getButtonClick(MICROPHONE)) {
|
||||||
|
Serial.print(F("\r\nMicrophone"));
|
||||||
|
microphone_led = !microphone_led;
|
||||||
|
PS5.setMicLed(microphone_led);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (printAngle) { // Print angle calculated using the accelerometer only
|
||||||
|
Serial.print(F("\r\nPitch: "));
|
||||||
|
Serial.print(PS5.getAngle(Pitch));
|
||||||
|
Serial.print(F("\tRoll: "));
|
||||||
|
Serial.print(PS5.getAngle(Roll));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (printTouch) { // Print the x, y coordinates of the touchpad
|
||||||
|
if (PS5.isTouching(0) || PS5.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad
|
||||||
|
Serial.print(F("\r\n"));
|
||||||
|
for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers
|
||||||
|
if (PS5.isTouching(i)) { // Print the position of the finger if it is touching the touchpad
|
||||||
|
Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": "));
|
||||||
|
Serial.print(PS5.getX(i));
|
||||||
|
Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": "));
|
||||||
|
Serial.print(PS5.getY(i));
|
||||||
|
Serial.print(F("\t"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -135,6 +135,8 @@ SHARE LITERAL1
|
||||||
OPTIONS LITERAL1
|
OPTIONS LITERAL1
|
||||||
TOUCHPAD LITERAL1
|
TOUCHPAD LITERAL1
|
||||||
|
|
||||||
|
MICROPHONE LITERAL1
|
||||||
|
|
||||||
LeftHatX LITERAL1
|
LeftHatX LITERAL1
|
||||||
LeftHatY LITERAL1
|
LeftHatY LITERAL1
|
||||||
RightHatX LITERAL1
|
RightHatX LITERAL1
|
||||||
|
|
Loading…
Reference in a new issue