Simple class for simplified pySerial interface for use in TTL event marking using e.g. PsychoPy.
Project description
digital-trigger
Easier sending of digital triggers/event markers/TTL signals using the Serial (pySerial) package with a Black Box Toolkit USB TTL module in Python and PsychoPy.
Install
pip install digital_trigger
Requires Python 3.8+ and pyserial (installed automatically).
In PsychoPy you can install packages in the Builder GUI by going to:
Tools > Plugins and packages manager > Packages > Open PIP terminal, and run pip install digital-trigger
To find COM port number:
- run the command
python -m serial.tools.list_ports -v - On Windows, open Device Manager, expand "Ports (COM & LPT)", unplug and replug to see which is your device
- On Mac run
ls /dev/cu.*in Terminal and look for something like /dev/cu.usbserial-XXXX or /dev/cu.usbmodemXXXX — use the cu.* name, not tty.*. - On Linux, run ls
/dev/ttyUSB* /dev/ttyACM*— USB-serial adapters are usuallyttyUSB0 - Arduino-style boards
ttyACM0; dmesg | tailright after plugging in shows the assigned name, and you may need to add yourself to the dialout group for permission.
Make sure your COM port is set up with a latency of 1ms (or lower) and Baudrate of 115200. This can be done under Device Manager > COM port > Advanced on Windows.
Usage
PsychoPy
Add the following in a 'custom code block' in the routine where your stimuli are presented. Note drag the Code Component so it sits below the stimulus component in the routine view, as PsychoPy runs them top to bottom. Consider managing the triggers using a 'trigger_line' variable in your conditions file.
Before Experiment
from digital_trigger import Trigger
port = Trigger('COM4', names=['cond_1', 'cond_2', 'stim_1', 'stim_2'])
Note you can only do this once per experiment. Don't do this in multiple code blocks in different routines or you will get a 'access/permission denied' error. Probably best to have a single code block jsut for this in your first routine even.
Begin Routine
trigger_opened = False
trigger_closed = False
Each Frame
# Open the line the instant the stimulus is drawn to the screen...
# Change 'image' to the name of your stimulus component
if image.status == STARTED and not trigger_opened:
win.callOnFlip(port.open, 'stim_1')
trigger_opened = True
# ...and close it the instant the stimulus is removed.
if image.status == FINISHED and not trigger_closed:
win.callOnFlip(port.close, 'stim_1')
trigger_closed = True
End Routine
if not trigger_closed:
port.close('stim_1')
End Experiment
port.stop()
Python
from digital_trigger import Trigger
port = Trigger('COM4', names=['cond_1', 'cond_2', 'stim_1', 'stim_2'])
port.open('stim_1') # turn a line on (by name)
port.open([1, 'cond_2']) # turn several on (numbers and names mixed)
port.close('stim_1') # turn one off
port.close_all() # turn everything off
port.open_lines() # index of open lines and prints the bitmask
port.status() # list of bools for each port if open or closed
port.stop() # reset all lines and close the port
Or as a context manager, which closes the port for you:
with Trigger('COM4', names=['stim_1']) as port:
port.open('stim_1')
Resources:
- https://www.blackboxtoolkit.com/support_usb_ttl_module.html
- https://www.blackboxtoolkit.com/docs/pdf/USBTTLv1r19.pdf
- https://psychopy.org/developers/pluginDevGuide.html#plugindevguide
Dev notes
build & upload
Setup venv:
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip build twine pytest
re-run build:
rm -rf dist # clear old builds so nothing stale is uploaded
python3 -m build
ls dist # confirm the new version number is shown
twine upload --repository testpypi dist/*
test
cd /tmp
rm -rf testenv
python3 -m venv testenv
source testenv/bin/activate
pip install -i https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
digital-trigger==0.1.2 # match the version
python -c "from digital_trigger import Trigger; t = Trigger(simulate=True, names=['stim_1']); t.open('stim_1'); print('ok', t.bitmask)"
deactivate
To install from the test repo on a new computer:
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ digital-trigger==0.1.2
Small script to check:
from digital_trigger import Trigger
t = Trigger(simulate=True, names=['stim_1'])
t.open('stim_1')
print('ok', t.bitmask)
release
cd /path/to/project # back to the project folder
twine upload dist/* # no --repository = real PyPI
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file digital_trigger-0.1.2.tar.gz.
File metadata
- Download URL: digital_trigger-0.1.2.tar.gz
- Upload date:
- Size: 5.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8a8f715fd57a1a4582a48fc65db105209a9053ece507774b47f114d86d81b4c
|
|
| MD5 |
c5c8227674c706ddbace9030eaf93b56
|
|
| BLAKE2b-256 |
69b545dd4a1fd39df03c7490d005dfcebf67e3fb9aee5228abe1f8ebe538fcae
|
File details
Details for the file digital_trigger-0.1.2-py3-none-any.whl.
File metadata
- Download URL: digital_trigger-0.1.2-py3-none-any.whl
- Upload date:
- Size: 5.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ae30ce1caced3ebf5aea588f93f755a028db45fe1c9125330f69682cd299b6a
|
|
| MD5 |
8656be0bc9260cd273c2591cc9c08ae7
|
|
| BLAKE2b-256 |
1be622dc3bba860fdba2c51372a837e3d94c8499a203468f0e2ff5f981dbaf45
|