psychopy.org | Reference | Downloads | Github

Using variable/parameter from Conditions file in Code component for value to be sent via serial port


#1

In the same way that a variable is being used to alter the presented stimulus on each loop repetition based on the Conditions file (“stimulusitem”), I would like to be able to transmit the corresponding value of another variable/parameter (“stimuluscode”). However, $stimuluscode doesn’t work in the Code component and a number of other things I’ve tried (based on various threads and discussions here and elsewhere) haven’t worked either.

This is in the Begin Experiment tab…

import serial
ser = serial.Serial('COM4', 9600, timeout=1)

And I’m using ser.write() to define the value to be sent. Ideally, it would just be ser.write($stimuluscode), but alas, it is not. I’ve looked around this forum and the wider interweb, but nothing I’ve tried has worked yet. My inkling is that I can’t directly access the conditions file values from Code without importing the contents of that conditions file in the code. My attempts to do this have failed too though.

Any help would be massively appreciated. :grin:


#2

Are you using Python 2 or 3? (Relevant because of the differences in unicode-types).

For Python 3, try:
ser.write(bytes([thisTrial["stimuluscode"]]))

Where “thisTrial” is the name of your loop.


#3

Thank you so much for your response. Alas, it has not worked for me. I tried adding another bracket at the end and removing the open bracket after bytes but still not luck and just get an error about the trial handler and __getitem__.

Thank you in advance for any further assistance. And I have Python 3.7.1 installed, but am using PsychoPy 1.9.3 on Windows 10 (if that makes any difference - apologies, I should have stated that in the first place).


#4

Ah, missed a bracket, good spot!

Could you post the exact error message?

To go back at the beginning, there are essentially three potential bottlenecks:

  1. PsychoPy does not generate the triggers correctly. You could check this with adding print statements every time you try to write to the port; so for example:
ser.write(bytes([thisTrial["stimuluscode"]]))
print(thisTrial["stimulus code"]) # To check if the correct value is found
print(bytes([thisTrial["stimuluscode"]])) # To check if the byte conversion is correct / necessary
  1. The trigger is not sent, which would have to do with your port-address and/or settings.

  2. The trigger is not read by the receiving hardware. What are you using exactly? Does it have a manual? Does it need a driver? What format does it expect? (Bytes)?


#5

When not trying to use a variable/reference for the data to be sent, it works absolutely fine e.g., just ser.write('\xfb').

[quote=“Cesco, post:4, topic:5745”]2. The trigger is not sent, which would have to do with your port-address and/or settings.
[/quote]
It does seem to be the case that the trigger is not sent, but because of the variable reference and PsychoPy not knowing what data to send.

[quote=“Cesco, post:4, topic:5745”]3. The trigger is not read by the receiving hardware. What are you using exactly? Does it have a manual? Does it need a driver? What format does it expect? (Bytes)?
[/quote]
As with [1], data sent individually is sent and received as expected and desired. It’s just this variable reference…

However, not replacing “thisTrial” with the name of my loop avoided the error and did send data! :smiley:
Unfortunately, not the data I was expecting e.g., instead of 1-9, it sent 49-57 and 91 and 93 on each transmission. I also tried the same but changed the values in the conditions file to hex values, which also worked, but sent even more values per transmission…

This leads me to ask… What format should the values be in the conditions file? I have tried “normal” integers (the 49-57 & 91&93 result) and hex as in the above example (which resulted in a stream of data being sent, but works when used directly in the code).

Thanks again SO MUCH for all your help. It is greatly appreciated. :grinning:
(As you can tell, I’m quite a beginner when it comes to Python and coding. :flushed:)


#6

I’m happy you’re getting there. I do not have a direct answer for your latest issue, as it has to do with encoding, and I’m still figuring out the “rules” of Python 3 types (coming from Python 2.7 myself). However, the values you report suggest that the receiving end of your setup decodes the triggers as decimal HTML (see this table where character 1 (U+0031 in unicode) matches decimal-numerical HTML encoding value 1).

Perhaps this is an option you can select on the receiving end, or you could encode the trigger itself. Maybe someone who is a bit more Python3 savvy can help you more precisely, but I imagine that it would be something like ser.write((str([thisTrial["stimuluscode"]]).encode('ascii', 'xmlcharrefreplace')). However, it probably encodes it to hexadecimal text.

If possible, I would stick with integers in your conditions file, if only to keep the experiment simple and transparent.


#7

Thank you so much for this. I had an inkling that there was some translation/class issue and I think you’ve nailed it.

I’ll test it more, try the other suggestions, and report back. Thanks again. :smiley:


#8

Thanks again, @Cesco. Your help was invaluable and ser.write(bytes([thisTrial["stimuluscode"]])) got me on the right track. It worked, but sent the (now) expected message plus a “91” and a “93” for the [ and ], respectively. Therefore, ser.write(bytes(thisTrial["stimuluscode"])) (i.e., same but with just one pair of square brackets) is what works for me and my purposes, sending just the data from the conditions file. :slight_smile:


#9

If anyone can shed any further light on how I can get values from the conditions.xlsx file to be sent and allow me to take advantage of (at least) the full 0-255 range of values, that would be absolutely amazing. So far, I’ve only been able to manage sending values corresponding to the decimal-numerical HTML encoded characters in the conditions file (e.g., 1 = 49), whereas I just want to be able to program the experiment to send 1 and the value 1 to be sent i.e., program/code in integers, hex, decimal, whatever, but the corresponding integer be sent.

I’ve tried using the suggested .encode, but it has not worked for me.

Thanks in advance. :relaxed:


#10

Glad you booked some progress. Again, what exactly (hardware / program) are you sending your triggers to?


#11

Apologies. I thought I’d included this in my initial post. Silly me.

At present, I’m trying to communicate with COBI Studio (fNIR Devices’ fNIRS data acquisition software), but will also be wanting to communicate with BIOPAC’s BSL 4 and AcqKnowledge 5 and, in future, EEG software (e.g., ANT Neuro’s data acquisition software). They all accept standard 8-bit 0-255 data input as event markers/triggers to my knowledge and I’m intending to keep it as standard and straightforward as possible due to the systems being used by various university staff and students.

I’m hoping to have time again later to further test and try the encode function and will report back.

Thanks again! :slight_smile:


#12

Thanks. Seems fairly standard, so still not sure why it reads (/ is sent) a different format; like you stated, ser.write('\xfb') works.

For debugging (and testing your future communication protocols) maybe try a simple script that sends all the triggers, one every 500 ms; so something like:*

import serial
from psychopy import core

ser = serial.Serial("COM4") # Or whatever your port is.

for i in range (256):
    ser.write(i)
    # If not working, replace with ser.write(bytes(i)) and other options to see which encoding works best. 
    print("sending trigger ", i)
    core.wait(0.5) # In seconds, speed up or slow down as you wish.
    ser.write(0)
    # Or ser.write(bytes(0)) etc...

*Typed on the fly, so errors are plausible :wink: