Clean-up actions for PySerial

Hi Chris,

I’m a first-year PhD student in the JazLab designing an MWorks experiment that interfaces with an incremental rotary encoder. I’m currently calculating the angle of the rotary encoder’s shaft using an Arduino Uno, and then I send the measured angle from the board to MWorks using pySerial. I’ve currently made an implementation of a simplified task that seems to work pretty well, where the position of a stimulus is set by the angle of the encoder. However, whenever I arbitrarily stop the protocol (i.e., by hitting the red “X” button or closing the MWClient window) and subsequently reload the protocol, the rendered stimulus will “glitch” at seemingly random times. The position of the stimulus is set directly by the angle of the encoder shaft, so I suspect that there’s an issue with clean-up actions of reading from the serial connection.

When I hit the green “pause” button and then resume the protocol by hitting “play",” no glitching is induced. The glitching also goes away if I kill the MWServer and MWClient applications and then restart them.

Do you have any suggestions on how to fix this glitching behavior? I’ve attached my Python code that reads from my rotary encoder (rotary_read.py), a template MWEL file I’ve modified to run my “Rotate Circle” protocol (main_mworks.mwel), and a google drive link with two videos that show the rendering at the first time I load it (“good_rotary”) and at the second time after I hit the red button (“bad_rotary”). Let me know if you would like the Arduino code too.

Thank you,

Nikasha

main_mworks.mwel (5.6 KB)

rotary_read.py (6.0 KB)

System Information:

  • MacOS Sonoma 14.5
  • Macbook Pro, 16-inch, 2019
  • MWorks version 0.13
  • Python version 3.11.3
  • Arduino UNO

Hi Nikasha,

Thanks for sharing your code.

I suspect the problem arises because when you stop the experiment in the middle of a trial, the stop method of SerialAngleDevice is never called. Then, when you restart the experiment, you create and start a new SerialAngleDevice instance, at which point you have two such instances trying to read from the serial port and set the stimulus position.

Since you’re using MWorks 0.13, you can use exit actions to resolve the issue. First, move the line that creates the SerialAngleDevice outside of the trial. (There’s no reason to re-create it at the start of every trial, is there?) Then, immediately after that line, add on on_exit that stops the device. Something like this:

run_python_string('angle_dev = SerialAngleDevice(port="/dev/cu.usbmodem142301", baud=57600, var_name="angle")')
on_exit {
    // Ensure that the device stops even when the experiment is stopped
    // manually
    run_python_string('angle_dev.stop()')
}

trial {
    ...

You can still start and stop the device during each trial. Since you’ve implemented stop so that it can be called safely even if the device is already stopped, the exit action won’t affect the case where your experiment ends normally.

Please give this a try, and let me know if you run in to any issues.

Cheers,
Chris

Thank you so much! That worked like a charm.

Cheers,
Nikasha