3

I'm very new to evdev and am currently trying to use an xbox controller to control a small robot. I am successful with each of the buttons, but I can not seem to figure out how to get valid readings from the joysticks.

This code only works for button response.

edit: Whenever an ABXY button is pressed, EV_ABS events occur also. I am not sure how to distinguish between these values and actual values of the joystick. Additionally, the event codes from the joystick movements are inconsistent. Here is the code I'm currently running:

from evdev import InputDevice, categorize, ecodes
dev = InputDevice('/dev/input/event1')

print(dev)

for event in dev.read_loop():
    if event.type == ecodes.EV_KEY:
        print("button is pressed")
    elif event.type == ecodes.EV_ABS:
        print(event)

Any advice is appreciated! Thanks!

s.t
  • 31
  • 1
  • 3
  • You'll get `EV_ABS` and/or `EV_REL` events for the joysticks. The exact details depend on the gamepad, but I don't have an xbox around :-( – rodrigo Jul 05 '17 at 19:25
  • Thank you so much for the quick reply! I tried that, but when pressing and releasing buttons, events with EV_ABS values also occur. I'm not quite sure why. The event codes for the joysticks are also very inconsistent. – s.t Jul 05 '17 at 19:28
  • Ah, but that is because in some gamepads, some buttons are arranged as a cross (I think it is called d-pad). And those are sometimes mapped by the driver to a _hat_, that in Linux is a pair of `EV_ABS` axes. Those `ABS_HAT*` axes usually only have three possible values: -MAX, 0 and +MAX, that represent one button, no button and the other button, respectively, just as those old arcade digital joysticks. – rodrigo Jul 05 '17 at 19:31
  • But note that I said _some gamepads_ and _sometimes_... For example, my chinese cheapo gamepad has a _Mode button_ that selects between analog joystick plus a buch of buttons and digital joystick plus a hat and fewer buttons... – rodrigo Jul 05 '17 at 19:34
  • The buttons that are returning multiple events are the ABXY buttons. How do I distinguish between these events and the events from the actual joysticks? – s.t Jul 05 '17 at 19:39
  • About the inconsistence, that is expected: when you move a joystick it is impossible to move the `ABS_X` without moving the `ABS_Y` too. And all the `EV_SYN` will make the console output a mess. My advice is to focus on one event at a time. For example, first print only `EV_ABS/ABS_X`, then only `EV_ABS/ABS_Y` and so on. – rodrigo Jul 05 '17 at 19:40
  • When I press `A` in my non-xbox gamepad, I get a `EV_MSC`, a `EV_KEY` and a `EV_SYN`. A similar output for when I release it. Those are expected, but you can generally ignore all but `EV_KEY`. If you are getting different, maybe you should post the full dump of your events. I recommend using command `input-events` from `input-utils`. – rodrigo Jul 05 '17 at 19:43
  • How do I differentiate between the multiple readings of the button and the joystick movement? – s.t Jul 05 '17 at 20:00
  • I'm not sure I understand your last question... you have `event.type==EV_KEY` and `event.code==BTN_A` and `event.value!=0` when pressed and `event.value==0` when released... – rodrigo Jul 05 '17 at 20:21
  • When I press and release a button twice, this what comes up: button is pressed / event at ----, code 01, type 03, val 109 / event at ----, code 01, type 03, val -1 / button is pressed / button is pressed / event at ----, code 03, type 03, val 1297 / event at ----, code 03, type 03, val 1184 / button is pressed / event at ----, code 01, type 03, val 110 / event at ----, code 01, type 03, val -5 When just one joystick is moved, the codes vary from 0 to 4 and the values vary as well – s.t Jul 06 '17 at 13:24
  • Now the problem is that you are not printing the button event information. Try again with `print("button:", event)` in the first `if` and `print("abs:", event)` in the second. – rodrigo Jul 06 '17 at 15:31

1 Answers1

2

Python is not my favorite language, and there is probably a better method of calibrating, but I slapped this together. Hope it helps.

All the mappings and maximums were measured from my Xbox One Wireless controller, so you may need to experiment/adjust the constants to your own controller.

from evdev import list_devices, InputDevice, categorize, ecodes

CENTER_TOLERANCE = 350
STICK_MAX = 65536

dev = InputDevice( list_devices()[0] )
axis = {
    ecodes.ABS_X: 'ls_x', # 0 - 65,536   the middle is 32768
    ecodes.ABS_Y: 'ls_y',
    ecodes.ABS_Z: 'rs_x',
    ecodes.ABS_RZ: 'rs_y',
    ecodes.ABS_BRAKE: 'lt', # 0 - 1023
    ecodes.ABS_GAS: 'rt',

    ecodes.ABS_HAT0X: 'dpad_x', # -1 - 1
    ecodes.ABS_HAT0Y: 'dpad_y'
}

center = {
    'ls_x': STICK_MAX/2,
    'ls_y': STICK_MAX/2,
    'rs_x': STICK_MAX/2,
    'rs_y': STICK_MAX/2
}

last = {
    'ls_x': STICK_MAX/2,
    'ls_y': STICK_MAX/2,
    'rs_x': STICK_MAX/2,
    'rs_y': STICK_MAX/2
}

for event in dev.read_loop():

    # calibrate zero on Y button
    if event.type == ecodes.EV_KEY:
        if categorize(event).keycode[0] == "BTN_WEST":
            center['ls_x'] = last['ls_x']
            center['ls_y'] = last['ls_y']
            center['rs_x'] = last['rs_x']
            center['rs_y'] = last['rs_y']
            print( 'calibrated' )

    #read stick axis movement
    elif event.type == ecodes.EV_ABS:
        if axis[ event.code ] in [ 'ls_x', 'ls_y', 'rs_x', 'rs_y' ]:
            last[ axis[ event.code ] ] = event.value

            value = event.value - center[ axis[ event.code ] ]

            if abs( value ) <= CENTER_TOLERANCE:
                value = 0

            if axis[ event.code ] == 'rs_x':
                if value < 0:
                    print('left')
                else:
                    print('right')
                print( value )

            elif axis[ event.code ] == 'ls_y':
                if value < 0:
                    print('foreward')
                else:
                    print('backward')
                print( value )
Nick
  • 742
  • 8
  • 14