Using an E-Prime Button Box

Excellent, looking forward!

Hi Richard,
So here we are!

The code that runs the box itself and replaces IOhub is attached. It goes into the same folder as the experiment.

pstButtonBox.py (4.3 KB)

As is the code component/instructions (pasted at the end). Though technically for this to work it needs to be split into three code components. The “begin experiment” part cannot be in a routine that is inside of a loop, or else it gives you an error message saying that you don’t have permission to use the port.

The experiment file itself is at our lab, and I’ll send that next time I am in. We figured out the other day that I cannot attach the image files here, and so I could email that to someone.

These are the necessary pieces to the code components for using the button box and providing error feedback

#Comments are provided for how to use this

#IMPORTANT: if you are using a windows machine, place the “begin experiment” section in its own code component at the begining of builder outside of any loops
#… otherwise you will get an error

Begin experiment

from pstButtonBox import pstButtonBox

#If windows machine, repalce ‘/dev/cu.usbserial’ with ‘COM1’ or ‘COM2’, etc.
#Also select if you want any lamps on below

pstbox = pstButtonBox(’/dev/cu.usbserial’)
pstbox.flush()
pstbox.lamps([1,0,0,0,1])

def get_stimulation_onset_time():
# We can use this to keep track of stimulation onset.
global stimulation_onset_time
stimulation_onset_time = core.getTime()

Begin Routine

pstbox.flush()
rt_measurement_started = False

Each Frame

#Here “stim”, from stim.status is the name of the text, image, or whatever, component that participants respond to. Change name as needed.
if not rt_measurement_started and Stim.status == STARTED:
# Will set the global variable stimulation_onset_time to the exact time
# the visual stimulus stimulus is being presented.
win.callOnFlip(get_stimulation_onset_time)
rt_measurement_started = True
pstbox.flush()

#if pstevents in (set available buttons from 1, 2, 4, 8, or 16)
pstevents = pstbox.mostRecent()
if pstevents in (1,16):
responseTime = core.getTime()
rt = responseTime - stimulation_onset_time
button = pstevents
continueRoutine = False # Stop if we have a response.
break #Stop the for loop now that we have an acceptable key press

End Routine

if not pstevents:

rt = None
button = None
isCorrect = False

else:
isCorrect = button == (2 ** (Correct - 1))
#“Correct” above is whatever variable you used to name your list of correct answers in the excel file

Trial.addData(‘TrialResponse_Button’, button)
Trial.addData(‘TrialCorrect’, isCorrect)
Trial.addData(‘TrialRT’, rt)

End Experiement

pstbox.lamps([0,0,0,0,0])

#if you want to use error feedback.

In the feedback routines, change the Begin Routine to this

#Make sure you set the text component displaying msg to “set every repeat”
if button == None:
msg=“Oops! You didn’t answer in time”
elif isCorrect:#stored on last run routine
msg=“Correct!”
else:
msg=“Oops! That was wrong”

Hello Richard,

I was wondering if I can ask a question regarding this post. I’ve adapted the code you provided so that we can use our Eprime response box to advance through an experiment. It works, but every button press makes the experiment advance extremely fast (across two routines). I’ve used the “clearEvents” command to empty the buffer, but it makes no difference. If you have any suggestions, I’d be very grateful.

best wishes
josé

Hi josé
Did it do this with a keyboard as well? It sounds like you have no delay between trials, and so the experiment advances to the next trial instantaneously before the participant may be ready.

Cheers,
Kyle

Hi Kyle!

With the keyboard this didn’t happen. Each button press only advanced to the next screen. With the e prime response box, I guess I could add a pause between routines so that each button press only advances to the next screen, but there must be a way to make it work like the keyboard component?Thanks!

Hi josé,
There isn’t really anything in the code component that should interact with your experiment in such a way as to cause it to be skipping trials. It just doesn’t interact with the loop at all. It just records what happens in the trial. Would you be able to post the experiment? You might have changed something by accident when putting in the code component.

Cheers,
Kyle

Hi Kyle,

Sorry, I think I didn’t explain this properly. The code component is not part of a loop. We want to use it to move from one routine to the next. So, I did add an “continueRoutine = False” line of code, but the problem is that if the finger is still on the button, it flashes through the experiment. I thought that the clearEvents command would take care of that, but it doesn’t.

Thanks for your time, by the way.
Best,
José

Hi José,
I am not sure that I understand what you mean. How can you have more than one trial if there is not a loop? Did you code each routine separately? That sounds really really time consuming.

Ok well if that is the case, then I can see why adding a buffer routine would be more work than if this was embedded in a loop!

I’ll link one of the people who helped make this. He thinks that he has an idea.

Kyle

Ok he couldn’t log in at this moment but he says that he needs to see the code and/or builder file to understand what it is that you are trying to do.

and I’ll second the request as well

Cheers,
Kyle

Hi,
I don’t have more than one trial. I have 5 successive routines (these are to give instructions to the participants), and the idea is to have participants advance through them via button presses. Those routines are not timed. The participant gets as much time as they need to read the instructions. The button press from the response box is what ends each routine. The problem is that it also ends the next routine…

Thanks for the help!
Best,
José

Ok, I’ll share this in a tic!
Best,
José

Wait, if this is not timed, then why are you using a button box to begin with? That is the only advantage of this hardware over a keyboard.

Kyle

Great question!

Because the study involves over 10 tasks, and some of them are timed. So, to prevent participants from getting confused about what keys they need to use for which task, everything has been set up so that they can use two keys from a response box for all tasks. At my previous lab, where data from one experimental group are being collected, our response box functioned as a keyboard. However, at my current lab, we only have an eprime response box, which doesn’t function like that…

I hope this makes sense!
best
José (massively appreciative of the time you’re taking to respond to my queries)

Hello,

Since the real thing is huge, I created a mini experiment with the code I’m using. You’ll see that each button press makes it advance through 2 routines.

Thanks,
josétest.psyexp (17.0 KB)

Hi,

After more testing, we think the problem is that a routine is skipped because psychopy seems to detect that the button is still pressed when the next routine begins.

I sort of solved the problem by adding a core.wait(.5) command, but I wonder if there is a way to set it up such that the “effects of the button press” end with the routine.

josé

Oh that is definitely the problem. The boxes transmit a continuous stream of characters while being pressed.

I see… Is there a way to stop that? Is there a way to ask psychopy to only capture the first character?

Thanks, Kyle.
best
josé

HI José,

I helped develop the code implementation of our pstButtonBox class, so maybe I can help (see Kyle’s post from Dec. 5th). Before I get to the problem at hand, I wanted to make sure that we are talking about the same code. From the test.psyexp file you posted, it seems like you are using the serial.Pstbox from the ioHub (from Richard’s post on Nov. 22nd). What Kyle has been talking about is our own class to replace the ioHub that we developed that was specifically for the ePrime pst box. It is called pstButtonBox (see Kyle’s post from Dec. 5th). If you are using the ioHub, I don’t know that code well enough to properly advise you. I will proceed instead on the assumption that you are using our pstButtonBox code and you can adapt it accordingly.

No matter which code you are using, I think you have properly diagnosed the problem, in that the program still sees the button as being pressed when it gets to the next screen. This is not a fault of the function itself, per se, just the fact that the computer is so fast and gets to the next instruction window and waits for a button press before a normal human can lift their finger up. Adding the half-second pause, as you did, is one way to solve the problem. Another way is to have the program wait for the button to be lifted before proceeding. With our class, you would do the following:

You can insert this code in the End Routine Section, or in the Begin Routine for the next instruction screen.

I do not have a button box at home, so I am not absolutely sure if it is a None value that gets returned when a button is not being pressed. It’s possible that the number 0 is returned if no buttons are being pressed. If that doesn’t work, you can try other things (like not equal to the numbers 1 to 31). If neither of these things work for you, I will check it out for you when I go back into the office on Monday. If you are using the ioHub code, there is probably something equivalent that you can do.

Hope this helps.

Dear Darcy,

I’ll try this first thing in the morning tomorrow! Thanks a lot for your kindness!
Best,
José

Hello,

Okay, so I tried using the code that you and Kyle developed, but it didn’t work. So we went back to using ioHub and we adapted the “while true: …” line of code that you suggested and now it works (but we’re not certain why…)

What I did was put the code below in the End Routine box of the code component:

while true:
if pstbox.getEvents():
break

I think the code tells psychopy that if a character is being sent by the response box at the end of the routine (due to the fact that the button is still pressed), then it should break the loop. Not sure it makes sense…

Thanks!