Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DualSense controller - base support #648

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

psstoyanov
Copy link

@psstoyanov psstoyanov commented Aug 15, 2021

Add support for Sony's new DualSense controller - see #624

Current status:

  • Dpad
  • Sticks
  • Analog triggers
  • Buttons
  • Gyro
  • Touchpad:
    • USB mode
    • BT mode
  • Separate GUI for DS5
  • Touchpad Gyro modifier

ToDo:

  • Rumble support

Unknown:

  • LED strip?
  • Adaptive triggers?
  • Audio for headphone/ mic while emulation is on?

I've made it as simple as possible while using a separate file. There are no changes to the original DS4 configuration.
This is very basic - the UI or logging shouldn't reflect that the user is using DualSense controller instead of DS4. There could be quite a few places to improve this but it works as a starting point.

The byte_offset changes were made by hand - there was no special method to derive them but trial and error.

@psstoyanov
Copy link
Author

Added Gyro support.

It works quite well from my initial testing.
I had to invert Y axis to get vertical mouse camera movement to match.

Touchpad is next....

@XDM-Inc
Copy link

XDM-Inc commented Feb 6, 2022

any update on touchpad and vibration?
also i noticed that when you try to make gyro activate on right or left touchpad it activates when you press the d pad (for left) and right thumbpad (for right)

@psstoyanov
Copy link
Author

psstoyanov commented Feb 6, 2022

I haven't had time to check more about rumble.

About left/right pad - I haven't used that option so far 🤔 I'm not even sure what they are connected to but that could be the first thing to take a look into (given that there is concrete place to start checking).

PS: I would bet that on the Steam Controller the touchpad in question is the left one - it defaults to DPad.

@psstoyanov
Copy link
Author

psstoyanov commented Feb 6, 2022

@XDM-Inc , here you have simple addition for Touch/ Press of the touchpad.

If that is to be extended, it would be great to know how to add the action modifier when a touchpad is present instead of cluttering the UI or where it is interpreted (in case we want to have separation for left/right side of the touchpad on the DualSense or DualShock 4 controller).

@XDM-Inc
Copy link

XDM-Inc commented Feb 7, 2022

@XDM-Inc , here you have simple addition for Touch/ Press of the touchpad.

If that is to be extended, it would be great to know how to add the action modifier when a touchpad is present instead of cluttering the UI or where it is interpreted (in case we want to have separation for left/right side of the touchpad on the DualSense or DualShock 4 controller).

Thanks! works great! @psstoyanov

@salar-shdk
Copy link

why this pull request haven't merged yet??

@psstoyanov Thank you, good job!
It would be great if you add bluetooth support for dualsense.

@kozec
Copy link
Owner

kozec commented May 9, 2022

I was under impression it's not ready yet, but if @psstoyanov is okay with it, I can merge it without Rumble support. I'm not really able to test it myself but it seems like there's at least one other person confirming that code works.

@Ryochan7
Copy link
Contributor

Ryochan7 commented May 9, 2022

One thing that I can notice off hand is that the HID decoder would only work for a USB connected DualSense. A BT connected DualSense has a padded byte at byte 1 so the LX value is located at byte 2 in the input report; can't remember what is normally there for me but DS4Windows never used it. An extra offset would be required to have the decoder work for both a USB and BT connected DualSense.

@theY4Kman
Copy link

I would really like to get the haptics working. I might take a peek inside here.

I broke my fifth and last Steam controller backplate (plastics for tension-heavy machinery doesn't jive with using back paddles in Rocket League), and refuse to spend $50 on a parts-only SC off eBay. I finally bit the bullet and got a DS5 w/ a back paddle mod — and now, the only thing that's missing is feeling a full trigger push. Right now, it's always a mystery as to when it's going to fire. And no matter how I configure it (full press @ 254 or 255), it fires waaaay before I'm flooring the the trigger

@theY4Kman
Copy link

Woo! I got somewhere! Sending an outgoing interrupt with data 02 03 00 <right_intensity> <left_intensity> seems to do the trick. Here's my farting around code in the DS5Controller class:

    def _feedback(self, left_intensity, right_intensity):
        data = b'\x02\x03\x00' + bytes(right_intensity) + bytes(left_intensity)
        zeros = b'\x00' * (64 - len(data))
        self.handle.interruptWrite(3, data + zeros)

I haven't played around with all possible values, and I've only tested over USB, but controller._feedback(255, 255) gives strong vibration for 5 seconds.

@theY4Kman
Copy link

theY4Kman commented May 12, 2022

Ah, this is gonna help a lot: https://github.com/nowrep/dualsensectl/blob/main/main.c


And this appears to implement adaptive triggers: https://github.com/Ohjurot/DualSense-Windows/blob/main/VS19_Solution/DualSenseWindows/src/DualSenseWindows/DS5_Output.cpp

@psstoyanov
Copy link
Author

psstoyanov commented May 12, 2022

@salar-shdk , I didn't push for the PR to be merged. There didn't seem to be enough feedback and I don't exactly have the expertise to proactively catch the "hidden gremlins" 🙂

For BT on my systems, it is definitely spotty (I tend to use wired connection anyway) although it should be relatively functional with python2-evdev installed on my Arch based systems (mainly Manjaro). Depending on your distro, support for this package can be in various states of compatibility. Python2 is supposed to be deprecated since 1st of Jan 2020 but as you can see, it's still used in the wild.

@Ryochan7 , this is very interesting. Can you share some more info on that offset in BT mode? How would it look in the current setup?
You can see I didn't touch the hiddrv.c and instead attempted to limit any changes to the project to a small subset that won't affect anything else.

@theY4Kman , awesome finds!

I don't know how much time I can dedicate to this PR at the moment (some work stuff keep popping up...)

@kozec , if you are happy with the current state and feedback from other users, feel free to merge it.

Note:
@kozec , do you need a DualSense to test? It's more than possible to figure out a way to get you one.

@theY4Kman
Copy link

theY4Kman commented Jun 18, 2022

FWIW, I threw together a little GUI for screwing with the controller — things like haptics, trigger effects, lightbar, player LEDs, mute button, etc — while I was figuring out where everything went. Now that it does the things I care about (honestly, just rumble and trigger effects), it might be easier for me to add them into scc

https://pypi.org/project/ds5ctl/

Pics

Example of GUI

Example recording after sending to controller


Edit: right, I always forget how crazy it is up in there :P Well, at least I can set my adaptive trigger settings to match my full press position in ds5ctl, exit it, spin up sc-controller, and play the game.

@theY4Kman
Copy link

theY4Kman commented Jun 19, 2022

Alright, I got some basic feedback support in my branch: psstoyanov/sc-controller@dualsense...theY4Kman:yak/dualsense (excuse the large diff, 'cause I "fixed" the formatting to my liking)

I threw in the DualSenseHIDOutput(ctypes.Structure) from ds5ctl for ease of implementation and future expansion. I can't figure out how to send haptics one side at a time to the controller, so I'm storing the current haptic state in the DualSenseHIDOutput stored in self._output, and emitting it to the controller during flush().

Unlike the Steam controller, which apparently supports sending haptic duration and letting the controller handle cessation at the right time, the DS5 doesn't support this (or I can't figure out how). So, instead, I schedule a callback to clear both motors in self._output and emit to the controller after the calculated duration. I calculate the duration based on the HapticData period * duration / 0x10000 (65536) — assuming the max value sent to the Steam controller represents 1 second. I don't know if that assumption is true.

Also, if the duration is too short, the motors (I'm guessing) don't have enough time to spin up, so nothing is felt; this effect is more pronounced on the heavier left motor. To address this, I've clamped the duration at a minimum of 20ms, which seems to work reasonably well — I have, on occasions, noticed the left motor fail to create an effect with this, though.

The DS5 apparently has two different motors on each side, with the left motor being much heavier. As such, I tried to compensate for this by scaling the left motor to half the amplitude an equally-sized input would have on the right motor. It seems to perform better than if treating them both the same, but it still doesn't feel quite "right".

I haven't tested this on BT, since I can't get that to work locally =/


Some other notes:

  • I observed some weirdness trying to test this: on the left stick, feedback was only sent when the stick first moved, and when it returned to home base; on the right stick, any movement sent feedback. I'm not sure why this is.
  • It would be nice if I could tell the Mapper "yes, this controller does support sending haptics to both sides at the same time", instead of it assuming all things act like the Steam controller and splitting each side into its own call
  • With the DualSenseHIDOutput, setting the lightbar colour is possible. It would be cool if the configured colour in the SC Controller GUI could be set as the lightbar colour, too.
  • I'd love to add adaptive trigger support, as well. Perhaps I could set them to match the "full press" distance configured for each trigger? I'm still learning how to control these effectively. The "Effect Extended" effect type seems to be the way to go, at least for my preferences, as it allows for something resembling the "click" of the Steam controller — it can apply pressure up until a point, then release it, acting sort of like a tactile button.

@theY4Kman
Copy link

Ayyy, I got support for changing the lightbar colour and brightness using the controller settings dialog. The colour is based solely on the controller icon selected, and the brightness slider basically modulates the RGB values sent to the controller (where sending 000000 kills the light entirely). All the places I've pulled the DS5 HID output structure from have mentioned led_brightness, but I have never been able to get it to work, so I'm not sure its function.

Also, I tried my hand at creating some controller icons for the DS5, based on the main configurator screen controller image and some handjammin :P

ds5-0 ds5-1 ds5-2 ds5-3 ds5-4 ds5-5 ds5-6

Could be worse ;)

Neat to see it in action, though (excuse the shit webcam exposure; it all looks right in real life)

ds5-lightbar-control.mp4

@theY4Kman
Copy link

mm, I'm learning that symmetric haptics, as opposed to the normal rumble effects, are actually performed by emitting to the rear channels of the controller's audio output device. Running speaker-test on the controller exhibits the effect I'd expect to feel for feedback.

I'm not sure what to do with that info, as the controller's audio device disappears in my system when running sc-controller.

@Ryochan7
Copy link
Contributor

I can confirm that the changes from theY4Kman work with minimal changes in the Python 3 fork.

Also,

I haven't tested this on BT, since I can't get that to work locally =/

When using BT with the DualSense, you have to compile the CRC-32 hash for the output report data and send that as the last 4 bytes in the output report. Otherwise, the controller will not accept the report.

@theY4Kman
Copy link

theY4Kman commented Jun 21, 2022

Oh, that's lovely, 'cause I was using your fork before all this, and developing in 2.7 is a pain :P

compile the CRC-32 hash for the output report data

Which bytes would that mean? 0-60? Is it with the nice, standard 0x04C11DB7 polynomial?

@theY4Kman
Copy link

Ooo, and I just discovered Etaash-mathamsetty/trigger-control, with a more complete set of trigger effects. Though, I still don't know how to get things just right, even messing with it manually.

I'd had a go at automatically applying trigger effects based on half/full press actions, and it was even more of a disaster. I wonder if I ought to instead just add a configuration screen for the effects. Though, I could easily imagine how an Action altering the trigger effects could be really useful — especially if the trigger haptic effects could be pulsed on command.

@Ryochan7
Copy link
Contributor

Ryochan7 commented Jun 21, 2022

DS4Windows and my dual_pod_shock fork use 0xedb88320 as the default polynomial for CRC-32. Also, the DualSense HID output report buffer size is 78 bytes. Bytes 74-77 will be filled with the generated CRC-32 hash made from bytes 0-73.

https://gitlab.com/ryochan7/dual-pod-shock/-/tree/ryochan-changes
https://github.com/Ryochan7/DS4Windows/blob/jay/DS4Windows/DS4Library/InputDevices/DualSenseDevice.cs#L1044

One other thing, the L2 and R2 digital button output has nothing to do with the adaptive triggers. Those buttons get fired at half-pull IIRC. The DS4 outputs digital button states for the triggers along with the analog axis value just like the DualSense; those digital buttons are stored as L2Btn and R2Btn in the DS4State objects used in DS4Windows. Those fake digital buttons are fired for the DirectInput interface in Windows as well.

https://github.com/theY4Kman/sc-controller/blob/1cd282ff7b76f842ec148243b85e18d67fde2dba/scc/drivers/ds5drv.py#L239

Edit: It looks like the digital buttons get fired almost immediately once the triggers are pulled any distance. Same behavior with both the DS4 and DualSense.

@Ryochan7
Copy link
Contributor

Ryochan7 commented Jun 22, 2022

I cannot get ds5ctl to open the DualSense when connected via Bluetooth. The underlying hidapi library has a problem finding it. Maybe it can only read HID devices connected using the USB bus? Also, I forgot that the Bluetooth connections in SC Controller would open the DS4 and DualSense via evdev rather than the HID device. Really would have to utilize the hidraw system to send full output packets to a Bluetooth connected DS4 or DualSense.

Although, some games like Streets of Rage 4 looks like it will invoke something like a fake disconnect if the underlying sysdev device is still exposed and get rid of an existing hidraw connection. Ended up working around it by doing a chmod on the main sysdev device path but maybe libusb could be used to claim that device instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants