Search code examples
pythonaxesjoystick

Get axis info from Logitech racing wheel via Python


Recently, I have been playing a bus driving simulator. I have a racing wheel, which doesn't have a clutch pedal, so I searched for a way to adapt the mouse to act as a joystick with clutch, brake and gas. I found a solution with vJoy and FreePIE - Programmable Input Emulator. I found an example, which works perfectly for the mouse:

#Title         :OMSI 2 Freepie Mouse (Edited Mjoy vJoy FreePIE python script)
#Version       :0.1 (2016-03-22)
#Author        :snp, NEMOROSUS
#Description   :FreePIE python script for OMSI 2 mouse controls such as acceleration, steering and clutch control that is more accurate that the built-in one.

import time
from System import Int16
from ctypes import windll, Structure, c_ulong, byref, CDLL

class POINT(Structure):
_fields_ = [("x", c_ulong), ("y", c_ulong)]

if starting:
vJoy0_stat = 1
vJoy[0].x = 0
vJoy[0].y = 0
vJoy[0].z = 0
x = 0
y = 0
z = 0
mouse_x = 0
mouse_y = 0
mouse_z = 0
mouse_x_locked = 0
mouse_y_locked = 0
x_m = 0
y_m = 0
axis_max = 16448
screen_x = windll.user32.GetSystemMetrics(0)
screen_y = windll.user32.GetSystemMetrics(1)
pt = POINT()   
sequence = 0
system.setThreadTiming(TimingTypes.HighresSystemTimer)
system.threadExecutionInterval = 1

z += mouse.wheel * 10

# X/Y axis centering

#if keyboard.getKeyDown(Key.Space):
#windll.user32.SetCursorPos((screen_x / 2),(screen_y / 2))

# Mjoy vJoy

# Turn VJoy on/off

if keyboard.getPressed(Key.K):
if sequence == 0:
  vJoy0_stat = 0   
elif sequence == 1:
  vJoy0_stat = 1

sequence = sequence + 1
if sequence > 1:
  sequence = 0

if vJoy0_stat == 1:
windll.user32.GetCursorPos(byref(pt))
mouse_x = pt.x
mouse_y = pt.y

sensitivity = 28
x_m = (mouse_x - (screen_x / 2)) * sensitivity
y_m = (mouse_y - (screen_y / 2)) * sensitivity / 0.66
x_both = x_m + x
y_both = y_m + y
x_keyb_sensitivity = 350
y_keyb_sensitivity = 350

if x_m > axis_max:
  x_m = axis_max

if x_m < - axis_max:
  x_m = - axis_max

if y_m > axis_max:
  y_m = axis_max

if y_m < - axis_max:
  y_m = - axis_max

if x > axis_max:
  x = axis_max

if x < - axis_max:
  x = - axis_max

if y > axis_max:
  y = axis_max

if y < - axis_max:
  y = - axis_max

if z > axis_max:
  z = axis_max

if z < - axis_max:
  z = - axis_max

if x_both > axis_max:
  x_both = axis_max

if x_both < - axis_max:
  x_both = - axis_max

if y_both > axis_max:
  y_both = axis_max

if y_both < - axis_max:
  y_both = - axis_max

vJoy[0].x = x_m + x
vJoy[0].y = y_m + y
vJoy[0].z = z

# Diag

diagnostics.watch(screen_x)
diagnostics.watch(screen_y)
diagnostics.watch(mouse_x)
diagnostics.watch(mouse_y)
diagnostics.watch(mouse_x_locked)
diagnostics.watch(mouse_y_locked)
diagnostics.watch(vJoy[0].x)
diagnostics.watch(vJoy[0].y)
diagnostics.watch(vJoy[0].z)

I am very new to Python so I started googling for a solution in terms of how to convert the two-pedal Logitech wheel into a three-pedal wheel. I came up with the idea to use vJoy as my "final" joystick. I planned to feed it up with all the axis from the wheel and the Y-axe from the mouse, which will act as the clutch. So, after searching for a solution, I added this piece of Python code:

from ctypes import CDLL

_glfw = CDLL("path_to_FreePIE_installation\\plugins\\lib-mingw-w64\\glfw3.dll")
joyNum = 0
isHere = _glfw.glfwJoystickPresent(joyNum)
diagnostics.watch(isHere)
joyName = _glfw.glfwGetJoystickName(joyNum)
diagnostics.watch(joyName)
#joy = _glfw.glfwGetJoystickAxes(0)
#joy = _glfw.glfwGetJoystickButtons(0)
#diagnostics.watch(joy)

I took the idea from this documentation and this documentation. They say, that joyNum should be between 0 and 15. I tried every single number between 0 and 15 and even with 16 and 17. Every time, isHere is 0 although I have connected the wheel to one of the three USB-ports of my laptop. I tried to attach it to all them three with the same result. _glfw.glfwGetJoystickName(joyNum) is also giving me 0 every time. Can somebody help me to debug this? I don't understand why it can't find the wheel. It is not a problem with the driver because I can play games with no problem right now - the wheel is working normally.

When I try to call _glfw.glfwGetJoystickAxes(0) or _glfw.glfwGetJoystickButtons(0) - FreePIE crashes and when I debug it with VS2010, the exception message is: "Attempted to read or write protected memory. This is often an indication that another memory is corrupt."

EDIT:

I took a closer look at another documentation and replaced the code I added with this one:

from ctypes import CDLL, POINTER, c_float, c_int

glfw = CDLL("path_to_FreePIE_installation\\plugins\\lib-mingw-w64\\glfw3.dll")
joyNum = 0
isHere = _glfw.glfwJoystickPresent(joyNum)
diagnostics.watch(isHere)
joyName = _glfw.glfwGetJoystickName(joyNum)
diagnostics.watch(joyName)
cnt = c_int(0)
_glfw.glfwGetJoystickAxes.restype = POINTER(c_float)
joy = _glfw.glfwGetJoystickAxes(0, byref(cnt))
diagnostics.watch(joy)
axes = [joy[i].value for i in range(cnt)]
diagnostics.watch(cnt)
diagnostics.watch(axes)

Now the script is not run at all and I see the following error in FreePIE's console:

range() integer end argument expected, got c_long. The error occurs on this line: axes = [joy[i].value for i in range(cnt)]

I am confused. Here they have shown an example on how to get the axes values. This is the snippet from their site:

def glfwGetJoystickAxes(joy):
count = c_int(0)
_glfw.glfwGetJoystickAxes.restype = POINTER(c_float)
c_axes = _glfw.glfwGetJoystickAxes(joy, byref(count))
axes = [c_axes[i].value for i in range(count)]

As you can see here, the count is of type c_int and they give it to range() and they say it should work. Or maybe joy[i].value in my code snippet is of type c_long and that is the case?

Any help will be greatly appreciated! Thanks!

EDIT2:

For anyone, who may fall into such a situation. This is the code, which did the job for me:

if starting:
    # ...
    _glfw.glfwInit() # this is VERY important!
    joyNum = 1 # not 0, because the mouse appears to be joystick No 0
    # ...
if vJoy0_stat == 1: # the main loop
    # ...
    isHere = _glfw.glfwJoystickPresent(joyNum)
    diagnostics.watch(isHere)
    if isHere == 1:
        cnt = c_int(0) # must be of type c_int!
        _glfw.glfwGetJoystickAxes.restype = POINTER(c_float) # VERY important!
        joy = _glfw.glfwGetJoystickAxes(joyNum, byref(cnt))
        diagnostics.watch(joy[0]) # this appears to be the steering axe
        diagnostics.watch(joy[1]) # this appears to be the two pedals combined in one axe
    # from here I will do sth like:
    windll.user32.GetCursorPos(byref(pt))
    mouse_y = pt.y
    y_m = (mouse_y - (screen_y / 2)) * sensitivity / 0.66
    vJoy[0].x = joy[0]
    vJoy[0].y = joy[1]
    vJoy[0].z = y_m

Solution

  • Not a solution to your problem but :

    1. A python module is available for GLFW : glfw 1.4.0

    2. As explained in the documentation, GLFW should be initialized by calling glfwInit before using glfwJoystickPresent. Maybe it would help