psychopy.org | Reference | Downloads | Github

Joystick/Gamepad button pressing

Hi all,

I’m coding a Nback task and want to use a joystick/gamepad for the button press. It’s a Logitech F310 gamepad controller. After some tinkering it works but the output/controller is still not working properly. If I press a button once in a trial (each trial allowing multiple button presses) then it records it as a button press multiple times.

joy = joystick.Joystick(0)

bttnPress = joy
bttnPress.clock = core.Clock()

            # *bttnPress* updates
            if t >= 0.0 and bttnPress.status == NOT_STARTED:
                # keep track of start time/frame for later
                bttnPress.tStart = t
                bttnPress.frameNStart = frameN  # exact frame index
                bttnPress.status = STARTED
                win.callOnFlip(bttnPress.clock.reset)  # t=0 on next screen flip
            if bttnPress.status == STARTED:
                theseKeys = []
                theseRTs = []
                if moviePres.status == FINISHED:
                    bttnPress.status == FINISHED

                if bttnPress.getButton(1):  # at least one key was pressed
                    bttnPress.rt.append(bttnPress.clock.getTime())
                    bttnPress.keys.append('A_Button')
                    theseKeys.append('A_button')
                    theseRTs.append(t)
                    continueRoutine = True
...
...
...
        if len(bttnPress.keys)==0:  # No response was made
            bttnPress.keys=None
        if bttnPress.keys != None:# bttnPress.keys != None:# moviePres.status == FINISHED # we had a response #joy.getButton(1):
            VideoPlays.addData('bttnPress.rt', bttnPress.rt)
            VideoPlays.addData('bttnPress.keys', bttnPress.keys) #theseKeys
        # the Routine "moviePlayer" was not non-slip safe, so reset the non-slip timer
        routineTimer.reset()
        thisExp.nextEntry()

This only works in a pygame window, pyglet creates an error.

I realize some of my code is excessive but I’m a newbie. Currently I’m trying this on version 3.0.09 and using it on Mac OSX Sierra 10.13.6.

If it helps, attached is a csv sample output of this code.

Noah_Nback_noise_2018_Sep_14_1819.csv (2.0 KB)

Thank you all,

Noah

I’ve never worked with joystick code, but I suspect it is treating the buttons like a mouse does. This is different to the way that the standard PsychoPy keyboard object works. i.e. with a keyboard, we detect keys as a single event (e.g. the a key was pressed). We don’t get information about whether the key is still being held down, or when or if it was released. But when checking for mouse buttons, we get the instantaneous state of the button (i.e. is it held down at this precise moment in time).

A button press has a finite duration that is often longer than the sampling period we are using to detect it. So a single mouse press can be detected as being pressed on multiple consecutive checks. If that is the issue, then you need to alter your logic to detect the first time that the button is detected as down. You’d then set a flag like button_down = True. You don’t record anything again until button_down = False (which you set when the button is not detected as pressed.

Suggested code for handling this issue with a mouse is here:

Hi, after playing around I got it. I took it from the code produced by the mouse click in the builder gui. It’s as follows:

from psychopy.hardware import joystick
joy = joystick.Joystick(0)
bttnPress = joy
bttnPress.status = None

if t >= 0.0 and bttnPress.status == NOT_STARTED:
                # keep track of start time/frame for later
                bttnPress.tStart = t
                bttnPress.frameNStart = frameN  # exact frame index
                bttnPress.status = STARTED
                prevButtonState = bttnPress.getButton(1)  # if button is down already this ISN'T a new click
            if bttnPress.status == STARTED and bool(postMovieFix.status == FINISHED):
                bttnPress.status = STOPPED
            if bttnPress.status == STARTED:  # only update if started and not stopped!
                buttons = bttnPress.getButton(1)
                if buttons != prevButtonState:  # button state changed?
                    prevButtonState = buttons
                    if buttons:#sum(buttons) > 0:  # state changed to a new click
                        x = 'Pushed'
                        bttnPress.x.append(x)
                        bttnPress.time.append(moviePlayerClock.getTime())

This may be a bit excessive but it works and just required repurposing preexisting code. Thanks for the help!