Using an E-Prime Button Box

Ok so I am trying to implement an e-prime button box to collect responses and reaction times for an experiment, preferably as a code component. I have found the demo so far, but it doesn’t save responses, or seem to have any documentation … anywhere… as to how it works (particularly the pst box function). I was online most of the morning with one of our resident computer guys and any documentation referencing this functionality was lablelled “TBC”. Would someone be able to advise?

Thanks!

Kyle

Hi Kyle,

to save log files in PsychoPy, one typically uses the TrialHandler. TrialHandlers are created automatically by the Builder for loops you insert. You can add data to the log file by doing something like:

trials.addData('column name', data)

where trials is the name of your loop.

So in case of responses collected via a button box, you may want to do:

trials.addData('RT', rt)
trials.addData('Response_Button', button)

rt and button would be the response time and response button acquired as demonstrated in the demo script.

If you need more assistance, we’d require more specific information about your experiment to provide helpful information.

Cheers,

Richard

Hi Richard,
Thanks for the reply.

This experiment is pretty simple (other than on this one response box part).

I have used builder with a few code components for things like error feedback. Each trial consists of showing a participant an image, which requires a response that will indicate that the stimuli either does, or does not, match some criterion. So one button will indicate it does match, while a second button indicates that it does not. Both the list of stimuli, and the list of correct buttons are stored in an excel file and retrieved for each trial via a loop.

So essentially the code component will (hopefully) be replacing a keyboard component in the builder routine that displays the trial stimuli (image) and then records the RT and button of the response. If it is simpler I can simply mark answers as correct or incorrect during data analysis, but I do need to know what button they pushed. Also, if possible, It would be good if a response could trigger an end to the trial. I have attached a screenshot of the keyboard component, the functionality of which I need to be able to replace.

Cheers,
Kyle

PS: Also which libraries do I need to import. I am assuming that I would also put these in the “beginning of experiment” part of the code component, while the actual code for recording of responses would go at the “end routine” part?

PPS: It would really really be nice to be able to do this as a code component that could be applied to different experiments with this setup. Our department has something like 14 of these boxes that have gone unused for years because people do not have E-prime and are not programmers (I am told this involved an impulsive decision way back relating to an unexpected budget surplus). So if I could set this up in this manner, then I could share this code component with a short set of accessible instructions, and maybe put some of these boxes to some use outside of just my own immediate needs.

Hi Richard, is there anything that I could add that might make it easier to answer my question?

Cheers,
Kyle

Hi Kyle, sorry, didn’t have time to really look any further into this, but will do so tomorrow!

Hi Richard,
So one of our resident departmental programming guys is suggesting that this would be easier to solve if we programmed the whole thing from scratch in python without psychopy, given the lack of documentation for this functionality. I’d like to save him the work, but if you don’t have the time or don’t yourself know how this function works, then I’ll understand.

Cheers,
Kyle

Hi, I only rarely use the Builder, but I think you would want to do something like the following.

You need a code component somewhere. Under Begin Experiment, you would put something like:

# ioHub configuration.
SERIAL_PORT = 'COM5'
BAUDRATE = 19200

iohubkwargs = {
    'serial.Pstbox': dict(name='pstbox', port=SERIAL_PORT, baud=BAUDRATE)
}

# Start the iohub server and set up devices.
io = launchHubServer(**iohubkwargs)
pstbox = io.devices.pstbox

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

Under Begin Routine, I assume you could put the code to clear the response buffer:

pstbox.clearEvents()
rt_measurement_started = False

Under Each Frame, you could try to record the onset time of the stimulus stimulus, to which the participants are supposed to respond:

if not rt_measurement_started and stimulus.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

It is important that the stimulus is positioned above this code component in the Builder, otherwise RTs may be one frame off!

Under End Routine, you can check out the collected responses and save them:

pstevents = pstbox.getEvents()
if pstevents:
    rt = pstevents[0].time - stimulation_onset_time
    button = pstevents[0].button
else:
    rt = None
    button = None

trials.addData('RT', rt)
trials.addData('Response_Button', button)

Hope that helps.

Yes I think the key is to combine the box access code from here:
https://github.com/psychopy/psychopy/blob/057736cd951f2d313cb7bb4b11a55829a60a9441/psychopy/demos/coder/iohub/serial/pstbox.py

with your experiment in PsychoPy. Code components allow you to specify the code that goes at different points. So, from the code above, you’ll need something like this at experiment start to initialise the box:

from psychopy.iohub import launchHubServer
iohubkwargs = {
    'serial.Pstbox': dict(name='pstbox', port=SERIAL_PORT, baud=BAUDRATE)
}
io = launchHubServer(**iohubkwargs)
pstbox = io.devices.pstbox
computer = iohub.devices.computer  #to check times
pstbox.setLampState([0, 0, 1, 0, 0]) # light a lamp to show it worked

then at Begin Routine on you’d need:

pstbox.clearEvents()
start_time = computer.getTime()

Then on each frame it will be something like this:

if <insert some condition about when box is **starting**>:
    t0 = computer.time
if <box is running>:
    pstevents = pstbox.getEvents()
    if pstevents:
        # jus tchecking first button (but not likely to be two in one frame)
        trials.addData("RT", pstevents[0].time - t0)
        trials.addData("button", pstevents[0].button)

To start with you can put these into a Code Component but youd could later convert that into a Custom Component to be more reusable.

cheers
Jon

1 Like

haha, sorry Richard and I were working on near-identical responses together! Shows how consistent the advice is! :wink:

1 Like

Thanks guys!
I’ll try this out later today. If this works, it will save the PST boxes from rotting in a closet and make everyone feel a bit better about the money that was spent on them.

Would there be any way… provided this works… to make the code component instructions available on the website in the way that the error feedback code component is? Speaking for the other dumb people, I actually found it a pretty accessible/useful compromise between harassing our computer guy and you guys creating a full fledged builder component. Also if people actually want to use the boxes with psychopy, it would be more useful than the current demo.

Oh and one other question: Does this end trials on its own after a response is collected? Nothing is jumping out at me as serving that function.

Cheers,
Kyle

My code certainly doesn’t. To reach that goal, should probably modify the code component like this.

Under Every Frame:

if not rt_measurement_started and stimulus.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

pstevents = pstbox.getEvents()
if pstevents:
    continueRoutine = False  # Stop if we have a response.

And under End Routine, do this:

if pstevents:
    rt = pstevents[0].time - stimulation_onset_time
    button = pstevents[0].button
else:
    rt = None
    button = None

trials.addData('RT', rt)
trials.addData('Response_Button', button)

I think we should provide a PST box component in the builder to really make this more accessible. The current coding solutions are, in my opinion, quite ugly workarounds.

1 Like

Thanks,
And I’ll defer to you on that!

Oh and by the way, should using the error feedback code work basically the same way as before?

Cheers,
Kyle

Oh and it is telling me this when I run it:
‘serial.Pstbox’: dict(name=‘pstbox’, port=SERIAL_PORT, baud=BAUDRATE)
NameError: name ‘SERIAL_PORT’ not defined

*Edit: wait, I am not totally helpless. It was missing the lines where we define serial port and baudrate:
SERIAL_PORT = 'COM3’
BAUDRATE = 19200

However it now says that iohub is not defined

*Edit #2: I tried taking “hub” out of the line of code and it says that iohubdevices has not attribute ‘computer’

*Edit #3 Ok I apparently was using Jon’s suggestion previously, and replaced the offending line with:
def get_stimulation_onset_time():
# We can use this to keep track of stimulation onset.
global stimulation_onset_time
stimulation_onset_time = core.getTime()

This now gets me an error message of:
File “C:\Program Files (x86)\PsychoPy2\lib\site-packages\psychopy-1.84.2-py2.7.egg\psychopy\iohub\client_init_.py”, line 247, in init
raise AttributeError(“An existing ioHubConnection is already open. Use ioHubConnection.getActiveConnection() to access it; or use ioHubConnection.quit() to close it.”)
AttributeError: An existing ioHubConnection is already open. Use ioHubConnection.getActiveConnection() to access it; or use ioHubConnection.quit() to close it.

Ok so my progress with testing this code out has plateaud. We have a few people on a few different computers trying things, but everyone is either getting that raise attribute error that I mention in the previous comment, or psychopy is saying that iohub is timing out or failing to connect with its server.

Kyle

Hi, I don’t know which code you’re currently using. Please provide the relevant parts of the compiled script, or your psyexp file. Thanks!

Sure no problem!

Though apparently I cannot attach the images that I am using.

Kyle

JillThesis_BS_CN_Test.psyexp (50.3 KB)
TrialList3.xlsx (9.0 KB)
TrialListC.xlsx (8.8 KB)
TrialListN.xlsx (8.8 KB)

Hi Richard,
So you remember how I mentioned that the computer guy that I consulted here mentioned that it would be easier to write something from scratch than to make Psychopy work for the button box? Apparently he was being serious because he went ahead and did just that. This afternoon I received a working code component for running the box with my experiment. I don’t fully understand what the problem was, but from what I gathered the problem was iohub itself. Something about it bringing up a lot of stuff that did other things in addition to what was needed and bogged down the computer while conflicting with what we were trying to do with the box. He wrote a script (I think thats the term in this context) called PstButtonBox.py, and another person on our team rewrote your suggested code component to reference PstButtonBox instead of iohub. It actually runs really smoothly and doesn’t lag badly in the way that even the iohub pst box demo did. I am going to be writing this into my and my honour’s student’s respective studies over the weekend so that we can start each testing on Monday!

BTW, my own experiment is a divided visual field test (written in builder with code components), which to my knowledge there is not a demo for yet. If you or Jon would like access to our work on this, I’d be happy to share so that it can benefit anyone else that wants to try something like this. The both of your code components that you suggested formed the template of this final product and saved us a lot of time in figuring out how the builder itself worked so that we could mesh these things together. It was my goal to make this work via psychopy so that we could make those response boxes available to students and staff who didn’t want to shell out for expensive software (or who may be hesitating to do certain projects because of the upfront investment). Really I think that is one of the more noble things that Psychopy can be used for.

Cheers and thanks for the assistance!

1 Like

Hi Kyle, thanks for that feedback! I really think it could be worthwhile sharing that code and consider whether this could be included “upstream” in PsychoPy! This would benefit both new users (who would find an easy-to-use solution built-in) and you and your lab since the PsychoPy devs would try to ensure that the existing implementation would continue to work in future releases. Win-win!

A colleague of mine is btw using expyriment’s StreamingButtonBox and is quite happy with it. But he also uses expyriment to display the visual stimuli, and I’ve never really tried to use this implementation with a “pure” PsychoPy experiment. One issue with the PST box is that is that it constantly streams data, so you will have to empty the OS buffer regularly, because its content is constantly overwritten is new data is received (approx. 800 chars/sec).

With regard to the existing implementation: it does come with some (imho very annoying!) boilerplate for sure. iohub runs in a separate process to constantly poll/interact with hardware in the background, without disturbing stimulus presentation in the main PsychoPy process. I believe this is the right approach, but it’s IMHO way too difficult to use as it is now. The interface should be simplified, potentially by wrapping often-required features (like waiting for a certain response button) in simpler methods.

1 Like

No problem Richard,
I also may have spoken slightly too soon. Apparently our fix works beautifully on mac machines but the PC computers deny psychopy access to the COM ports. We’ll be taking another crack at that on Monday, while we run on an alternate mac machine to the PC we had intended to use. Once that last bug is fixed and I add some instructions into the code component, I’ll pass everything along.

Cheers,
Kyle

1 Like