Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Supported by

SerialException: could not open port 'COM3' WindowsError(5, 'Access is denied')

edited June 22 in OpenSesame

I've been trying to send a trigger through a CEDRUS StimTracker Duo. The information on here about using serial ports seemed fairly straight forward, but implementing the scripts has not worked for me despite many modifications to the script. My current setup looks as follows, This inline script is at the very beginning of the experiment:

import serial
ser = serial.Serial()
ser.baudrate = 115200
ser.port = 'COM3'
ser.open()
exp.ser = serial.Serial('COM3', baudrate=115200, bytesize=serial.EIGHTBITS)
exp.ser.write(chr(1))
exp.cleanup_functions.append(exp.ser.close)

The following inline script is put in during the practice_sequence and is meant to send the trigger when the stimulus is presented:

exp.ser.write(chr(1))

Running this gives me the error in the title. I have also thought that perhaps I need to implement the sample python code provided by Cedrus, but this gives me an even more complicated error. running the following script:

import pyxid

# get a list of all attached XID devices
devices = pyxid.get_xid_devices()

dev = devices[0] # get the first device to use
if dev.is_response_device():
    dev.reset_base_timer()
    dev.reset_rt_timer()

    while True:
        dev.poll_for_response()
        if dev.response_queue_size() > 0:
            response = dev.get_next_response()
            # do something with the response

The line that seems to be giving me trouble is dev = devices[0] as I get the error message IndexError: list index out of range. If anyone has any idea as to how I can approach these errors I'd appreciate it as google hasn't given me a great solution yet.

Comments

  • Hi Flint,

    I suspect that both errors (from serial and pyxid) reflect the same underlying problem, namely that the device is simply not there. If you go into the Device Manager of Windows, you can see a list of all COM ports. Is COM3 there? I suspect not.

    If not, then getting your system to recognize the button box to begin with is the first step. Do you need to install drivers for it? Is there some hardware issue with the device or the cable?

    Cheers!
    Sebastiaan

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • Hello Sebastiaan,

    Thank you for taking the time to respond. I have checked device manager and made sure that COM3 is the StimTracker Duo so that is not the problem, it is under Ports as USB Serial Port(COM3). However, there is a USB driver that CEDRUS links to in their website and I have also already downloaded that. Perhaps the problem is where I have placed the pyxid folder? after downloading the file from github I placed the pyxid-master folder into the same folder that holds OpenSesame. I am not sure if this was the correct protocol. Similarly, I have also downloaded and installed pyserial-master from github and placed it into the same folder containing OpenSesame. At fist the problem was that it could not find pyxid so I copied the folder inside of the pyxid-master folder, called pyxid and brought it up to the main OpenSesame folder, as that did not work I then attempted to rename pyxid-master to simply pyxid, but that too gave me the same error of list index out of range.

    I have tried running a simpler script (such as the one described here: http://forum.cogsci.nl/index.php?p=/discussion/734/open-serial-port-trigger-for-eeg/p1) for activating a serial port and sending triggers, and while the experiment ran just fine, the triggers were not sent so I do believe the sample python code provided by CEDRUS will be essential for getting the triggers to send.

    After speaking to someone from NIRx they gave us a sample code that works with psychopy, but I figured it might work for OpenSesame as well so I gave it a try.

    import pyxid
    import time

    devices = pyxid.get_xid_devices()

    assert len(devices) > 0

    d = devices[0]
    d.init_device()
    d.set_pulse_duration(1000)

    Then to actually send the triggers it would be:

    d.activate_line(lines=[6])

    Unfortunately, I get an Assertion Error for assert len(devices) > 0. If I delete assert, then I get and error
    with d = devices[0] and once again get the list index out of range error.
    Using a simpler script that doesn't use pyxid, I have tried your suggestion from a different thread where you suggest printing something to the debug window after sending the trigger.

    exp.trigger.write(chr(1))
    print 'I just sent trigger 1! (or did I ... ?)'

    The problem I run into here is that I get a NameError: name 'exp' is not defined.
    I am sorry if this is a bit too long I just wanted to give as much information as possible, but I truly appreciate your help.

    Best,
    FlintMarco

  • The assert error means it cannot find the CEDRUS device so it might be a bug in the pyxid package or in your driver installation. The NameError: name 'exp' is not defined can be solved by using var.experiment.trigger instead of exp.trigger.

  • Hi _Bob,

    I uninstalled and reinstalled the driver and deleted and re downloaded the pyxid package. Using the 'init' script you suggested:

    import serial
    var.experiment.serial_port = serial.Serial('COM3', baudrate=115200, bytesize=serial.EIGHTBITS)
    var.experiment.cleanup_functions.append(var.serial_port.close())

    'send trigger script':

    var.experiment.serial_port.write(chr(1))

    Here is where I ran into the error: SerialException: Attempting to use a port that is not open
    If I remove the line "var.experiment.cleanup_functions.append(var.serial_port.close())" and place it as inline script at the end of the experiment, then the whole experiment will run but will give the error:
    TypeError: 'NoneType' object is not callable.
    Triggers were not sent in this situation either even though it ran the whole experiment.

    Now if I try to implement the pyxid script above import serial I initially got the error:
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position 0: ordinal not in range(128)
    But, every time after that I receive the same error: list index out of range.
    I suspect something in my laptop is giving me issues so I have decided to try downloading OpenSesame on a different computer in which I have successfully sent triggers using E-Prime. If I continue having the same issues it will definitely be an error in my code. I will update immediately if I have success using a different computer. I would still appreciate any suggestions you may have regarding my latest error codes.

    Many thanks,
    FlintMarco

  • Unfortunately, switching computers did not make a difference as I continued to get the same error codes. I even accounted for the switch in COM ports ('COM2' in this case) in this other computer by using device manager. I tried many iterations of my script, but each had the same errors as before. Mainly:

    UnicodeDecodeError: 'ascii' codec can't decode byte 0xf0 in position 0: ordinal not in range(128)

    UnicodeDecodeError: 'ascii' codec can't decode byte 0xf0 in position 1: ordinal not in range(128)

    SerialException: could not open port 'COM2': WindowsError(5, 'Access is denied.')

    SerialException: Attempting to use a port that is not open

    I have included a few screenshots if they are any help visualizing my experiment:


    The UnicodeDecodeError is exclusive to the import pyxid scripts.
    SerialException is exclusive to import serial scripts.

  • Hi FlintMarco,

    There are several issues mixed up here. Rather than posting them all here without any further information, let's tackle them one at a time.

    This:

    import serial
    var.experiment.serial_port = serial.Serial('COM3', baudrate=115200, bytesize=serial.EIGHTBITS)
    var.experiment.cleanup_functions.append(var.serial_port.close())
    

    Doesn't work, because you're calling var.serial_port.close(), and appending the return value of that function to the cleanup_functions list. Do you see how this works?

    What you want to do instead is append the function itself to the cleanup_functions, without calling (()) it:

    import serial
    var.experiment.serial_port = serial.Serial('COM3', baudrate=115200, bytesize=serial.EIGHTBITS)
    var.experiment.cleanup_functions.append(var.serial_port.close)
    

    So let's start by fixing this. Then you'll probably run into some issue. Post this issue here, including the full traceback that you get, and the part of the script that triggers the error. Baby steps!

    Cheers!
    Sebastiaan

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • Hey Sebastiaan,

    I see what you're saying. I have now modified the script to append the function itself just like your example. The send_trigger_script inserted into the practice_sequence1 looks as follows:

    var.experiment.serial_port.write(chr(1))

    The experiment runs without any errors, but it does not send any triggers at all.

    If I unplug the StimTracker and try to run the experiment I get the error:
    SerialException: could not open port 'COM3': WindowsError(2, 'The system cannot find the file specified.')
    This would mean that OpenSesame is aware of the StimTracker, but something must be missing in my code because the trigger isn't being sent. Attached is an Image with the traceback:

    Perhaps I have made an error with my "Run if" statements in the practice sequence? I will attach a screenshot of this as well, I have it set to run the send trigger script when the stimulus is heard:

    Best,
    FlintMarco

  • Hi FlintMarco,

    Let's think about how to debug this issue: The most obvious possibility is that the trigger code is simply not executed. So how could you find this out?

    • Add a print("Sending trigger!") line to the code that also sends a trigger. This will print "Sending trigger!" to the debug window, allowing you to see whether this piece of code is executed.
    • If you find that the trigger code is not executed, then this is probably because the Run If statement is always false. To determine this, you could remove the Run If statement, and see if the trigger code is now executed.
    • If you find that the trigger code is now executed, you know that the Run If statement is the culprit, so you'd take a good look at what might be wrong there.

    Do you see what I mean? Try to systematically pin down where the problem lies, and then you'll figure it out!

    Cheers,
    Sebastiaan

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • Hi Sebastiaan,

    As always, thank you for your help. I added the print("Sending trigger!") line as you suggested and interestingly enough it would seem the trigger code is sent:

    Unfortunately, that does not appear to be the case as the StimTracker should light up each time a trigger is sent and so should the m-pod. The other computer shows no records of receiving the event markers either. I will continue to try and systematically pin down where the problem lies as you have suggested and will update if I have any more helpful information.

    Best,
    FlintMarco

  • edited July 11

    Looking at the set up for E-Prime the code seems to have many settings and intricacies to it. I figured perhaps there is something I can extract from it to help me activate the triggers using OpenSesame.
    The following is E-Basic script for sending triggers:

    Dim integerVersionOfAttribute as Integer
    Dim millisecondsDuration as Integer
    Dim bytesThatMakePulseConfigCommand(5) as Integer
    Dim bytesToRaiseStimTrackerLines(3) as Integer
    Dim flagValueForDisablingPulse as Integer
    
    millisecondsDuration = 30
    
    ​‘ Just for fun, we will turn pulse OFF for the last few trials
    flagValueForDisablingPulse = 16
    
    ​‘ We use “Context.GetAttrib” to get the
    ‘ current value of “StimVal” from the StimList.
    ‘ (Refer to E-Basic help regarding “Context.GetAttrib”)
    Debug.Print “atrrib is: “ & c.GetAttrib(“StimVal”)
    integerVersionOfAttribute = CInt(c.GetAttrib(“StimVal”))
    
    ​‘ Just for fun, we will turn pulse OFF for the last few trials
    if integerVersionOfAttribute = flagValueForDisablingPulse then
    millisecondsDuration = 0
    end if
    
    ​if integerVersionOfAttribute = 1 or integerVersionOfAttribute = flagValueForDisablingPulse then
    
    ​‘ Here is where we construct the byte sequence
    ‘ that forms a StimTracker Event Marker command.
    ‘ (Refer to http://cedrus.com/pix/stimtracker/tn1450_st_commands.htm)
    bytesThatMakePulseConfigCommand(0) = 109 ‘ ascii m
    bytesThatMakePulseConfigCommand(1) = 112 ‘ ascii p
    bytesThatMakePulseConfigCommand(2) = millisecondsDuration
    bytesThatMakePulseConfigCommand(3) = 0
    bytesThatMakePulseConfigCommand(4) = 0
    bytesThatMakePulseConfigCommand(5) = 0
    
    ​
       ‘ (E-Studio generates the following script when the serial device is
    ‘ added to the experiment: Dim Serial As SerialDevice).
    ‘ Send the command to StimTracker!
        Serial.WriteBytes bytesThatMakePulseConfigCommand
    
    ​end if
    
    ‘ Make the command to zero out (clear) all lines
    bytesToRaiseStimTrackerLines(0) = 109 ‘ ascii m
    bytesToRaiseStimTrackerLines(1) = 104 ‘ ascii h
    bytesToRaiseStimTrackerLines(2) = integerVersionOfAttribute
    bytesToRaiseStimTrackerLines(3) = 0   ‘ currently ignored by StimTracker
    
    ​Serial.WriteBytes bytesToRaiseStimTrackerLines
    

    Even though it is not in Python I figured it wouldn't hurt to ask for advice since I am currently at a standstill with my current set up. The experiment functions as normal and indicates triggers are being sent, but there is no recorded evidence that this is the case. I must still be missing something for this to work.

  • edited July 11

    One thing that comes to mind is that the triggers are only registered when a change occurs. With a parallel port that's certainly the case. With a serial it doesn't need to be, but perhaps they mimic that behavior. So what happens if you do:

    exp.serial_port.write('a')
    exp.serial_port.write('b')
    

    Does that register?

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • Hey Sebastiaan,

    I tried your suggestion two different ways. First with the latest set up of:
    import serial
    var.experiment.serial_port = serial.Serial('COM3', baudrate=115200, bytesize=serial.EIGHTBITS)
    var.experiment.cleanup_functions.append(var.serial_port.close)

    Then I added your script:

    and received the following error:

    Attempting to use a port that is not open.

    I then went back to an earlier version of the set up script that seemed a bit more fitting:

    And it ran the whole experiment and it printed that it sent the triggers in the debug window, but again did not send any triggers:

    It seems I am in a bit of a tricky situation and feel I have exhausted my resources. I feel like it is definitely possible to use this piece of hardware to send triggers through OpenSesame I just don't know what I could possibly be missing besides that pyxid script.

    Thank you for taking the time to help me through this difficult situation even if all we hit are dead ends. I will continue trying different combinations and hope something works.

    Best,
    FlintMarco

  • Hi,

    After taking a closer look at the E-Prime and pyxid code, I'd say that the stimtracker uses a specific protocol. That is, you cannot send random bytes and expect the device to respond to that, but rather you need to send a specific byte sequence that forms a command that the stimtracker responds to. Does that make sense?

    This protocol is no doubt documented somewhere by Cedrus. Alternatively, you can try to figure out what they're doing in pyxid:

    Cheers!
    Sebastiaan

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • Hello,

    I see what you mean about a specific byte sequence that forms a command. I found an interesting excerpt from the link you provided.:

    Try to determine if we have a valid packet. Our options
    are limited; here is what we look for:
    a. The first byte must be the letter 'k'
    b. Bits 0-3 of the second byte indicate the port number.
    Lumina and RB-x30 models use only bits 0 and 1; SV-1 uses
    only bits 1 and 2. We check that the two remaining bits are
    zero.
    c. The remaining four bytes provide the reaction time.
    Here, we'll assume that the RT will never exceed 4.66
    hours :-) and verify that the last byte is set to 0.
    Refer to: http://www.cedrus.com/xid/protocols.htm

    The hardware they are referring to here are two response pads and a voice key; therefore I am not sure if the byte sequence would be similar for the StimTracker. The link they provide is not found, but: https://cedrus.com/support/stimtracker/tn1950_st_commands.htm
    seems to be similar.

    Since we are looking for a specific byte sequence I remembered using RealTerm to test the triggers by sending Event Codes myself and it worked just fine in that setting. The way they set it up:

    A pulse can be sent using the mh command. 255 different event codes can be produced by sending pulses on up to 8 lines at the same time.

    Using mh is slightly more complicated than using _d1 because it involves sending a binary value to indicate which of lines the pulse should be sent on. In this context, “binary value” means that the byte of information cannot be easily represented by a text character.

    As described in the StimTracker Software Commands page, the entire command consists of four bytes:

    •Letter m, which corresponds to number 109 (its ACSII value)

    •Letter h, which corresponds to number 104

    •A number indicating which lines we want to send the pulse on. For testing purposes, we will send value 255 to send a pulse on all eight lines. This makes it easier to see things on your EEG, eye tracker, or other recording software.

    •The last number is reserved for future use and should always be 0.

    Continuing where we left off in RealTerm’s Send tab:

    •Type “109 104 255 0” in the edit field shown.

    Looking back at the E-Prime code I can see that the numbers 109 and 104 once again had important roles for sending the event codes. The problem is I don't really have any idea how I would incorporate this into the inline scripts on OpenSesame. Would you happen to have any suggestions?

    Thank you for taking the time to respond and providing helpful advice.

    Best,
    FlintMarco

  • Hi,

    Python's serial module expects byte strings: strings of characters that each reflects a single byte. The chr() function will convert a byte value to its corresponding character. So sending 109 104 255 0 would be:

    exp.serial_port.write(chr(109) + chr(104) + chr(255) + chr(0))
    

    Cheers!
    Sebastiaan

    Thanked by 1FlintMarco

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • Sebastiaan you magnificent genius!

    I have successfully gotten OpenSesame to send a trigger! At the moment it only sends one that is constant throughout the experiment, but I had a similar issue when I first started working on E-Prime and It's only a matter of making a few changes to the experiment and adding some inline script. The important thing is that we have finally gotten OpenSesame to send a trigger with the StimTracker!

    If I run in to any roadblocks I'll be sure to ask again, but at this point we are past the toughest challenge and it should be a lot easier moving forward. Once I have it functioning smoothly I'll update this thread with some screenshots so that it can help other users in similar situations.

    Many thanks,
    Flint Marco

  • I have now successfully set up my experiment to send triggers when the stimuli is presented; in my case it is audio. I will post some screenshots to hopefully make it easier for anyone who works with a CEDRUS StimTracker Duo to set up.

    First, you should begin the experiment with an inline script at the top of the experiment that is used for setting up the serial device that will be used to send triggers during the experiment. In my case I renamed it serial_setup and it looks like this:

    Next I will show you what my specific block loop looks like for my experiment. Yours will look different, but it is what will be used when you want to send the trigger. In my case I want it sent each time Mandarin tone is presented for the practice sequence:

    Now we will take a look at the send_trigger_script. It is fairly simple and includes the byte sequence that the StimTracker Duo is able to recognize. It should be noted that when this event code is sent it will show up as the number "15" on the receiving computer. While this is fine for my experiment since I just want to receive an event marker when one stimulus is presented, other experiments requiring more than one event marker will likely require you to change the number "255" in the script (likely a number between 1-255). I have not yet tested this as I am still finishing up my project. one send_trigger_script should be placed in every sequence and for every trigger you wish to send. I use unlinked copies to expedite the process:

    If we look at the sequence in which the send_trigger_script is placed you will see I have them synced to send at the same time the audio is heard:

    In my pretest sequence I have pairs of words grouped for counterbalancing, but I will show you how I use the send_trigger_script within them so that you can have a clearer understanding:

    Finally, I just want to note that you might have seen an inline script in my experiment called shuffle_script and wondered if it has anything to do with the triggers, it does not. I have used that script to shuffle the blocks so that they are presented in a randomized order for counterbalancing purposes. If you wish to find our more about this you can do so by clicking on this discussion:
    http://forum.cogsci.nl/index.php?p=/discussion/2580/how-to-randomize-blocks-in-opensesame-a-solution-not-a-question

    There is likely a way to make the trigger sending process more efficient and cleaner without having so much inline script, but for the time being this serves its purpose and isn't too difficult to set up.

    Special thanks to Sebastiaan and _Bob for providing insightful comments throughout the process.

    Best,
    FlintMarco

    Thanked by 1eduard
Sign In or Register to comment.