PS5 is now also working via Bluetooth

However the output report is still not working
This commit is contained in:
Kristian Sloth Lauszus 2021-01-17 23:34:31 +01:00
parent ee7bf6e5a0
commit 28a75dea6b
11 changed files with 480 additions and 28 deletions

View file

@ -8,7 +8,7 @@ jobs:
strategy: strategy:
matrix: matrix:
# find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g'
example: [examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] example: [examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-python@v2 - uses: actions/setup-python@v2

12
BTD.cpp
View file

@ -317,7 +317,7 @@ void BTD::Initialize() {
incomingWii = false; incomingWii = false;
connectToHIDDevice = false; connectToHIDDevice = false;
incomingHIDDevice = false; incomingHIDDevice = false;
incomingPS4 = false; incomingPSController = false;
bAddress = 0; // Clear device address bAddress = 0; // Clear device address
bNumEP = 1; // Must have to be reset to 1 bNumEP = 1; // Must have to be reset to 1
qNextPollTime = 0; // Reset next poll time qNextPollTime = 0; // Reset next poll time
@ -1011,9 +1011,9 @@ void BTD::HCI_task() {
} }
if(classOfDevice[2] == 0 && classOfDevice[1] == 0x25 && classOfDevice[0] == 0x08 && strncmp((const char*)remote_name, "Wireless Controller", 19) == 0) { if(classOfDevice[2] == 0 && classOfDevice[1] == 0x25 && classOfDevice[0] == 0x08 && strncmp((const char*)remote_name, "Wireless Controller", 19) == 0) {
#ifdef DEBUG_USB_HOST #ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nPS4 controller is connecting"), 0x80); Notify(PSTR("\r\nPS4/PS5 controller is connecting"), 0x80);
#endif #endif
incomingPS4 = true; incomingPSController = true;
} }
if((pairWithWii || pairWithHIDDevice) && checkRemoteName) if((pairWithWii || pairWithHIDDevice) && checkRemoteName)
hci_state = HCI_CONNECT_DEVICE_STATE; hci_state = HCI_CONNECT_DEVICE_STATE;
@ -1034,8 +1034,8 @@ void BTD::HCI_task() {
} }
D_PrintHex<uint8_t > (disc_bdaddr[0], 0x80); D_PrintHex<uint8_t > (disc_bdaddr[0], 0x80);
#endif #endif
if(incomingPS4) if(incomingPSController)
connectToHIDDevice = true; // We should always connect to the PS4 controller connectToHIDDevice = true; // We should always connect to the PS4/PS5 controller
// Clear these flags for a new connection // Clear these flags for a new connection
l2capConnectionClaimed = false; l2capConnectionClaimed = false;
@ -1068,7 +1068,7 @@ void BTD::HCI_task() {
connectToWii = incomingWii = pairWithWii = false; connectToWii = incomingWii = pairWithWii = false;
connectToHIDDevice = incomingHIDDevice = pairWithHIDDevice = checkRemoteName = false; connectToHIDDevice = incomingHIDDevice = pairWithHIDDevice = checkRemoteName = false;
incomingPS4 = false; incomingPSController = false;
hci_state = HCI_SCANNING_STATE; hci_state = HCI_SCANNING_STATE;
} }

2
BTD.h
View file

@ -575,7 +575,7 @@ private:
bool pairWiiUsingSync; // True if pairing was done using the Wii SYNC button. bool pairWiiUsingSync; // True if pairing was done using the Wii SYNC button.
bool checkRemoteName; // Used to check remote device's name before connecting. bool checkRemoteName; // Used to check remote device's name before connecting.
bool incomingPS4; // True if a PS4 controller is connecting bool incomingPSController; // True if a PS4/PS5 controller is connecting
uint8_t classOfDevice[3]; // Class of device of last device uint8_t classOfDevice[3]; // Class of device of last device
/* Variables used by high level HCI task */ /* Variables used by high level HCI task */

282
PS5BT.h Normal file
View file

@ -0,0 +1,282 @@
/* 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
*/
#ifndef _ps5bt_h_
#define _ps5bt_h_
#include "BTHID.h"
#include "PS5Parser.h"
/*const uint32_t crc32_table[] PROGMEM = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
uint32_t crc32(uint8_t *buffer, size_t length) { // Inspired by: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/crc32.c and http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=28214
uint32_t crc = ~0L; // Initial value
for (size_t i = 0; i < length; i++)
crc = (crc >> 8) ^ pgm_read_dword(&crc32_table[*buffer++ ^ (crc & 0xFF)]);
return ~crc;
};*/
/*
* There are multiple 16-bit CRC polynomials in common use, but this is
* *the* standard CRC-32 polynomial, first popularized by Ethernet.
* x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
*/
#if 0
#define CRC32_POLY_LE 0xedb88320
#define CRC32_POLY_BE 0x04c11db7
#define CRC_LE_BITS 1
typedef uint32_t u32;
static inline u32 crc32_le_generic(u32 crc, unsigned char const *p, size_t len, const u32 (*tab)[256], u32 polynomial)
{
#if CRC_LE_BITS == 1
int i;
while (len--) {
crc ^= *p++;
for (i = 0; i < 8; i++)
crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0);
}
# elif CRC_LE_BITS == 2
while (len--) {
crc ^= *p++;
crc = (crc >> 2) ^ tab[0][crc & 3];
crc = (crc >> 2) ^ tab[0][crc & 3];
crc = (crc >> 2) ^ tab[0][crc & 3];
crc = (crc >> 2) ^ tab[0][crc & 3];
}
# elif CRC_LE_BITS == 4
while (len--) {
crc ^= *p++;
crc = (crc >> 4) ^ tab[0][crc & 15];
crc = (crc >> 4) ^ tab[0][crc & 15];
}
# elif CRC_LE_BITS == 8
/* aka Sarwate algorithm */
while (len--) {
crc ^= *p++;
crc = (crc >> 8) ^ tab[0][crc & 255];
}
# else
crc = (__force u32) __cpu_to_le32(crc);
crc = crc32_body(crc, p, len, tab);
crc = __le32_to_cpu((__force __le32)crc);
#endif
return crc;
}
#if CRC_LE_BITS == 1
static u32 crc32_le(u32 crc, unsigned char const *p, size_t len)
{
return crc32_le_generic(crc, p, len, NULL, CRC32_POLY_LE);
}
#else
static u32 crc32_le(u32 crc, unsigned char const *p, size_t len)
{
return crc32_le_generic(crc, p, len, (const u32 (*)[256])crc32table_le, CRC32_POLY_LE);
}
#endif
#endif
/**
* This class implements support for the PS5 controller via Bluetooth.
* It uses the BTHID class for all the Bluetooth communication.
*/
class PS5BT : public BTHID, public PS5Parser {
public:
/**
* Constructor for the PS5BT class.
* @param p Pointer to the BTD class instance.
* @param pair Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
*/
PS5BT(BTD *p, bool pair = false, const char *pin = "0000") :
BTHID(p, pair, pin) {
PS5Parser::Reset();
};
/**
* Used to check if a PS5 controller is connected.
* @return Returns true if it is connected.
*/
bool connected() {
return BTHID::connected;
};
protected:
/** @name BTHID implementation */
/**
* Used to parse Bluetooth HID data.
* @param len The length of the incoming data.
* @param buf Pointer to the data buffer.
*/
virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) {
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 void OnInitBTHID() {
PS5Parser::Reset();
enable_sixaxis(); // Make the controller send out the entire output report
if (pFuncOnInit)
pFuncOnInit(); // Call the user function
else
setLed(Blue);
};
/** Used to reset the different buffers to there default values */
virtual void ResetBTHID() {
PS5Parser::Reset();
};
/**@}*/
/** @name PS5Parser implementation */
virtual void sendOutputReport(PS5Output *output) {
#if 1
return; // TODO: Fix this
#else
// See the series of patches here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/
uint8_t buf[1 /* BT Set Output Report */ + 1 /* report id */ + 1 /* seq_tag */ + 1 /* tag */ + 47 /* common */ + 24 /* reserved */ + 4 /* crc32 */];
memset(buf, 0, sizeof(buf));
buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)
buf[0x01] = 0x31; // Report ID
buf[0x02] = (output_sequence << 4) | 0x0; // Highest 4-bit is a sequence number, which needs to be increased every report. Lowest 4-bit is tag and can be zero for now.
if(++output_sequence == 15)
output_sequence = 0;
buf[0x03] = 0x10; // Magic number must be set to 0x10
buf[0x01 + 3] = 0xFF; // feature flags 1
buf[0x02 + 3]= 0xF7; // feature flags 2
buf[0x03 + 3] = output->smallRumble; // Small Rumble
buf[0x04 + 3] = output->bigRumble; // Big rumble
// 5-7 headphone, speaker, mic volume, audio flags
buf[0x09 + 3] = (uint8_t)output->microphoneLed;
// 0x0A mute flags
// Adaptive Triggers: 0x0B-0x14 right, 0x15 unknown, 0x16-0x1F left
rightTrigger.processTrigger(&buf[0x0B + 3]); // right
leftTrigger.processTrigger(&buf[0x16 + 3]); // left
// 0x20-0x24 unknown
// 0x25 trigger motor effect strengths
// 0x26 speaker volume
// player LEDs
buf[0x27 + 3] = 0x03; // led brightness, pulse
buf[0x2A + 3] = output->disableLeds ? 0x01 : 0x2; // led pulse option
// buf[0x2B] LED brightness, 0 = full, 1= medium, 2 = low
buf[0x2C + 3] = output->playerLeds; // 5 white player LEDs
// lightbar
buf[0x2D + 3] = output->r; // Red
buf[0x2E + 3] = output->g; // Green
buf[0x2F + 3] = output->b; // Blue
//uint32_t crc = crc32(&buf[1], 79 - 1 /* do not include the BT Set Output Report */ - 4 /* crc */);
uint8_t seed = 0xA2;
uint32_t crc = crc32_le(0xFFFFFFFF, &seed, 1);
crc = ~crc32_le(crc, &buf[1], 79 - 1 /* do not include the BT Set Output Report */ - 4 /* crc */);
buf[75] = crc;
buf[76] = crc >> 8;
buf[77] = crc >> 16;
buf[78] = crc >> 24;
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
HID_Command(buf, sizeof(buf));
#endif
};
/**@}*/
private:
uint8_t output_sequence = 0;
void enable_sixaxis() { // Command used to make the PS5 controller send out the entire output report
// Request the paring info. This makes the controller send out the full report - see: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/
uint8_t buf[2];
buf[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03)
buf[1] = 9; // Report ID for paring info
HID_Command(buf, 2);
};
void HID_Command(uint8_t *data, uint8_t nbytes) {
pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]);
};
};
#endif

View file

@ -15,7 +15,8 @@
e-mail : lauszus@gmail.com e-mail : lauszus@gmail.com
Thanks to Joseph Duchesne for the initial code. Thanks to Joseph Duchesne for the initial code.
Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port. Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port
and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/
*/ */
#include "PS5Parser.h" #include "PS5Parser.h"
@ -88,19 +89,21 @@ void PS5Parser::Parse(uint8_t len, uint8_t *buf) {
if (buf[0] == 0x01) // Check report ID if (buf[0] == 0x01) // Check report ID
memcpy(&ps5Data, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(ps5Data))); 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 else if (buf[0] == 0x31) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data
if (len < 4) { if (len < 3) {
#ifdef DEBUG_USB_HOST #ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nReport is too short: "), 0x80); Notify(PSTR("\r\nReport is too short: "), 0x80);
D_PrintHex<uint8_t > (len, 0x80); D_PrintHex<uint8_t > (len, 0x80);
#endif #endif
return; return;
} }
memcpy(&ps5Data, buf + 3, min((uint8_t)(len - 3), MFK_CASTUINT8T sizeof(ps5Data))); memcpy(&ps5Data, buf + 2, min((uint8_t)(len - 2), MFK_CASTUINT8T sizeof(ps5Data)));
} else { } else {
#ifdef DEBUG_USB_HOST #ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nUnknown report id: "), 0x80); Notify(PSTR("\r\nUnknown report id: "), 0x80);
D_PrintHex<uint8_t > (buf[0], 0x80); D_PrintHex<uint8_t > (buf[0], 0x80);
Notify(PSTR(", len: "), 0x80);
D_PrintHex<uint8_t > (len, 0x80);
#endif #endif
return; return;
} }

View file

@ -15,7 +15,8 @@
e-mail : lauszus@gmail.com e-mail : lauszus@gmail.com
Thanks to Joseph Duchesne for the initial code. Thanks to Joseph Duchesne for the initial code.
Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port. Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port
and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/
*/ */
#ifndef _ps5parser_h_ #ifndef _ps5parser_h_
@ -32,7 +33,7 @@ const uint8_t PS5_BUTTONS[] PROGMEM = {
DOWN, // DOWN DOWN, // DOWN
LEFT, // LEFT LEFT, // LEFT
0x0C, // SHARE 0x0C, // CREATE
0x0D, // OPTIONS 0x0D, // OPTIONS
0x0E, // L3 0x0E, // L3
0x0F, // R3 0x0F, // R3
@ -64,7 +65,7 @@ union PS5Buttons {
uint8_t r1 : 1; uint8_t r1 : 1;
uint8_t l2 : 1; uint8_t l2 : 1;
uint8_t r2 : 1; uint8_t r2 : 1;
uint8_t share : 1; uint8_t create : 1;
uint8_t menu : 1; uint8_t menu : 1;
uint8_t l3 : 1; uint8_t l3 : 1;
uint8_t r3 : 1; uint8_t r3 : 1;
@ -96,7 +97,7 @@ union PS5Status {
// second byte // second byte
uint8_t mic : 1; uint8_t mic : 1;
uint8_t dummy4 : 3; uint8_t dummy3 : 3;
} __attribute__((packed)); } __attribute__((packed));
uint16_t val; uint16_t val;
} __attribute__((packed)); } __attribute__((packed));
@ -106,28 +107,28 @@ struct PS5Data {
uint8_t hatValue[4]; // 0-3 bytes uint8_t hatValue[4]; // 0-3 bytes
uint8_t trigger[2]; // 4-5 uint8_t trigger[2]; // 4-5
uint8_t dummy; // 6 unknown uint8_t sequence_number; // 6
PS5Buttons btn; // 7-9 PS5Buttons btn; // 7-9
uint8_t dummy2[5]; // 0xA-0xD unknown uint8_t reserved[5]; // 0xA-0xD
/* Gyro and accelerometer values */ /* Gyro and accelerometer values */
int16_t gyroX, gyroZ, gyroY; // 0x0F - 0x14 int16_t gyroX, gyroZ, gyroY; // 0x0F - 0x14
int16_t accX, accZ, accY; // 0x15-0x1A int16_t accX, accZ, accY; // 0x15-0x1A
int32_t sensor_timestamp;
uint8_t dummy3[5]; // 0x1B - 0x1F unknown uint8_t reserved2;
// 0x20 - 0x23 touchpad point 1 // 0x20 - 0x23 touchpad point 1
// 0x24 - 0x27 touchpad point 2 // 0x24 - 0x27 touchpad point 2
ps5TouchpadXY xy; ps5TouchpadXY xy;
uint8_t dummy4; //0x28 unknown uint8_t reserved3; // 0x28
uint8_t rightTriggerFeedback; // 0x29 uint8_t rightTriggerFeedback; // 0x29
uint8_t leftTriggerFeedback; // 0x2A uint8_t leftTriggerFeedback; // 0x2A
uint8_t reserved4[10]; // 0x2B - 0x34
uint8_t dummy5[10]; // 0x2B - 0x34 unknown
// status bytes 0x35-0x36 // status bytes 0x35-0x36
PS5Status status; PS5Status status;
@ -260,7 +261,7 @@ public:
* @return The battery level in the range 0-15. * @return The battery level in the range 0-15.
*/ */
/*uint8_t getBatteryLevel() { /*uint8_t getBatteryLevel() {
return ps5Data.status.battery; return ps5Data.status.battery; // TODO: Where to read the battery level?
};*/ };*/
/** /**

View file

@ -93,13 +93,16 @@ protected:
virtual void sendOutputReport(PS5Output *output) { // Source: https://github.com/chrippa/ds4drv virtual void sendOutputReport(PS5Output *output) { // Source: https://github.com/chrippa/ds4drv
// PS4 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/ // 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 // , Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows
uint8_t buf[48]; // and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/
uint8_t buf[1 /* report id */ + 47 /* common */];
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
buf[0x00] = 0x02; // report type buf[0x00] = 0x02; // Report ID
buf[0x01] = 0xFF; // feature flags 1 buf[0x01] = 0xFF; // feature flags 1
buf[0x02]= 0xF7; // feature flags 2 buf[0x02]= 0xF7; // feature flags 2
buf[0x03] = output->smallRumble; // Small Rumble buf[0x03] = output->smallRumble; // Small Rumble
buf[0x04] = output->bigRumble; // Big rumble buf[0x04] = output->bigRumble; // Big rumble

View file

@ -161,6 +161,7 @@ enum ButtonEnum {
/**@{*/ /**@{*/
/** PS5 buttons */ /** PS5 buttons */
CREATE = 4,
MICROPHONE = 18, MICROPHONE = 18,
/**@}*/ /**@}*/
}; };

View file

@ -0,0 +1,159 @@
/*
Example sketch for the PS5 Bluetooth 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 <PS5BT.h>
#include <usbhub.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;
//USBHub Hub1(&Usb); // Some dongles have a hub inside
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
/* You can create the instance of the PS5BT class in two ways */
// This will start an inquiry and then pair with the PS5 controller - you only have to do this once
// You will need to hold down the PS and Share button at the same time, the PS5 controller will then start to blink rapidly indicating that it is in pairing mode
PS5BT PS5(&Btd, PAIR);
// After that you can simply create the instance like so and then press the PS button on the device
//PS5BT PS5(&Btd);
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 Bluetooth 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"));
PS5.disconnect();
} else {
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(CREATE))
Serial.print(F("\r\nCreate"));
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"));
}
}
}
}
}
}

View file

@ -106,8 +106,8 @@ void loop() {
if (PS5.getButtonClick(R3)) if (PS5.getButtonClick(R3))
Serial.print(F("\r\nR3")); Serial.print(F("\r\nR3"));
if (PS5.getButtonClick(SHARE)) if (PS5.getButtonClick(CREATE))
Serial.print(F("\r\nShare")); Serial.print(F("\r\nCreate"));
if (PS5.getButtonClick(OPTIONS)) { if (PS5.getButtonClick(OPTIONS)) {
Serial.print(F("\r\nOptions")); Serial.print(F("\r\nOptions"));
printAngle = !printAngle; printAngle = !printAngle;

View file

@ -36,6 +36,8 @@ PS3BT KEYWORD1
PS3USB KEYWORD1 PS3USB KEYWORD1
PS4BT KEYWORD1 PS4BT KEYWORD1
PS4USB KEYWORD1 PS4USB KEYWORD1
PS5BT KEYWORD1
PS5USB KEYWORD1
#################################################### ####################################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
@ -135,6 +137,7 @@ SHARE LITERAL1
OPTIONS LITERAL1 OPTIONS LITERAL1
TOUCHPAD LITERAL1 TOUCHPAD LITERAL1
CREATE LITERAL1
MICROPHONE LITERAL1 MICROPHONE LITERAL1
LeftHatX LITERAL1 LeftHatX LITERAL1