Holcombe:ButtonBox

From OpenWetWare

(Difference between revisions)
Jump to: navigation, search
Current revision (19:27, 17 November 2010) (view source)
(Problems)
 
(20 intermediate revisions not shown.)
Line 1: Line 1:
{{Template:Holcombe}}
{{Template:Holcombe}}
 +
Iolabs device [http://www.iolab.co.uk/products/index.html] might be better than the Cedrus box detailed below. And there is PsychoPy code available for the Iolabs device.
== The box ==
== The box ==
-
The button-box we have used for our "sensorimotor synchronisation" experiments is a Cedrus RB-730 response pad. It is on loan from Nenad and the IT office. It connects to the computer via a USB port, and is controlled by the computer as a serial port (or something like that). [http://www.cedrus.com/responsepads/rb730.htm]. To work, you must install a driver from the Cedrus website here:[http://www.cedrus.com/support/rb_series/tn1057_install_rbx30_macos_x.htm], and surprisingly, I also had to install a USB to serial port driver from a company called Keyspan: [http://www.keyspan.com/downloads/homepage_pn_usa28x.spml]. I used the driver for their USA-28x product.  
+
The button-box we have used for our "sensorimotor synchronisation" experiments is a Cedrus RB-730 response pad. It is on loan from Nenad and the IT office. It connects to the computer via a USB port, and is controlled by the computer as a serial port (or something like that). [http://www.cedrus.com/responsepads/rb730.htm]. To work, you must install a driver from the Cedrus website here:[http://www.cedrus.com/support/rb_series/tn1057_install_rbx30_macos_x.htm], and surprisingly, I also had to install a USB to serial port driver called keyspanUSAdrvr from Tripplite, formerly Keyspan: [http://www.tripplite.com/en/support/downloads/driver-firmware-downloads.cfm?start=16&txtType=31&txtOS_Type=74]. I used the driver for their USA-28x product.  
I had all of the little dip switches in back in the down position.  
I had all of the little dip switches in back in the down position.  
-
== Programming with the box ==
+
== Basic programming with the box ==
-
To use the button-box with a python program, you need these lines at the start of your code:  
+
Oh. the below was written before I saw Jon Pierce has put [http://www.psychopy.org/reference/psychopy.hardware-pysrc.html code supporting the Cedrus in psychopy], but i think his code is vulnerable to the same problems
 +
 
 +
To use the button-box with a python program, we put dipswitches 1 and 2 down so the box used XID (eXperimental Interface Device) communication protocol, which provides timestamps of button press times. Then you need these lines at the start of your code:  
 +
 
  from psychopy import serial
  from psychopy import serial
  from struct import unpack
  from struct import unpack
Line 25: Line 29:
To get out the data about the button press or release:  
To get out the data about the button press or release:  
  response=s.read(6)
  response=s.read(6)
-
  formatted_response=unpack('<cBI>', response)
+
  formatted_response=unpack('<cBI', response) #'<' means little-endian order, 'c' means char, 'B' means unsigned char, 'I' means unsigned int
-
  time_in_msec = formatted_response[2]
+
  time_in_msec = formatted_response[2] #unsigned int, long?
Example code to increment a counter of presses and button-releases:
Example code to increment a counter of presses and button-releases:
-
  pressEventCodes = (48,80,112,144,176,208) #6 buttons from left to right
+
  pressEventCodes = (48,80,112,144,176,208,240) #7 buttons from left to right
-
  releaseEventCodes=(32,64,96,128,160,192) #6 buttons from left to right
+
  releaseEventCodes=(32,64,96,128,160,192,224) #7 buttons from left to right
 +
lineInCodes=(209) #dunno the others
  eventType = formatted_response[1]
  eventType = formatted_response[1]
  if (eventType in pressEventCodes):
  if (eventType in pressEventCodes):
-
    pressEvents += 1
+
  pressEvents += 1; print 'button PRESSED'
  elif (eventType in releaseEventCodes):
  elif (eventType in releaseEventCodes):
-
    releaseEvents += 1
+
  releaseEvents += 1; print 'button RELEASED'
 +
elif (eventType in lineInCodes):
 +
  lineEvents += 1; print 'line in TRIGGERED'
 +
else:
 +
  print "WARNING! unexpected buttonbox event ", eventType
 +
  print "formatted_response =", formatted_response, ", eventType = ",eventType, ",time=",formatted_response[2]
 +
 
 +
== Sending an external trigger to the box ==
 +
Box needs to be in General Purpose mode, http://community.cedrus.com/showthread.php?t=717
 +
http://docs.psychtoolbox.org/CedrusResponseBox
 +
 
 +
s.write('a10')  #switch button box to general purpose mode i hope
 +
s.write('a11') #or switch button box to reflective mode i hope
 +
s.write('_a1'); #ask what mode you're in
 +
msg= s.read(6) # get answer of what mode it's in
 +
numWaiting= s.inWaiting(); print 'numWaiting=',numWaiting
 +
msg=s.read(numWaiting)
 +
print msg
 +
 
 +
[http://www.cedrus.com/xid/a4_set_io_direction.htm Input/output lines have to be set] so that touching the wires together will register an event in the box. Check their current status:
 +
 
 +
s.write('_a4'); time.sleep(.2) #The XID device returns _a4 followed by the bit mask.
 +
#get number of chars in the receive buffer
 +
numWaiting= s.inWaiting(); print 'numWaiting=',numWaiting  #should be 4
 +
#_a4 will be first 3 bytes, so read those
 +
msg=s.read(3); print msg
 +
#last byte  is the bitmask
 +
linesBitmask=s.read(1)
 +
linesStatus=unpack('B',linesBitmask)  #binary
 +
linesStatus=linesStatus[0] #dunno why but unpack returns tuple with empty second member
 +
binary = lambda i, c = (lambda i, c: i and (c(i >> 1, c) + str(i & 1)) or ''): c(i, c) #one-line function to convert to binary format
 +
print (binary(linesStatus)) #the status of each line, 1 being input, 0 output
 +
 
 +
[http://community.cedrus.com/showthread.php?t=300 Info about messaging format] e.g. ASCII vs. binary, [http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-01/3515.html printing binary]
 +
 
 +
Retrieve a line triggered event from the box. We trigger a photodetector by drawing on the screen at a known time, and have the photodetector send an event into the box.
 +
 
 +
 
 +
=== Problems ===
 +
UPDATE: regarding the below problems, it's possible they're all avoided by using the new code that's since been developed at https://github.com/cedrus-opensource/pyxid
 +
Hisham Abboud of Cedrus says "the code detects button presses and can retrieve the timestamp, but we're currently setting the timestamp to zero because there is some drift in the hardware itself.  We will update the code once we know exactly what the time drift is and can compensate for it. The code is also capable of handling two devices at the same time, e.g. a response pad and an SV-1 voice key."
 +
 
 +
* Any commands sent to button box occasionally needs to be done more than once before it takes. Box unreliable in this way (although apparently not a problem with code at github link above). Sometimes I have to restart code several times before box will listen
 +
* If send too long a stream of events (e.g. line in left on), box seems to become unresponsive
 +
* Sam T tried to do an experiment collecting multiple responses per trial, but seemed that the Cedrus RB-730 measured multiple responses inaccurately, accumulating a timing error of roughly 500ms over a 20-tap-piloting trial.
-
Sam T tried to do an experiment collecting multiple responses per trial, but seemed that the Cedrus RB-730 measured multiple responses inaccurately, accumulating a timing error of roughly 500ms over a 20-tap-piloting trial.
+
== Timing Precision ==
-
== Precision ==
+
A file summarizing our tests of the precision of the timing is here [[media:buttonBoxPrecisionSummary.doc]]
A file summarizing our tests of the precision of the timing is here [[media:buttonBoxPrecisionSummary.doc]]
-
For on-line, during-experiment timing verification we would like to trigger a photodetector by drawing on the screen at a known time, and have the photodetector press a button on the button box using what? an "Active Switch Closure" lead?
+
For on-line, during-experiment timing verification we trigger a photodetector by drawing on the screen at a known time, and have the photodetector trigger an event in the button box.
Another way to verify timing and precision would be with [http://www.blackboxtoolkit.co.uk/ this expensive device].
Another way to verify timing and precision would be with [http://www.blackboxtoolkit.co.uk/ this expensive device].

Current revision

Members

Alex Holcombe
Sarah McIntyre
Fahed Jbarah
• Shih-Yu Lo
• Patrick Goodbourn
Lizzy Nguyen
Alumni


Technical

Skills Checklist
Python Programming
Psychopy/VisionEgg Installation Notes
R analysis,plot,stats
Statistics
Buttonbox
Verifying timing
Programming Cheat Sheets


Iolabs device [1] might be better than the Cedrus box detailed below. And there is PsychoPy code available for the Iolabs device.

The box

The button-box we have used for our "sensorimotor synchronisation" experiments is a Cedrus RB-730 response pad. It is on loan from Nenad and the IT office. It connects to the computer via a USB port, and is controlled by the computer as a serial port (or something like that). [2]. To work, you must install a driver from the Cedrus website here:[3], and surprisingly, I also had to install a USB to serial port driver called keyspanUSAdrvr from Tripplite, formerly Keyspan: [4]. I used the driver for their USA-28x product.

I had all of the little dip switches in back in the down position.

Basic programming with the box

Oh. the below was written before I saw Jon Pierce has put code supporting the Cedrus in psychopy, but i think his code is vulnerable to the same problems

To use the button-box with a python program, we put dipswitches 1 and 2 down so the box used XID (eXperimental Interface Device) communication protocol, which provides timestamps of button press times. Then you need these lines at the start of your code:

from psychopy import serial
from struct import unpack

And to initialize the box:

s = serial.Serial('/dev/tty.usbserial-FT3ERKOD', 115200)

To erase all info in the box about previous button-presses:

s.flushInput()

To reset the button-box internal clock to 0:

s.write('e5')

Critically, the box records both the press and release of the button and sends a signal in exactly the same way (at least how it's set up now). To avoid this, you need to record both separately. In the attached file that shows how we tested the precision of the box relative to the computer clock, you can see how I did this. But basically, to check if a key has been pressed or released, you check this:

if s.inWaiting() > 0:   ......

To get out the data about the button press or release:

response=s.read(6)
formatted_response=unpack('<cBI', response) #'<' means little-endian order, 'c' means char, 'B' means unsigned char, 'I' means unsigned int
time_in_msec = formatted_response[2] #unsigned int, long?

Example code to increment a counter of presses and button-releases:

pressEventCodes = (48,80,112,144,176,208,240) #7 buttons from left to right
releaseEventCodes=(32,64,96,128,160,192,224) #7 buttons from left to right
lineInCodes=(209) #dunno the others
eventType = formatted_response[1]
if (eventType in pressEventCodes):
 pressEvents += 1; print 'button PRESSED'
elif (eventType in releaseEventCodes):
 releaseEvents += 1; print 'button RELEASED'
elif (eventType in lineInCodes):
 lineEvents += 1; print 'line in TRIGGERED'
else: 
 print "WARNING! unexpected buttonbox event ", eventType
 print "formatted_response =", formatted_response, ", eventType = ",eventType, ",time=",formatted_response[2]

Sending an external trigger to the box

Box needs to be in General Purpose mode, http://community.cedrus.com/showthread.php?t=717 http://docs.psychtoolbox.org/CedrusResponseBox

s.write('a10')  #switch button box to general purpose mode i hope
s.write('a11') #or switch button box to reflective mode i hope
s.write('_a1'); #ask what mode you're in
msg= s.read(6) # get answer of what mode it's in
numWaiting= s.inWaiting(); print 'numWaiting=',numWaiting
msg=s.read(numWaiting)
print msg

Input/output lines have to be set so that touching the wires together will register an event in the box. Check their current status:

s.write('_a4'); time.sleep(.2) #The XID device returns _a4 followed by the bit mask.
#get number of chars in the receive buffer
numWaiting= s.inWaiting(); print 'numWaiting=',numWaiting  #should be 4
#_a4 will be first 3 bytes, so read those
msg=s.read(3); print msg
#last byte  is the bitmask
linesBitmask=s.read(1)
linesStatus=unpack('B',linesBitmask)  #binary 
linesStatus=linesStatus[0] #dunno why but unpack returns tuple with empty second member
binary = lambda i, c = (lambda i, c: i and (c(i >> 1, c) + str(i & 1)) or ): c(i, c) #one-line function to convert to binary format
print (binary(linesStatus)) #the status of each line, 1 being input, 0 output 

Info about messaging format e.g. ASCII vs. binary, printing binary

Retrieve a line triggered event from the box. We trigger a photodetector by drawing on the screen at a known time, and have the photodetector send an event into the box.


Problems

UPDATE: regarding the below problems, it's possible they're all avoided by using the new code that's since been developed at https://github.com/cedrus-opensource/pyxid Hisham Abboud of Cedrus says "the code detects button presses and can retrieve the timestamp, but we're currently setting the timestamp to zero because there is some drift in the hardware itself. We will update the code once we know exactly what the time drift is and can compensate for it. The code is also capable of handling two devices at the same time, e.g. a response pad and an SV-1 voice key."

  • Any commands sent to button box occasionally needs to be done more than once before it takes. Box unreliable in this way (although apparently not a problem with code at github link above). Sometimes I have to restart code several times before box will listen
  • If send too long a stream of events (e.g. line in left on), box seems to become unresponsive
  • Sam T tried to do an experiment collecting multiple responses per trial, but seemed that the Cedrus RB-730 measured multiple responses inaccurately, accumulating a timing error of roughly 500ms over a 20-tap-piloting trial.

Timing Precision

A file summarizing our tests of the precision of the timing is here media:buttonBoxPrecisionSummary.doc

For on-line, during-experiment timing verification we trigger a photodetector by drawing on the screen at a known time, and have the photodetector trigger an event in the button box.

Another way to verify timing and precision would be with this expensive device.
Views
Personal tools