ApproxEng / approxeng.input

Python classes to handle game controllers, including PS3, PS4, XBox One and Rock Candy
https://approxeng.github.io/approxeng.input
Apache License 2.0
55 stars 17 forks source link

PS4 DualShock4 incorrect mapping #26

Closed exinmusic closed 6 years ago

exinmusic commented 6 years ago

DualShock4 PS4 (product_id 2508) controller class appears to be mapping a number of the buttons incorrectly and returning NoneType when checking battery. I've binded to the controller explicitly and using the ControllerResource. Controller connects fine either way. But when queried for buttons pressed, some don't work and the ones that do are incorrectly named. I assume this is due to a software update on the DualShock4. Remapping looks easy enough, but I wanted to see if this was a known bug.

cnobile2012 commented 6 years ago

I have also seen this exact issue. This is the dump I get from lsusb on Linux: Bus 001 Device 020: ID 054c:09cc Sony Corp. 09cc == 2508 so it looks like the same product_id as @exinmusic

I just tested a much older PS4 that I have and it has the same issues, but very different product_id. Bus 001 Device 021: ID 054c:05c4 Sony Corp. DualShock 4 05c4 == 1476

tomoinn commented 6 years ago

It's less likely to be the DS4 than the kernel version - later (4.15+) kernels have a much more sorted out approach to controllers with lots of axes (more than the original evdev devices were intended to handle). At the same time a bunch of buttons got remapped for some reason. If you're on a pre-4.15 kernel that's probably the issue, but let me know if you're on a modern one and still have this problem?

cnobile2012 commented 6 years ago

Are you talking about the Linux kernel? If so mine is 4.4.0 on Ubuntu. I don't see how a Linux kernel will make any difference at all in this situation.

tomoinn commented 6 years ago

Yup, and it does, whether you understand it or not! There's been an ongoing debate as to how to represent these very complex devices and the policies have changed over those kernel versions. This manifests in different evdev event codes for the same physical control across different linux kernel versions. Since 4.15 this has been resolved, but older kernels such as the one you're using will have different mappings. In fact, until (IIRC) 4.13 the two different DS4 versions had different evdev codes, just to make life even more annoying.

tomoinn commented 6 years ago

This is also why you're not seeing battery status, the nodes in /sys/class/... aren't created for these controllers in older kernels so the library can't read them. Same for LED support.

cnobile2012 commented 6 years ago

Not everybody will be able to upgrade their version of Linux. I would expect pygame to have the same issues, but it doesn't. So what is the fix for this?

tomoinn commented 6 years ago

For this library, there isn't one. I decided some time ago that I couldn't reasonably support multiple kernel versions (I assume pygame attempts to read the evdev metadata, but for some of the supported controllers this metadata is just plain wrong so I don't use it). It'd be nice if the metadata was all correctly reported because we could drop the driver classes (apart from power and LED management) but it generally just isn't. Even when it is it breaks the 'same control is in the same place' for muscle memory that this library has.

The 'fix' would be to pick up kernel versions and handle each variant explicitly (which is tedious and I don't need so isn't going to be something I do), or to rely on the metadata (which is unreliable and breaks some of the principles that make this usable). For a specific use case you can always create a new driver class, there's a description of how to do that in the docs and the code should be fairly self explanatory, but really for the DS4 there's so much lacking in earlier kernel versions that the correct answer really is to upgrade there. Even if you fix the button mappings, you won't get the extra features of the controller such as the accelerometer, trackpad, LED bar etc as those require a more modern kernel.

tomoinn commented 6 years ago

Something that might be worth having as a fallback (for this, and for unsupported but cooperative controllers) would be a driver class that used evdev metadata to create controls. It won't be possible to make this as good as a hand-coded driver because of the various issues with evdev metadata, and it won't handle modern compound devices (where a single physical controller creates multiple device nodes in /dev/input - the DS4 actually creates three devices!) but it might be a reasonable fallback for older systems.

cnobile2012 commented 6 years ago

OK, so I have updated my version of Ubuntu which now uses kernel 4.15. I'm doing testing on my laptop, but the Raspberry PI is where my code will eventually run. The latest kernel on the RPI is 4.14 as of this writing.

On my laptop, I am still not getting any 'ps4_pad', 'pitch', or 'roll' responses at all. From your comments above these should be working on kernel 4.15. Everything else seems to be working correctly.

tomoinn commented 6 years ago

That's probably a permissions issue. The evdev subsystem will restrict anything it thinks is a keyboard or mouse to root or members of the appropriate group. As the trackpad and motion sensors don't have buttons they're classed as non-gamepad devices and hidden. On my system here there's an 'input' group, if your user is a member of that group you should be able to see the additional devices and the library will make use of them, otherwise it can't. A good check is to see whether running evtest as root shows the device nodes, they've got suitably obvious names.

I've not tested on the latest release kernel for the pi, I build my own kernels so it's never an issue, but I believe it should work. People seem to have used it with no issues anyway.

cnobile2012 commented 6 years ago

So, it looks like all my issues are solved. I don't know about the person @exinmusic who originally created this ticket. And yes it even works on the Raspberry Pi if you have the latest kernel. Mine currently has version 4.14.62.

Tom, I would recommend adding to the docs the process of adding the input group to a user account. This seems to be done for you already on the RPI if you use the pi account, but will certainly not be done if you are using a laptop.

Since this works on the RPI and that's my target platform my problem is solved, but running on older versions of Linux may still be an issue for some people.

Anyway, thanks for you help. Carl

exinmusic commented 6 years ago

Hey, @cnobile2012 glad to see this get resolved on your end! I'll have trying updating my raspberry pi's kernel version and report back.

tomoinn commented 6 years ago

My main target for this library (and others, but this is the one where the kernel version seems to actually matter) is the latest release kernel on the Pi, so I'm glad this works. I tend to build my own pi kernels anyway, mostly out of habit these days, but I do a lot of support for the PiWars community so need to make it work for that lot! As you say, the 'pi' user on raspbian defaults to being a member of the input group.

I've added a new issue #27 to the tracker to remind myself to update the docs for the DS4 to mention all this stuff, it was all 'known' but didn't make it into the docs last time around :)

Glad it's working for you - what are you using it for, out of curiosity?

cnobile2012 commented 6 years ago

I have rewritten the motor access library for the PiBorg robot. You can see links to the motor controller in my GitHub account: https://github.com/cnobile2012/python-thunderborg. My lib is functionally equivalent to the original, but it is a lot more Pythonic and follows PEP8 pretty closely.

Yeah, I noticed that you had added that ticket for docs, good idea. That's the reason I mentioned the addition of the input stuff. Your library seems to work very well and as people find out about it they will most certainly want to use it for things besides robots.

cnobile2012 commented 6 years ago

Found another issue that is related to this one so I'll continue it here. Bus 001 Device 020: ID 054c:09cc Sony Corp. On the above controller everything seems to work fine. The yaw readings I find a bit strange, but you call it a yaw_rate which seems to be more related to speed than position.

However, on this version of an older PS4 controller, Bus 001 Device 021: ID 054c:05c4 Sony Corp. DualShock 4, The pad, pitch, roll, and yaw do not seem to work at all.

Tom, do you have a mapping of what you're expecting in the /sys FS? I'd like to track down what the issue is and if it is showing up in a different place in /sys than expected.

tomoinn commented 6 years ago

It's not looking for stuff in /sys, it uses evdev to find devices (which correspond to nodes in /dev/input). Can you do evtest and copy the initial output where it lists devices here? With my version of the hardware I see nodes that are very obviously the DS4's extra sensors, if we're lucky it's just giving them a different name but in the past the two different hardware versions haven't been treated as the same device type by the kernel drivers so it may just not be possible.

cnobile2012 commented 6 years ago

I get the same exact thing with both of my PS4 controllers.

$ evtest
No device specified, trying to scan all of /dev/input/event*
Not running as root, no devices may be available.
Available devices:
/dev/input/event0:      Lid Switch
/dev/input/event1:      Sleep Button
/dev/input/event2:      Power Button
/dev/input/event3:      AT Translated Set 2 keyboard
/dev/input/event4:      Video Bus
/dev/input/event5:      SynPS/2 Synaptics TouchPad
/dev/input/event6:      Logitech M510
/dev/input/event7:      TPPS/2 IBM TrackPoint
/dev/input/event8:      ThinkPad Extra Buttons
/dev/input/event9:      HDA Intel PCH Dock Mic
/dev/input/event10:     HDA Intel PCH Mic
/dev/input/event11:     HDA Intel PCH Dock Headphone
/dev/input/event12:     HDA Intel PCH Headphone
/dev/input/event13:     HDA Intel PCH HDMI/DP,pcm=3
/dev/input/event14:     HDA Intel PCH HDMI/DP,pcm=7
/dev/input/event15:     HDA Intel PCH HDMI/DP,pcm=8
/dev/input/event16:     HDA Intel PCH HDMI/DP,pcm=9
/dev/input/event17:     HDA Intel PCH HDMI/DP,pcm=10
/dev/input/event18:     Integrated Camera: Integrated C
/dev/input/event19:     Sony Computer Entertainment Wireless Controller Touchpad
/dev/input/event20:     Sony Computer Entertainment Wireless Controller Motion Sensors
/dev/input/event21:     Sony Computer Entertainment Wireless Controller
Select the device event number [0-21]:

I chose 20 (Motion Sensors) and got a never ending list of this:

Event: time 1534773124.212337, -------------- SYN_REPORT ------------
Event: time 1534773124.216125, type 4 (EV_MSC), code 5 (MSC_TIMESTAMP), value 148074753
Event: time 1534773124.216125, type 3 (EV_ABS), code 3 (ABS_RX), value -5738
Event: time 1534773124.216125, type 3 (EV_ABS), code 4 (ABS_RY), value -2430
Event: time 1534773124.216125, type 3 (EV_ABS), code 5 (ABS_RZ), value -3266
Event: time 1534773124.216125, type 3 (EV_ABS), code 0 (ABS_X), value 846
Event: time 1534773124.216125, type 3 (EV_ABS), code 1 (ABS_Y), value 8052
Event: time 1534773124.216125, type 3 (EV_ABS), code 2 (ABS_Z), value -2209

MOF--both of my controllers show the exact same thing, though the number are different. I looked at the other two device events but they also look very similar between the two controllers.

tomoinn commented 6 years ago

There's not much more I can do from here. If you figure it out and submit a pull request I'll merge it if it doesn't negatively impact on anything else, but without actually having the controller in front of me this kind of thing is almost impossible to diagnose. The scanning logic is in https://github.com/ApproxEng/approxeng.input/blob/master/src/python/approxeng/input/controllers.py and that file contains a bunch of functions that attempt to associate different device nodes, you could try seeing whether there's anything odd there (the DS4 should associate with three device nodes, if it does and still doesn't produce output then that's one issue, if it doesn't then it's another)

exinmusic commented 6 years ago

For my purposes updating the kernel on my raspberry pi did the trick. @tomoinn thank you! Really useful library! rpi-update and a reboot is probably all you need to do if you run into this problem!