Skip to content

Commit 6652808

Browse files
committed
add a simple raw controller mapper using python-evdev
1 parent e74fa44 commit 6652808

File tree

2 files changed

+215
-0
lines changed

2 files changed

+215
-0
lines changed

fixes/raw_ps4/README

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
Developed for but can work with others, it should but there is no garantee
2+
Playstation DualShock4 controller hw_version=0x0000b418 fw_version=0x0000a00b
3+
4+
I could not get Dpad to work with this API but the Sticks works.
5+
I use the Dpad only around edges but can play without it.
6+
This is just a way to take the kernel RAW when everything else is not working
7+
You might just need to replace this line.
8+
device = evdev.InputDevice('/dev/input/event21') # Replace X with the correct number
9+
10+
The quick way:
11+
sudo modprobe uinput
12+
sudo chown root:input /dev/uinput
13+
sudo chmod 0660 /dev/uinput
14+
15+
Fixing /dev/uinput Permissions
16+
17+
To make it persist across reboots, add it to /etc/modules-load.d/uinput.conf:
18+
19+
echo "uinput" | sudo tee /etc/modules-load.d/uinput.conf
20+
21+
2 Give Your User Access to /dev/uinput
22+
23+
By default, only root and the input group can access it.
24+
25+
Add yourself to the input group:
26+
27+
sudo usermod -aG input $USER
28+
29+
Log out and back in (or reboot) for the group change to take effect.
30+
31+
If that still doesn't work, you might need udev rules.
32+
3 Create a udev Rule to Fix Permissions
33+
34+
If /dev/uinput still has crw------- (only root access), create a new rule:
35+
36+
sudo nano /etc/udev/rules.d/99-uinput.rules
37+
38+
Add this line:
39+
40+
KERNEL=="uinput", GROUP="input", MODE="0660", OPTIONS+="static_node=uinput"
41+
42+
Then apply the new rule:
43+
44+
sudo udevadm control --reload-rules && sudo udevadm trigger
45+
46+
Now, check if the permissions are fixed:
47+
48+
ls -l /dev/uinput
49+
50+
It should look like:
51+
52+
crw-rw---- 1 root input 10, 223 Feb 1 4:20 /dev/uinput
53+
54+
If it still says crw-------, reboot.
55+
56+
Now your Python script should be able to read/write to /dev/uinput without sudo! 🚀

fixes/raw_ps4/controller.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import evdev
2+
from evdev import UInput, ecodes as e
3+
4+
# Open your PS4 controller device
5+
device = evdev.InputDevice('/dev/input/by-id/usb-Sony_Interactive_Entertainment_Wireless_Controller-if03-event-joystick') # Replace
6+
7+
# Create a virtual keyboard
8+
ui = UInput()
9+
10+
print(f"Listening for input from {device.name}...")
11+
12+
for event in device.read_loop():
13+
if event.type == e.EV_ABS: # Detects D-pad, Sticks and Triggers
14+
"""
15+
Sticks and Triggers
16+
ABS_X and ABS_Y 0 means left stick is most to the left or most down
17+
227-228 is the middle 255 is most to the right or most up.
18+
dead zone should be something like 64 to 191.
19+
20+
We then have the trigger ABS_Z L2 0 means not pushed down
21+
and 255 means most pushed down.
22+
23+
We then have the same on the right, stick and R2.
24+
ABS_RX, ABS_RY and ABS_RZ. We might not use ABS_RX and ABS_RY.
25+
26+
Stick must have a threshold and pair of ways or single way
27+
so it can trigger one or two ways in the game.
28+
This is called 8-way digital stick. We can start simple
29+
with only a threshold. We should at lest calculate a circle
30+
threshold we can start with a 8 way square.
31+
"""
32+
if event.code == e.ABS_X: # Left stick X
33+
last_x = event.value
34+
if event.value < 63: # Left threshold
35+
ui.write(e.EV_KEY, e.KEY_LEFT, 1) # Press Left Arrow
36+
ui.syn()
37+
elif event.value > 192: # Right threshold
38+
ui.write(e.EV_KEY, e.KEY_RIGHT, 1) # Press Right Arrow
39+
ui.syn()
40+
else:
41+
ui.write(e.EV_KEY, e.KEY_LEFT, 0)
42+
ui.write(e.EV_KEY, e.KEY_RIGHT, 0)
43+
ui.syn()
44+
45+
elif event.code == e.ABS_Y: # Left stick Y
46+
last_y = event.value
47+
if event.value < 63: # Up threshold
48+
ui.write(e.EV_KEY, e.KEY_UP, 1) # Press Up Arrow
49+
ui.syn()
50+
elif event.value > 192: # Down threshold
51+
ui.write(e.EV_KEY, e.KEY_DOWN, 1) # Press Down Arrow
52+
ui.syn()
53+
else:
54+
ui.write(e.EV_KEY, e.KEY_UP, 0)
55+
ui.write(e.EV_KEY, e.KEY_DOWN, 0)
56+
ui.syn()
57+
58+
elif event.code == e.ABS_Z: # Left trigger Z
59+
if event.value > 200:
60+
ui.write(e.EV_KEY, e.KEY_S, 1) # Activate S
61+
ui.syn()
62+
else:
63+
ui.write(e.EV_KEY, e.KEY_S, 0) # Deactivate S
64+
ui.syn()
65+
66+
elif event.code == e.ABS_RZ: # Right trigger Z
67+
if event.value > 200:
68+
ui.write(e.EV_KEY, e.KEY_C, 1) # Activate C
69+
ui.syn()
70+
else:
71+
ui.write(e.EV_KEY, e.KEY_C, 0) # Deactivate C
72+
ui.syn()
73+
74+
if event.type == e.EV_KEY: # Detects simple click buttons
75+
"""
76+
start at 1 ends at 0 when lifted, super simple.
77+
BTN_EAST Ring
78+
BTN_SOUTH Cross
79+
BTN_WEST Square
80+
BTN_NORTH Triangle
81+
82+
BTN_TL L1
83+
BTN_TR R1
84+
85+
BTN_SELECT Share
86+
BTN_START Options
87+
88+
"""
89+
if event.code == e.BTN_EAST: # Ring
90+
if event.value == 1:
91+
ui.write(e.EV_KEY, e.KEY_END, 1) # Activate END
92+
ui.syn()
93+
else:
94+
ui.write(e.EV_KEY, e.KEY_END, 0) # Deactivate END
95+
ui.syn()
96+
97+
if event.code == e.BTN_SOUTH: # Cross
98+
if event.value == 1:
99+
ui.write(e.EV_KEY, e.KEY_LEFTCTRL, 1) # Activate Left CTRL
100+
ui.syn()
101+
else:
102+
ui.write(e.EV_KEY, e.KEY_LEFTCTRL, 0) # Deactivate Left CTRL
103+
ui.syn()
104+
105+
if event.code == e.BTN_WEST: # Square
106+
if event.value == 1:
107+
ui.write(e.EV_KEY, e.KEY_LEFTALT, 1) # Activate Left ALT
108+
ui.syn()
109+
else:
110+
ui.write(e.EV_KEY, e.KEY_LEFTALT, 0) # Deactivate Left ALT
111+
ui.syn()
112+
113+
if event.code == e.BTN_NORTH: # Triangle
114+
if event.value == 1:
115+
ui.write(e.EV_KEY, e.KEY_SPACE, 1) # Activate SPACE
116+
ui.syn()
117+
else:
118+
ui.write(e.EV_KEY, e.KEY_SPACE, 0) # Deactivate SPACE
119+
ui.syn()
120+
121+
if event.code == e.BTN_TL: # L1
122+
if event.value == 1:
123+
ui.write(e.EV_KEY, e.KEY_KP0, 1) # Activate Numpad 0
124+
ui.syn()
125+
else:
126+
ui.write(e.EV_KEY, e.KEY_KP0, 0) # Deactivate Numpad 0
127+
ui.syn()
128+
129+
if event.code == e.BTN_TR: # R1
130+
if event.value == 1:
131+
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 1) # Activate LEFTSHIFT
132+
ui.syn()
133+
else:
134+
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 0) # Deactivate LEFTSHIFT
135+
ui.syn()
136+
137+
if event.code == e.BTN_SELECT: # Share
138+
if event.value == 1:
139+
ui.write(e.EV_KEY, e.KEY_COMMA, 1) # Activate COMMA
140+
ui.syn()
141+
else:
142+
ui.write(e.EV_KEY, e.KEY_COMMA, 0) # Deactivate COMMA
143+
ui.syn()
144+
145+
if event.code == e.BTN_START: # Options
146+
if event.value == 1:
147+
ui.write(e.EV_KEY, e.KEY_ESC, 1) # Activate ESC
148+
ui.syn()
149+
else:
150+
ui.write(e.EV_KEY, e.KEY_ESC, 0) # Deactivate ESC
151+
ui.syn()
152+
153+
154+
# This function implements an analog-to-digital conversion with angular thresholding,
155+
# where the joystick angle determines whether to send a full digital press or a pulsed key press.
156+
# This makes the joystick act more smooth to Lara's turning movement,
157+
# def map_analog_to_digital(joystick_input):
158+
# ... implementation details ...
159+

0 commit comments

Comments
 (0)