Skip to content

Commit ad04705

Browse files
committed
Improvements: wireless mode and battery status display on DualSense
1 parent 9a97ad8 commit ad04705

File tree

4 files changed

+215
-53
lines changed

4 files changed

+215
-53
lines changed

README.RU.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,28 @@
22
[![RU](https://user-images.githubusercontent.com/9499881/27683795-5b0fbac6-5cd8-11e7-929c-057833e01fb1.png)](https://github.com/r57zone/DSAdvance/blob/master/README.RU.md)
33

44
# DSAdvance
5-
Продвинутая эмуляция Xbox геймпада для Sony DualSense, DualShock 4, Nintendo Pro контроллера или Joy-con-ов. Поддерживается прицеливание и вождение наклонами геймпада, а также эмуляция стиков на тачпаде. Работает на базе драйвера [ViGEm](https://github.com/ViGEm).
5+
Продвинутая эмуляция Xbox геймпада для Sony DualSense, DualShock 4, Nintendo Pro контроллера или Joycon-ов. Поддерживается прицеливание и вождение наклонами геймпада, а также эмуляция стиков на тачпаде. Работает на базе драйвера [ViGEm](https://github.com/ViGEm).
66

77
[![](https://user-images.githubusercontent.com/9499881/164945071-5b9f86dd-c396-45a5-817b-fc7068450f02.gif)](https://youtu.be/gkyqO_HuPnk)
88
[![](https://user-images.githubusercontent.com/9499881/164945073-cfa1bfb7-cb82-4714-b2ad-7ecd84a5bcfc.gif)](https://youtu.be/gkyqO_HuPnk)
99

1010
# Режимы работы
11-
Поддерживается несколько режимов работы, переключаются они нажатиями по тачпаду для DualSene и DualShock 4, а для Pro контроллеров, и Joy-Con-ов на кнопки `+`, и `-`. Яркость регулируется без нажатия.
11+
Поддерживается несколько режимов работы, переключаются они нажатиями по тачпаду для DualSene и DualShock 4, а для Pro контроллеров, и JoyCon-ов на кнопки `+`, и `-`. Яркость регулируется без нажатия.
1212

1313
![](https://user-images.githubusercontent.com/9499881/164546701-c1f49c86-2f65-45b0-9a8f-83d751b46004.png)
1414

15-
Для того, чтобы выйти из режима эмуляции стиков на тачпаде нужно переключиться на режим по умолчанию.
15+
Для того, чтобы выйти из режима эмуляции стиков на тачпаде нужно переключиться на режим по умолчанию.
16+
17+
18+
Нажимая на профиль по умолчанию, на DualSense, белые светодиоды отображает текущий статус заряда аккумулятора (1 - 25%, 2 - 50%, 3 - 75%, 4 - 100%).
1619

1720

1821
Кнопка `PS` симулирует нажатие `Win+G`, а кнопка микрофона `Win+Alt+PrtScr`.
1922

2023
## Настройка
2124
1. Установить [ViGEmBus](https://github.com/ViGEm/ViGEmBus/releases).
2225
2. Установите Microsoft Visual C++ Redistributable 2017 или новее.
23-
3. Подключить геймпад Sony DualSense, DualShock 4, Nintendo Pro controller по USB (беспроводной поддерживается ограничено).
26+
3. Подключить геймпад Sony DualSense, DualShock 4, Nintendo Pro controller.
2427
4. Распаковать и запустить DSAdvance.
2528
5. При необходимости измените мёртвые зоны стиков или другие параметры, в конфигурационном файле `Config.ini`.
2629
6. При использовании со Steam играми, в настройках контроллера, отключите "персональные настройки Playstation".
@@ -34,9 +37,8 @@
3437
* Sony за самые продвинутые геймпады и инвестирование в инновации.
3538
* [ViGEm](https://github.com/ViGEm) за возможность эмуляции разных геймпадов.
3639
* [HIDAPI library](https://github.com/signal11/hidapi), с [исправлениями](https://github.com/libusb/hidapi), за библиотеку для работы с USB устройства. В проекте используется этот [форк](https://github.com/r57zone/hidapi).
37-
* [JoyShockLibrary](https://github.com/JibbSmart/JoyShockLibrary) за классную библиотеку геймпадов, позволяющую легко получить вращение контроллера.
38-
* Разработчику [DualSense4Windows](https://github.com/broken-bytes/DualSense4Windows) & [пользователям Reddit](https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/) за детальное описание выходного USB пакета.
39-
* Разработчику [Gen_Dev_TactonBiotic](https://github.com/hizbi-github/Gen_Dev_TactonBiotic) за пример работы с JoyShockLibrary.
40+
* [JoyShockLibrary](https://github.com/JibbSmart/JoyShockLibrary) за классную библиотеку геймпадов, позволяющую легко получить вращение контроллера. Также используется некоторый код из этой библиотеки.
41+
* [Пользователям Reddit](https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/) за детальное описание выходного USB пакета.
4042

4143
## Обратная связь
4244
`r57zone[собака]gmail.com`

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
– Other languages
44

55
# DSAdvance
6-
Advanced Xbox gamepad emulation for Sony DualSense, DualShock 4, Nintendo Pro controller or Joycons. Supports aiming and driving by tilting the gamepad, as well as emulation of sticks on the touchpad. Works based on the driver [ViGEm](https://github.com/ViGEm).
6+
Advanced Xbox gamepad emulation for Sony DualSense, DualShock 4, Nintendo Pro controller or Joy-cons. Supports aiming and driving by tilting the gamepad, as well as emulation of sticks on the touchpad. Works based on the driver [ViGEm](https://github.com/ViGEm).
77

88
[![](https://user-images.githubusercontent.com/9499881/164945071-5b9f86dd-c396-45a5-817b-fc7068450f02.gif)](https://youtu.be/gkyqO_HuPnk)
99
[![](https://user-images.githubusercontent.com/9499881/164945073-cfa1bfb7-cb82-4714-b2ad-7ecd84a5bcfc.gif)](https://youtu.be/gkyqO_HuPnk)
@@ -16,12 +16,15 @@ Several working modes are supported, they are switched by pressing the touchpad
1616
In order to exit the stick emulation mode on the touchpad, need to switch to the default mode.
1717

1818

19+
By clicking on the default profile on DualSense the white LEDs display the current battery status (1 - 25%, 2 - 50%, 3 - 75%, 4 - 100%).
20+
21+
1922
The `PS` button simulates pressing `Win+G`, and the microphone button `Win+Alt+PrtScr`.
2023

2124
## Setup
2225
1. Install [ViGEmBus](https://github.com/ViGEm/ViGEmBus/releases).
2326
2. Install Microsoft Visual C++ Redistributable 2017 or newer.
24-
3. Connect the Sony DualSense, DualShock 4, Nintendo Pro controller via USB (wireless support is limited).
27+
3. Connect the Sony DualSense, DualShock 4, Nintendo Pro controller via USB.
2528
4. Unzip and launch DSAdvance.
2629
5. If necessary, change the dead zones of the sticks or other parameters in the configuration file `Config.ini`.
2730
6. When used with Steam games, in the controller settings, disable "Playstation personal settings".
@@ -35,9 +38,8 @@ The `PS` button simulates pressing `Win+G`, and the microphone button `Win+Alt+P
3538
* Sony for the most advanced gamepads and investing in innovation.
3639
* [ViGEm](https://github.com/ViGEm) for the ability to emulate different gamepads.
3740
* [HIDAPI library](https://github.com/signal11/hidapi) with [fixes](https://github.com/libusb/hidapi) for the library to work with a USB devices. The project uses this [fork](https://github.com/r57zone/hidapi).
38-
* [JoyShockLibrary](https://github.com/JibbSmart/JoyShockLibrary) for a cool gamepad library that makes it easy to get controller rotation.
39-
* For developer of [DualSense4Windows](https://github.com/broken-bytes/DualSense4Windows) & [Reddit users](https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/) for a detailed description of the USB output packet.
40-
* For developer of [Gen_Dev_TactonBiotic](https://github.com/hizbi-github/Gen_Dev_TactonBiotic) for an example of working with JoyShockLibrary.
41+
* [JoyShockLibrary](https://github.com/JibbSmart/JoyShockLibrary) for a cool gamepad library that makes it easy to get controller rotation. Also some code from this library is used.
42+
* For [Reddit users](https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/) for a detailed description of the USB output packet.
4143

4244
## Feedback
4345
`r57zone[at]gmail.com`

Source/DSAdvance/DSAdvance.cpp

Lines changed: 117 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ struct Gamepad {
1212
hid_device *HidHandle;
1313
WORD ControllerType;
1414
bool USBConnection;
15+
unsigned char BatteryMode;
16+
unsigned char BatteryLevel;
1517
wchar_t *serial_number;
1618
};
1719

@@ -21,27 +23,67 @@ InputOutState GamepadOutState;
2123
void GamepadSetState(InputOutState OutState)
2224
{
2325
if (CurGamepad.HidHandle != NULL) {
24-
if (CurGamepad.ControllerType == SONY_DUALSENSE && CurGamepad.USBConnection) { // https://github.com/broken-bytes/DualSense4Windows/blob/master/src/DualSense.cxx & https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/
25-
unsigned char outputReport[48];
26-
memset(outputReport, 0, 48);
27-
28-
outputReport[0] = 0x02;
29-
outputReport[1] = 0xff;
30-
outputReport[2] = 0x15;
31-
outputReport[3] = OutState.LargeMotor;
32-
outputReport[4] = OutState.SmallMotor;
33-
outputReport[5] = 0xff;
34-
outputReport[6] = 0xff;
35-
outputReport[7] = 0xff;
36-
outputReport[8] = 0x0c;
37-
outputReport[38] = 0x07;
38-
outputReport[45] = std::clamp(OutState.LEDRed - OutState.LEDBrightness, 0, 255);
39-
outputReport[46] = std::clamp(OutState.LEDGreen - OutState.LEDBrightness, 0, 255);
40-
outputReport[47] = std::clamp(OutState.LEDBlue - OutState.LEDBrightness, 0, 255);
41-
42-
hid_write(CurGamepad.HidHandle, outputReport, 48);
43-
44-
} else if (CurGamepad.ControllerType == SONY_DUALSHOCK4 && CurGamepad.USBConnection) { // JyoShockLibrary rumble working for USB DS4 ???
26+
if (CurGamepad.ControllerType == SONY_DUALSENSE) { // https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/
27+
28+
unsigned char PlayersDSPacket = 0;
29+
30+
if (OutState.PlayersCount == 0) PlayersDSPacket = 0;
31+
else if (OutState.PlayersCount == 1) PlayersDSPacket = 4;
32+
else if (OutState.PlayersCount == 2) PlayersDSPacket = 2; // Center 2
33+
else if (OutState.PlayersCount == 5) PlayersDSPacket = 1; // Both 2
34+
else if (OutState.PlayersCount == 3) PlayersDSPacket = 5;
35+
else if (OutState.PlayersCount == 4) PlayersDSPacket = 3;
36+
37+
if (CurGamepad.USBConnection) {
38+
unsigned char outputReport[48];
39+
memset(outputReport, 0, 48);
40+
41+
outputReport[0] = 0x02;
42+
outputReport[1] = 0xff;
43+
outputReport[2] = 0x15;
44+
outputReport[3] = OutState.LargeMotor;
45+
outputReport[4] = OutState.SmallMotor;
46+
outputReport[5] = 0xff;
47+
outputReport[6] = 0xff;
48+
outputReport[7] = 0xff;
49+
outputReport[8] = 0x0c;
50+
outputReport[38] = 0x07;
51+
outputReport[44] = PlayersDSPacket;
52+
outputReport[45] = std::clamp(OutState.LEDRed - OutState.LEDBrightness, 0, 255);
53+
outputReport[46] = std::clamp(OutState.LEDGreen - OutState.LEDBrightness, 0, 255);
54+
outputReport[47] = std::clamp(OutState.LEDBlue - OutState.LEDBrightness, 0, 255);
55+
56+
hid_write(CurGamepad.HidHandle, outputReport, 48);
57+
58+
} else { // BT
59+
unsigned char outputReport[79];
60+
memset(outputReport, 0, 79);
61+
62+
outputReport[0] = 0xa2;
63+
outputReport[1] = 0x31;
64+
outputReport[2] = 0x02;
65+
outputReport[3] = 0x03;
66+
outputReport[4] = 0x54;
67+
outputReport[5] = OutState.LargeMotor;
68+
outputReport[6] = OutState.SmallMotor;
69+
outputReport[11] = 0x00;
70+
outputReport[41] = 0x02;
71+
outputReport[44] = 0x02;
72+
outputReport[45] = 0x02;
73+
outputReport[46] = PlayersDSPacket;
74+
outputReport[46] &= ~(1 << 7);
75+
outputReport[46] &= ~(1 << 8);
76+
outputReport[47] = std::clamp(OutState.LEDRed - OutState.LEDBrightness, 0, 255);
77+
outputReport[48] = std::clamp(OutState.LEDGreen - OutState.LEDBrightness, 0, 255);
78+
outputReport[49] = std::clamp(OutState.LEDBlue - OutState.LEDBrightness, 0, 255);
79+
uint32_t crc = crc_32(outputReport, 75);
80+
memcpy(&outputReport[75], &crc, 4);
81+
82+
hid_write(CurGamepad.HidHandle, &outputReport[1], 78);
83+
}
84+
85+
}
86+
else if (CurGamepad.ControllerType == SONY_DUALSHOCK4 && CurGamepad.USBConnection) { // JoyShockLibrary rumble working for USB DS4 ???
4587
unsigned char outputReport[31];
4688
memset(outputReport, 0, 31);
4789

@@ -60,25 +102,35 @@ void GamepadSetState(InputOutState OutState)
60102
JslSetLightColour(0, (std::clamp(OutState.LEDRed - OutState.LEDBrightness, 0, 255) << 16) + (std::clamp(OutState.LEDGreen - OutState.LEDBrightness, 0, 255) << 8) + std::clamp(OutState.LEDBlue - OutState.LEDBrightness, 0, 255)); // https://github.com/CyberPlaton/_Nautilus_/blob/master/Engine/PSGamepad.cpp
61103
JslSetRumble(0, OutState.SmallMotor, OutState.LargeMotor); // Not working with DualSense USB connection
62104
}
63-
}
105+
} else // Unknown controllers - Pro controller, Joy-cons
106+
JslSetRumble(0, OutState.SmallMotor, OutState.LargeMotor);
64107
}
65108

66109
void GamepadSearch() {
67110
struct hid_device_info *cur_dev;
68111
cur_dev = hid_enumerate(SONY_VENDOR, 0x0);
69112
while (cur_dev) {
70-
if (cur_dev->product_id == SONY_DS5_USB || cur_dev->product_id == SONY_DS4_USB || cur_dev->product_id == SONY_DS4_V2_USB)
113+
if (cur_dev->product_id == SONY_DS5 || cur_dev->product_id == SONY_DS4_USB || cur_dev->product_id == SONY_DS4_V2_USB)
71114
{
72115
CurGamepad.HidHandle = hid_open(cur_dev->vendor_id, cur_dev->product_id, cur_dev->serial_number);
73116
hid_set_nonblocking(CurGamepad.HidHandle, 1);
74117

75-
if (cur_dev->product_id == SONY_DS5_USB)
118+
if (cur_dev->product_id == SONY_DS5) {
76119
CurGamepad.ControllerType = SONY_DUALSENSE;
120+
CurGamepad.USBConnection = true;
121+
122+
// BT detection https://github.com/JibbSmart/JoyShockLibrary/blob/master/JoyShockLibrary/JoyShock.cpp
123+
unsigned char buf[64];
124+
memset(buf, 0, 64);
125+
hid_read_timeout(CurGamepad.HidHandle, buf, 64, 100);
126+
if (buf[0] == 0x31)
127+
CurGamepad.USBConnection = false;
128+
}
77129
else if (cur_dev->product_id == SONY_DS4_USB || cur_dev->product_id == SONY_DS4_V2_USB)
130+
{
78131
CurGamepad.ControllerType = SONY_DUALSHOCK4;
79-
80-
CurGamepad.USBConnection = true;
81-
//printf("DS found\n");
132+
CurGamepad.USBConnection = true;
133+
}
82134
break;
83135
}
84136
cur_dev = cur_dev->next;
@@ -87,6 +139,30 @@ void GamepadSearch() {
87139
hid_free_enumeration(cur_dev);
88140
}
89141

142+
void GetBatteryInfo() {
143+
if (CurGamepad.HidHandle != NULL) {
144+
if (CurGamepad.ControllerType == SONY_DUALSENSE) {
145+
if (CurGamepad.USBConnection) {
146+
unsigned char buf[64];
147+
memset(buf, 0, 64);
148+
hid_read(CurGamepad.HidHandle, buf, 64);
149+
CurGamepad.BatteryLevel = (buf[53] & 0x0f) / 2;
150+
//CurGamepad.BatteryMode = buf[30]; //???
151+
} else { // BT
152+
unsigned char buf[64];
153+
memset(buf, 0, 64);
154+
hid_read(CurGamepad.HidHandle, buf, 64);
155+
CurGamepad.BatteryLevel = (buf[54] & 0x0f) / 2;
156+
//CurGamepad.BatteryMode = buf[31]; //???
157+
}
158+
if (CurGamepad.BatteryLevel > 4)
159+
CurGamepad.BatteryLevel = 4;
160+
//printf("%d ", CurGamepad.BatteryLevel);
161+
//printf("%d\n", CurGamepad.BatteryMode);
162+
}
163+
}
164+
}
165+
90166
static std::mutex m;
91167

92168
struct EulerAngles {
@@ -207,7 +283,7 @@ SHORT ToLeftStick(double Value, float WheelAngle)
207283

208284
int main(int argc, char **argv)
209285
{
210-
SetConsoleTitle("DSAdvance 0.2");
286+
SetConsoleTitle("DSAdvance 0.3");
211287
// Config parameters
212288
CIniReader IniFile("Config.ini");
213289
int KEY_ID_EXIT = IniFile.ReadInteger("Main", "ExitBtn", 192); // "~" by default for RU, US and not for UK
@@ -233,11 +309,13 @@ int main(int argc, char **argv)
233309
float MotionSensY = IniFile.ReadFloat("Motion", "SensY", 3);
234310

235311
GamepadSearch();
312+
GamepadOutState.PlayersCount = 0;
236313
GamepadOutState.LEDBlue = 255;
237314
GamepadOutState.LEDBrightness = std::clamp((int)(255 - IniFile.ReadInteger("Gamepad", "DefaultBrightness", 100) * 2.55), 0, 255);
238315
GamepadSetState(GamepadOutState);
239316

240317
int SkipPollCount = 0;
318+
int BatteryShowCounter = 0;
241319
bool DeadZoneMode = false;
242320
int GamepadMode = 0; int LastAIMProCtrlMode = 2;
243321
EulerAngles AnglesOffset;
@@ -248,7 +326,7 @@ int main(int argc, char **argv)
248326

249327
system("cls");
250328
if (controllersCount == 0)
251-
printf("\n Connect DualSense, DualShock 4, Pro controller via USB and restart the app.");
329+
printf("\n Connect DualSense, DualShock 4, Pro controller and restart the app.");
252330
printf("\n Press \"ALT\" + \"Escape\" or \"exit key\" to exit.\n");
253331

254332
JOY_SHOCK_STATE InputState;
@@ -299,34 +377,34 @@ int main(int argc, char **argv)
299377

300378
TouchState = JslGetTouchState(deviceID[0]);
301379

302-
if (TouchState.t0Down && TouchState.t0Y <= 0.1 && TouchState.t0X >= 0.325 && TouchState.t0X <= 0.675) {
380+
if (TouchState.t0Down && TouchState.t0Y <= 0.1 && TouchState.t0X >= 0.325 && TouchState.t0X <= 0.675) { // Brightness change
303381
GamepadOutState.LEDBrightness = std::clamp((int)((TouchState.t0X - 0.325) * 255 * 2.9), 0, 255);
304382
//printf("%5.2f\n", (TouchState.t0X - 0.325) * 255 * 2.9);
305383
GamepadSetState(GamepadOutState);
306384
}
307385

308386
if (InputState.buttons & JSMASK_TOUCHPAD_CLICK) {
309387
if (TouchState.t0Y > 0.1) {
310-
if (TouchState.t0X > 0 && TouchState.t0X <= 1 / 3.0 && GamepadMode != 4) { // [O--]
388+
if (TouchState.t0X > 0 && TouchState.t0X <= 1 / 3.0 && GamepadMode != 4) { // [O--] - Driving mode
311389
GamepadMode = 1;
312390
AnglesOffset = MotionAngles;
313391
GamepadOutState.LEDBlue = 0; GamepadOutState.LEDRed = 255; GamepadOutState.LEDGreen = 0;
314392
}
315-
else if (TouchState.t0X > 1 / 3.0 && TouchState.t0X <= 1 / 3.0 * 2.0) { // [-O-]
316-
317-
if (TouchState.t0Y > 0.1 && TouchState.t0Y < 0.5) {
393+
else if (TouchState.t0X > 1 / 3.0 && TouchState.t0X <= 1 / 3.0 * 2.0) { // [-O-] // Default & touch sticks modes
394+
395+
if (TouchState.t0Y > 0.1 && TouchState.t0Y < 0.7) { // Default mode
318396
GamepadMode = 0;
319397
GamepadOutState.LEDBlue = 255; GamepadOutState.LEDRed = 0; GamepadOutState.LEDGreen = 0;
320-
// Show battery level - JslSetPlayerNumber(deviceID[0], 5);
321-
}
322-
else {
398+
// Show battery level
399+
GetBatteryInfo(); BatteryShowCounter = 35; GamepadOutState.PlayersCount = CurGamepad.BatteryLevel; GamepadSetState(GamepadOutState); // JslSetPlayerNumber(deviceID[0], 5);
400+
} else { // Touch sticks mode
323401
GamepadMode = 4;
324402
JslSetRumble(0, 255, 255);
325403
GamepadOutState.LEDBlue = 255; GamepadOutState.LEDRed = 255; GamepadOutState.LEDGreen = 0;
326404
}
327405

328406
}
329-
else if (TouchState.t0X > (1 / 3.0) * 2.0 && TouchState.t0X <= 1 && GamepadMode != 4) { // [--O]
407+
else if (TouchState.t0X > (1 / 3.0) * 2.0 && TouchState.t0X <= 1 && GamepadMode != 4) { // [--O] Aiming mode
330408

331409
if (TouchState.t0Y > 0.1 && TouchState.t0Y < 0.5) { // Motion AIM always
332410
GamepadMode = 3;
@@ -458,6 +536,7 @@ int main(int argc, char **argv)
458536

459537
ret = vigem_target_x360_update(client, x360, report);
460538

539+
if (BatteryShowCounter > 0) { if (BatteryShowCounter == 1) { GamepadOutState.PlayersCount = 0; GamepadSetState(GamepadOutState); } BatteryShowCounter--; }
461540
if (SkipPollCount > 0) SkipPollCount--;
462541
Sleep(SleepTimeOut);
463542
}

0 commit comments

Comments
 (0)