Use of a USB-connected potentiometer with Psychopy

Hello, I use a computerized visual analogue scale (COVAS) potentiometer device (Medoc) to collect pain ratings during experiments. It attaches via an USB port to the computer. The webpage for the device is this one: https://www.medoc-web.com/covas

I would like to use it with a slider component so participants can rate pain by sliding the COVAS’ knob between left and right positions, and have the knob position to change the slider’s marker accordingly. I was wondering if you have any suggestions on how to use this device with Psychopy.

Thank you.

You’ll need to provide us the details from the manufacturer about the communication protocol the device uses. eg does it emulate the ancient but very common serial port protocol over USB? If so, what do the values look like?

If it isn’t using a generic protocol like serial, does it have its own driver? Is there a Python API for it?

It’s quite possible that PsychoPy can listen to the device, and if so, controlling a slider in real time would be straightforward. But we need more technical detail to point you in the right direction. The place to start is with the manufacturer (ideally this info would be provided in your technical user manual) or their support community, and then come back here with the details.

I got some information about the device. It is basically a joystick controller, with a knob that only moves left and right with no vertical movement allowed. (BU0836-LC Interface, http://www.leobodnar.com/shop/index.php?main_page=product_info&products_id=183.

However when I tried to add a joystick component in a barebone psychopy script, it was not able to recognize it and the script ended up using “WARNING joystick_0: Using keyboard+mouse emulation ‘ctrl’ + ‘Alt’ + digit.” See psychopy script attached.
R00Aim1_V2_EPM-COVAS_2022-08-01.psyexp (15.8 KB)

To test if the device is recognized by the computer, I used an example python script and it works fine to detect the position of the device’s knob. Any pointers on why psychopy is not detecting the device as a joystick?

The example python code to get the device’s knob position is below:

import pygame


# Define some colors.
BLACK = pygame.Color('black')
WHITE = pygame.Color('white')


# This is a simple class that will help us print to the screen.
# It has nothing to do with the joysticks, just outputting the
# information.
class TextPrint(object):
    def __init__(self):
        self.reset()
        self.font = pygame.font.Font(None, 20)

    def tprint(self, screen, textString):
        textBitmap = self.font.render(textString, True, BLACK)
        screen.blit(textBitmap, (self.x, self.y))
        self.y += self.line_height

    def reset(self):
        self.x = 10
        self.y = 10
        self.line_height = 15

    def indent(self):
        self.x += 10

    def unindent(self):
        self.x -= 10


pygame.init()

# Set the width and height of the screen (width, height).
screen = pygame.display.set_mode((500, 500))

pygame.display.set_caption("My Game")

# Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates.
clock = pygame.time.Clock()

# Initialize the joysticks.
pygame.joystick.init()

# Get ready to print.
textPrint = TextPrint()

# -------- Main Program Loop -----------
while not done:
    #
    # EVENT PROCESSING STEP
    #
    # Possible joystick actions: JOYAXISMOTION, JOYBALLMOTION, JOYBUTTONDOWN,
    # JOYBUTTONUP, JOYHATMOTION
    for event in pygame.event.get(): # User did something.
        if event.type == pygame.QUIT: # If user clicked close.
            done = True # Flag that we are done so we exit this loop.
        elif event.type == pygame.JOYBUTTONDOWN:
            print("Joystick button pressed.")
        elif event.type == pygame.JOYBUTTONUP:
            print("Joystick button released.")

    #
    # DRAWING STEP
    #
    # First, clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.
    screen.fill(WHITE)
    textPrint.reset()

    # Get count of joysticks.
    joystick_count = pygame.joystick.get_count()

    textPrint.tprint(screen, "Number of joysticks: {}".format(joystick_count))
    textPrint.indent()

    # For each joystick:
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()

        textPrint.tprint(screen, "Joystick {}".format(i))
        textPrint.indent()

        # Get the name from the OS for the controller/joystick.
        name = joystick.get_name()
        textPrint.tprint(screen, "Joystick name: {}".format(name))

        # Usually axis run in pairs, up/down for one, and left/right for
        # the other.
        axes = joystick.get_numaxes()
        #textPrint.tprint(screen, "Number of axes: {}".format(axes))
        #textPrint.indent()

        #for i in range(axes):
        axis = joystick.get_axis(0)
        textPrint.tprint(screen, "Axis {} value: {:>6.3f}".format(i, axis))
        textPrint.unindent()

        #buttons = joystick.get_numbuttons()
       # textPrint.tprint(screen, "Number of buttons: {}".format(buttons))
        #textPrint.indent()

       # for i in range(buttons):
        #    button = joystick.get_button(i)
        #    textPrint.tprint(screen,
        #                     "Button {:>2} value: {}".format(i, button))
        #textPrint.unindent()

       # hats = joystick.get_numhats()
        #textPrint.tprint(screen, "Number of hats: {}".format(hats))
        #textPrint.indent()

        # Hat position. All or nothing for direction, not a float like
        # get_axis(). Position is a tuple of int values (x, y).
        #for i in range(hats):
         #   hat = joystick.get_hat(i)
            #textPrint.display(screen, "Hat {} value: {}".format(i, str(hat)))
        #textPrint.unindent()

        #textPrint.unindent()

    #
    # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
    #

    # Go ahead and update the screen with what we've drawn.
        pygame.display.flip()

    # Limit to 20 frames per second.
        clock.tick(20)

# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit()

Hi, just wondering if anybody has any tips on how to get the joystick component to recognize the USB-connected device. Thank you.

If the joystick component doesn’t work, then it would probably be easiest just to implement the bones of the pygame script you found, integrating that code rather than using a joystick component.

e.g. insert a code component (from the “custom” component panel), and in the “begin experiment” tab, do the initial set-up:

import pygame

# Initialize the joysticks.
pygame.joystick.init()

# I'm assuming there is just one, and it is numbered 0. 
# Maybe that's not valid?
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(f'Joystick found: {joystick.get_name()}')

Then in the “each frame” tab, put code that allows you to respond to live changes in the joystick on each screen refresh:

joy_x = joystick.get_axis(0)

# presumably you will need to scale this value to use it with a slider,
# e.g. something like:

scaled_joy_x = (joy_x / some_value) * some_slider_max_value

# then apply this value to the slider object to change the current rating value.

Thanks for the tip, Michael. From your suggestion I was able to make it work with an additional code piece that is needed. Initially I could not get the joystick position updated despite having “joystick.get_axis(0)” in the “each frame” tab. By trial and error I found that I needed also “pygame.event.pump()” in order to have the joystick position updated each frame (pygame.event — pygame v2.1.3 documentation), like this:

pygame.event.pump()

joy_x = joystick.get_axis(0)

# presumably you will need to scale this value to use it with a slider,
# e.g. something like:

scaled_joy_x = (joy_x / some_value) * some_slider_max_value

# then apply this value to the slider object to change the current rating value.

Hopefully this will help others who need to update the joystick position in a routine. Cheers.