Technical help for sending event triggers: Brainvision EEG with Psychopy

As a quick intro to my current setup, I’m current using Brainvision Recorder on the acquisition PC, along with Brainvision’s LiveAmp and the Sensor & Trigger Extension (STE) for LiveAmp. The experiment PC is connected to the STE with a usb to IO port that goes into the trigger in port of the STE device.

I played around and wanted to test how things work using a simple experiment setup. The code is as follows:

from psychopy import visual, core, event
import serial
import time
import threading

Connected = True
PulseWidth = 0.01
def ReadThread(port):
  while Connected:
     if port.inWaiting() > 0:
        print("0x%X"%ord(port.read(1)))
# Open the Windows device manager, search for the "TriggerBox VirtualSerial Port (COM6)"
# in "Ports /COM & LPT)" and enter the COM port number in the constructor.
port = serial.Serial("COM6")
# Start the read thread
thread = threading.Thread(target=ReadThread, args=(port,))
thread.start()
# Set the port to an initial state
port.write([0x00])
time.sleep(PulseWidth)

# Create a window
win = visual.Window(size=(800, 600), units="pix", fullscr=False, color=[-1,-1,-1])

# Create a green circle stimulus
circle = visual.Circle(win, radius=50, fillColor=[0, 1, 0], lineColor=[0, 1, 0])

# Number of frames to display the circle for 1 second
frames_for_1_sec = 60

event.clearEvents(eventType='keyboard')

for trial in range(3):
    # Reset frame counter for each trial
    frame_count = 0
    port.write([0x01]) #S4
    time.sleep(PulseWidth)

    while frame_count < frames_for_1_sec:
        # Check for "esc" key press to exit
        keys = event.getKeys()
        if 'escape' in keys:
            win.close()
            core.quit()
        elif 'space' in keys:
            port.write([0x02]) #S
            time.sleep(PulseWidth)            

        # Draw and show the circle
        circle.draw()
        win.flip()

        # Increment frame counter
        frame_count += 1
    
    #port.write([0x00])
    #time.sleep(PulseWidth)
    # Clear the screen after each trial (for 1 second)
    win.flip()
    core.wait(1)

# Close the window
win.close()

# Reset the port to its default state
port.write([0xFF]) #Reset = 12
time.sleep(PulseWidth)

# Terminate the read thread
Connected = False
thread.join(1.0)
# Close the serial port
port.close()

At the recorder view that showed the sensor’s live activity, I was able to view the triggers, marked as annotations by the base of the EEG signal monitor. So at the very least, trigger signal could be sent out from the experiment PC to the acquisition PC.

  1. However, the annotations are often very inconsistent. For a start, port.write([0x00]),port.write([0x01]) and port.write([0x02]) all resulted in the same label of S4 (stimulus 4) on the signal monitor. And I can’t seem to find a way to have 01, 02 or even 00 (which is supposed to be a reset to baseline state?) marked as different annotation marker. How do I remedy this issue?

  2. The annotation delay coming from port.write wildly fluctuates. Annotations can sometimes appear immediately, sometimes, delayed for about a second, sometimes even not appearing at all. What might be the issue right here?

  3. I would really prefer not to use time.sleep after each port.write call, as that would mess up timing. At the same time, it seems like the sleep is needed for port.write to be properly registered/functional without being skipped over at times. Is there a better way to get around this issue?

Any help would be greatly appreciated!