Sending multiple triggers in one routine using serial port

OS : Win 10

PsychoPy version : V 2021.2.3

What are you trying to achieve?:

I’m trying to get Psychopy to send triggers to our brainvision EEG system through a serial port. The goal is to send triggers both when the stimuli (images) are presented and when the response key is pressed.

Below is the overview of the task in Psychopy:

I have a loop for the routine (ValenceRating) to show 1 image per iteration. Below is the condition file for this loop:

conditionfile

Below you can see the code I have so far in both “begin experiment” and “begin routine” tabs for triggers:

My questions:

  1. With a serial port, how can I read triggers from the condition file (is there a way to read through the trigger column when calling the triggers in Psychopy)? Right now I wrote “…” in the code in the “begin routine” tab because I didn’t know what should I placed there in order for the code to work.

  2. How to send triggers for more than one component in a routine? Right now with the code in the ‘begin routine’ tab, I expect Psychopy to send triggers for the first component in the routine. But I don’t know what to add to the code to also send triggers for the other components (for example in this task, third component (response key)) as well.

  3. Options for the possibility to integrate serial port as a component? (Similar to the parallel port component option). This will probably help us with the second question as well.

Thank you very much in advance.
Best wishes,
Somi

Hi Somi,

Thanks for your question - we’re working on making some materials to help people with this - so your questions are really helpful thankyou! In answer to your third question, yes I do think a component for serial port communication would be helpful.

I think this should work. Add a code component and in the “Begin Experiment” tab use:

import serial
port = serial.Serial('COM1')

In your Begin Routine tab use:

pulse_started = False
pulse_ended = False

in the Each frame tab use:

    if mything.status == STARTED and not pulse_started:# where "mything" refers to a visual thing you are trying to sync the trigger to (so you could have one of these for different component's in the same routine
        win.callOnFlip(port.write([0x01])) # send the trigger but only when the window flips to ensure syncronisation
        pulse_started  = True
        pulse_ended = False
        pulse_start_time = globalClock.getTime()
    if pulse_started and not pulse_ended:
        if globalClock.getTime() - pulse_start_time >= 0.005:
            pulse_ended = True

Oh and in terms of setting the trigger value through a conditions file. If you have a column with the header “trigger_value” and write what you want the trigger value to be on each trial, you should in theory be able to use this in the port.write() command. So instead of port.write([0x01]) it would be port.write([trigger_value]) - please let me know if that works!

I think that should do the trick - but please do let me know if not!!

Hope this helps
Becca

PS. to check the frame timing is write here we also highly recommend checking with a photodiode for visual stimuli if you can.

1 Like

Hello,

I have recently managed to send triggers via serial port, with a code similar to yours @Becca

Begin Experiment:
import serial
port = serial.Serial('COM3') 

Begin Routine:
pulse_started = False
pulse_ended = False

Each Frame:
if face_hab.status == STARTED and not pulse_started:
    port.write([trigger_values_hab]) # trigger_values_hab is the header of an excel coloumn where I define the triggers
    pulse_started = True
    pulse_ended = False
    pulse_start_time = globalClock.getTime()

if pulse_started and not pulse_ended:
    if globalClock.getTime() - pulse_start_time >= 0.005:
        pulse_ended = True

However I have some issues and any help would be appreciated:

1. I want to use the win.callOnFlip function as described in the link above and as can be seen below:

win.callOnFlip(port.write([trigger_values_hab])) 

But I receive this error:

“C:\Users\kppadmin\Desktop\Metin_Paradigm_2\Paradigm_2_ThirdTry_lastrun.py”, line 1451, in

win.flip()

File “C:\Program Files\PsychoPy\lib\site-packages\psychopy\visual\window.py”, line 1219, in flip

callEntry['function'](*callEntry['args'], **callEntry['kwargs'])

TypeError: ‘int’ object is not callable

I have also tried to change the settings in the properties of the excel coloums (I defined the values as “Text”, rather than “Standard”. Because otherwise the first code I wrote above did not work either…) But then, I receive this error:

data = to_bytes(data)

File “C:\Program Files\PsychoPy\lib\site-packages\serial\serialutil.py”, line 68, in to_bytes

return bytes(bytearray(seq))

TypeError: ‘float’ object cannot be interpreted as an integer

Any idea how I can make the win.callOnFlip function work while indexing to my trigger list on the excel sheet?

2. I have realized that not all the triggers are sent to the EEG. And I have figured out a pattern: If, let’s say, two identical triggers (I give the triggers values like 1,2 and 3 on the excel sheet) appear sequentially, then only the first one is being sent. For example, if I have stimuli_2 appear two times sequentially (meaning have two "2"s on the trigger_list_hab coloumn one coming after the other in rows), then a trigger called S2 is sent for the first stimuli_2, but not for the second stimuli_2. Do you know maybe what causes this?

Thanks in advance!

Hi There!

For win.callOnFlip, you need to call the function slightly differently the first argument would be the name of the function and the second would be the input to the function i.e.

win.callOnFlip(port.write, [trigger_values_hab)

regarding the second point - I am not sure about that - have you tried with values other than 1, 2, 3 ?

Becca

1 Like

Hi @grimfandangosh,

Can I also just confirm that you’re re-setting the port back to 0 before sending a second trigger?

Thanks!

Kim

1 Like

Hi @Becca and @Kimberley_Dundas

Thank you both for your fast replies.

@Becca I have tried the win.callOnFlip function again and I have not received any error this time, when I wrote the code as you suggested. However, triggers are either not sent or sometimes are sent and sometimes not… So, something is not right. I could not figure out what is the issue yet. I have tried changing the properties of the excel coloumn (I have realized when the properties are set to “Text”, only then the port.write function works for me. But I thought maybe this won’t be the case when I use win.callOnFlip. In fact, when I changed to properties to “Number” or simply “Standard”, the triggers started working but as I said, not consistently - i.e some triggers did not appear) Thank you for your help!

@Kimberley_Dundas Well actually, I was not re-setting the port back to 0. Now I am doing that and I can send multiple identical triggers one after another. :slight_smile: Thanks! Putting my code below:

if pulse_started and not pulse_ended:
    if globalClock.getTime() - pulse_start_time >= 0.005:
    port.write ([0x00]) # that was the missing part!
    pulse_ended = True

I would also like to ask some unrelated questions.

1.What does the number “0.005” actually refer to in the code above? Is it related to the trigger duration? If I am correct the trigger durations need to be set according to the sampling rate of the recording device. Although I am not sure where to define the duration.
2. I have seen somewhere else that timeout variable can be defined within the serial.Serial function. What does this variable actually refer to and is it important to define it?

Thanks in advance :slight_smile:

1 Like

Hi @grimfandangosh,

  1. Yes you’re perfectly correct, the 0.005 is the duration of the pulse - as in it will be set to your trigger value for 0.005s and then set back to 0 after that. So yes, if you wanted to change the duration you would need to change it in the code you’ve pasted here.
  2. Regarding timeout - this relates more to reading from the port than sending data to it from what I understand. It relates to whether you want to wait until a certain number of bytes have been received before reading. You can read more on this here but the short answer is that no, you don’t need to set it for your use here.
1 Like

Hi @Kimberley_Dundas

thanks a lot for your response. It is all more clear now.

One more question though: is it definetely necessary to set the port back to 0? (I was not doing it until I realized two identical triggers cannot be sent without doing it… But other triggers were working fine)

Thanks! :slight_smile:

Hi @grimfandangosh,

No problem!

Re- setting the port to something is necessary, otherwise you’re setting your variable pulse_ended to True but not actually making any changes to the port itself. Therefore when you want to send the same value on the next trial, no changes are made. So if the port was set to send a 2 trigger on one trial, and then the next trigger is also 2, no changes are made as the port is already sending the value you’ve asked for.

What you don’t have to do is re-set to zero though. If you wanted to send a trigger at the start of the routine and then a separate trigger to mark the end of the routine, you could set the port to a given value at the start of the routine and then set it to say 5 at the end of the routine.

2 Likes

Hi @Kimberley_Dundas

thanks again for the explanation :slight_smile:

Hello! I am attempting to do something similar within my study, but am having issues sending two separate triggers to the BioSemi. We tried some of the suggestions in this thread, but were unsuccessful. Essentially what I am wanting to do is send one trigger when the Flanker stimulus appears, and then another separate trigger when the participant responds (called key_resp). The goal is to evaluate both stimulus-locked and response-locked ERPs with these data. So far, we have been able to send triggers that correspond to my FlankSignal, but we have been unable to get a second trigger to send indicating a response (corrAns). What it is currently doing is adding a 1 or 0 to my 2/4 rather than sending a separate trigger (which should happen just after stimulus presentation). I have included images of the code, any suggestions would be greatly appreciated!


Code2

Code4

Hi @knagel27,

In your current each frame code, what’s happening is that everything that’s indented is only going to occur if the trigger hasn’t already been sent. But the trigger is marked as ‘sent’ as soon as the stimulus trigger has been sent. So everything related to your second trigger isn’t going to run, after the initial stimulus trigger has been sent.

The second issue is that the port is not reset back to 0 after the stimulus trigger.

Then finally, is there a reason you’re not able to use the “Store correct” option within the key_resp component to set whether an answer is correct? If so, the code I’ve suggested below will need to be modified, but otherwise if you can set up your key_resp component like this:

image

This will generate a variable (that will be stored in your data output also) called key_resp.corr. You can use that variable to send the triggers rather than setting the trigger to 1 or 0 manually as you’re doing now.

So, in your Begin Routine tab, use:

stimulus_started = False
stimulus_ended = False
response_started = False

Then in your Each Frame tab, try:

#Send the stimulus trigger    
if componentname.status == STARTED and not stimulus_started: #Replace componentname with the name of your stimulus component
    win.callOnFlip(port.write, str.encode(chr(FlankSignal)))
    stimulus_started  = True
    stimulus_ended = False
    stimulus_start_time = globalClock.getTime()
if stimulus_started and not stimulus_ended:
    if globalClock.getTime() - stimulus_start_time >= 0.005:
        port.write (str.encode(chr(0))) #Reset to 0
        stimulus_ended = True

#Send the response trigger
if len(key_resp.keys) > 0 and not response_started: #Send this when a participant has pressed a key   
    win.callOnFlip(port.write, str.encode(chr(key_resp.corr))) #key_resp.corr will be 1 if the response is correct and 0 if the response is incorrect
    response_started  = True

Then in the End Routine tab, use:
port.write (str.encode(chr(0)))
To reset the port back to 0 when a response has been made (assuming the key response ends your trial.

I’ve not tested this out, other than with print statements, so please let me know how you get on!

Hope this helps,

Kim

1 Like

Hi @Kimberley_Dundas,

This worked great! Thank you so much for your help with this. One more quick question for you, is there a way that I could send a number other than 0 for an incorrect response? Or perhaps switch it so that an incorrect response is 1 and correct is 0? As it is now, a trigger is sent for the stimulus (2 or 4) and for a correct response (1), which is excellent, but since we have set incorrect as 0, a trigger is not sent for an incorrect response.

Thanks!
Kaitlyn

Hi @knagel27,

So glad this worked!

In the Each Frame tab, you could modify the response trigger so that it adds 5 (or any other suitable number) onto the 1 or 0 stored by key_resp.corr? So you would have:

#Send the response trigger
if len(key_resp.keys) > 0 and not response_started: #Send this when a participant has pressed a key   
    win.callOnFlip(port.write, str.encode(chr(key_resp.corr + 5))) #key_resp.corr will be 1 if the response is correct and 0 if the response is incorrect, so triggers are 6 if correct and 5 if incorrect 
    response_started  = True

This should then generate a trigger value of 6 for a correct answer and 5 for an incorrect answer. Again, I’ve not tested though so let me know how you get on!

Thanks,

Kim

2 Likes

Thank you, @Kimberley_Dundas! That did just the trick. I appreciate your help!

1 Like

Hi! I’m trying to send multiple triggers to LUMO. My study design is pretty much the same as others in this forum and I tried all the codes above but it doesn’t work! My goal is to send triggers whenever the start screen and also the videos in both the baseline and the experiment conditions are played. I could send a trigger “1” for the “start screen” by defining the trigger as str. However, my codes are not working for sending triggers for the rest of the study: it sends triggers to the LUMO, but the triggers are like question marks or sometimes empty boxes on the signal!! I changed the excel format cell to txt, numerical, and general but it is not working!
Below is the overview of the task:

and here is my code for the baseline trigger in the Each frame tab of the baseline routine:

if BaselineAVideo.status == STARTED and not pulse_started:
win.callOnFlip(port.write, [Base_Trigger])
pulse_start_time = globalClock.getTime()
pulse_started = True
if pulse_started and not pulse_ended:
if globalClock.getTime() - pulse_start_time >= 0.005:
port.write([0x00])
pulse_ended = True

here is my code for the SetA trigger in the Each frame tab of the SetA routine:
if SetAVideo.status == STARTED and not pulse_started:
win.callOnFlip(port.write,[Actor_Trigger])
pulse_start_time = globalClock.getTime()
pulse_started = True
if pulse_started and not pulse_ended:
if globalClock.getTime() - pulse_start_time >= 0.005:
port.write([0x00])
pulse_ended = True
And here is my triggers:

Any help would be appreciated!

Best,
Zohreh

Hi @Becca and @Kimberley_Dundas, I was wondering if you could take a look at my coding. Is it something wrong with my coding? Thanks so much in advance!

Hi there @zsoleimani,

Apologies for the late response here!

I think the issue might be to do with the line of code where you’re resetting the port to 0. It seems like your LUMO wants triggers in string format which you’ve correctly identified, but the code is resetting the port to 0 using hexadecimal format ([0x00]). Therefore, could you try changing the line:

port.write([0x00])

to

port.write("0")

and see if that helps? This will need to be changed in all of your trigger code components.

Thanks so much,

Kim

Hi @Kimberley_Dundas ,

Thanks for your help. Finally, I figured out how to get Psychopy to send triggers from my condition file! but I need to send 3 triggers for one of my conditions (Exp: SetA) and it can’t send multiple triggers simultaneously. I looked at the output file in Matlab and from what I see in the S Matrix it could send two/out of three triggers at the same time but the third was sent with delays and sometimes it didn’t send one of them at all. Do you have any idea how to fix it?

Thanks in advance,
Zohreh


Hi @zsoleimani,

Is there a reason you can’t combine the triggers into one single trigger value? For instance,could the trigger value for your first condition be “2572” instead of 4 separate triggers? I think there might be quite a bit of time that’s taken up by the code you have running on each frame. Sending just one trigger rather than 3 or 4 will reduce that considerably.

Hope that helps/makes sense!

Kim