atar-axis / xpadneo

Advanced Linux Driver for Xbox One Wireless Controller (shipped with Xbox One S)
https://atar-axis.github.io/xpadneo/
GNU General Public License v3.0
2.01k stars 113 forks source link

Provide example script in Python to start a rumble effect on the xbox controller #102

Closed gordonsolar closed 5 years ago

gordonsolar commented 5 years ago

Is your feature request related to a problem? Please describe. I don't get a rumble effect when trying to make the xbox controller rumble using the example python script provided on the evdev documentation site: https://python-evdev.readthedocs.io/en/latest/tutorial.html#injecting-an-ff-event-into-first-ff-capable-device-found

I am testing on a raspberry py raspbian stretch, kernel 4.14.98-v7+, python3.6 and the xpadneo driver installed. The controller is wireless using bluetooth. Rumble should in principle be possible since the tool fftest works fine and the controller rumbles, when xpadneo takes over control :-)

Describe the solution you'd like A python example script using evdev would be perfect, since i am using evdev together with asyncio to read out the controller buttons and analog input.

Describe alternatives you've considered An alternative would be a solution, where one uses e.g. fcntl.ioctl to write directly to the /dev/input/eventx file. An example for that is given in https://github.com/chrippa/ds4drv/issues/91 In my case i get en error and i am currently unable to understand/remove the error.

atar-axis commented 5 years ago

Here you go, q 'n d:

from evdev import ecodes, InputDevice, ff, util
import time

dev = None

# Find first EV_FF capable event device (that we have permissions to use).
for name in util.list_devices():
    dev = InputDevice(name)
    if ecodes.EV_FF in dev.capabilities():
        break

if dev is None:

    print("Sorry, no FF capable device found")

else:
    print("found " + dev.name + " at " + dev.path)
    print("Preparing FF effect...")

    rumble = ff.Rumble(strong_magnitude=0xc000, weak_magnitude=0xc000)
    effect_type = ff.EffectType(ff_rumble_effect=rumble)
    duration_ms = 1000

    effect = ff.Effect(
        ecodes.FF_RUMBLE, # type
        -1, # id (set by ioctl)
        0,  # direction
        ff.Trigger(0, 0), # no triggers
        ff.Replay(duration_ms, 0), # length and delay
        ff.EffectType(ff_rumble_effect=rumble)
    )

    print("Uploading FF effect...")

    effect_id = dev.upload_effect(effect)

    print("Playing FF effect...")

    repeat_count = 1

    dev.write(ecodes.EV_FF, effect_id, repeat_count)
    time.sleep(1)

    dev.erase_effect(effect_id) 
atar-axis commented 5 years ago

https://github.com/atar-axis/xpadneo/tree/pydev-example/misc/examples/python_evdev_rumble Give it a try and tell me if it works for you please, then I will merge it into master

gordonsolar commented 5 years ago

https://github.com/atar-axis/xpadneo/tree/pydev-example/misc/examples/python_evdev_rumble Give it a try and tell me if it works for you please, then I will merge it into master

You are simply too fast :-) Script works well, Thank you! Yesterday i took the fftest.c code from https://github.com/flosse/linuxconsole/blob/master/utils/fftest.c, learned some c-coding and the basics how to compile. Then i stripped the code until i had only the very necessary parts in it: open the device, generate the effect data, upload the effect data, start the effect, stop the effect. Doing so, i learned, that i have to wait after starting the effect before stopping it :-) Today i wanted to insert the wait into the python code and see if it works then ... :-)

Below the c-code with the basic steps to generate a rumble (to compile, you have to download also bitmaskros.h from the site given above). This example might help people coding in c. /*

include

include <sys/types.h>

include <sys/stat.h>

include

include

include

include

include <sys/ioctl.h>

include <linux/input.h>

include "bitmaskros.h"

int main(int argc, char* argv) { struct ff_effect effect; struct input_event play, stop, gain; int fd; const char device_file_name = "/dev/input/event0"; int i;

printf("Force feedback test program.\n");
printf("HOLD FIRMLY YOUR WHEEL OR JOYSTICK TO PREVENT DAMAGES\n\n");

for (i=1; i<argc; ++i) {
    if (strncmp(argv[i], "--help", 64) == 0) {
        printf("Usage: %s /dev/input/eventXX\n", argv[0]);
        printf("Tests the force feedback driver\n");
        exit(1);
    }
    else {
        device_file_name = argv[i];
    }
}
// Open device 
fd = open(device_file_name, O_RDWR);
if (fd == -1) {
    perror("Open device file");
    exit(1);
}
printf("Device %s opened\n", device_file_name);
// generate Data for a weak rumbling effect 
effect.type = FF_RUMBLE;
effect.id = -1;
effect.u.rumble.strong_magnitude = 0;
effect.u.rumble.weak_magnitude = 0xc000;
effect.replay.length = 2000;
effect.replay.delay = 0;
// upload effect
printf("Uploading effect #5 (Weak rumble, with light motor) ... ");
if (ioctl(fd, EVIOCSFF, &effect) == -1) {
    perror("Error");
} else {
    printf("OK (id %d)\n", effect.id);
}
fflush(stdout);
// play effect
memset(&play,0,sizeof(play));
play.type = EV_FF;
play.code = effect.id;
play.value = 1;
if (write(fd, (const void*) &play, sizeof(play)) == -1) {
    perror("Play effect");
    exit(1);
}
// wait until the effect is over
sleep(2);
// Stop the effect 
printf("Stopping effect\n");
memset(&stop,0,sizeof(stop));
stop.type = EV_FF;
stop.code =  effect.id;
stop.value = 0;

exit(0);

}

atar-axis commented 5 years ago

I am happy If you are, thank you for getting back and confirming that it works 😊

atar-axis commented 5 years ago

merged into master :grin: