-
-
Save rdb/8864666 to your computer and use it in GitHub Desktop.
| # Released by rdb under the Unlicense (unlicense.org) | |
| # Based on information from: | |
| # https://www.kernel.org/doc/Documentation/input/joystick-api.txt | |
| import os, struct, array | |
| from fcntl import ioctl | |
| # Iterate over the joystick devices. | |
| print('Available devices:') | |
| for fn in os.listdir('/dev/input'): | |
| if fn.startswith('js'): | |
| print(' /dev/input/%s' % (fn)) | |
| # We'll store the states here. | |
| axis_states = {} | |
| button_states = {} | |
| # These constants were borrowed from linux/input.h | |
| axis_names = { | |
| 0x00 : 'x', | |
| 0x01 : 'y', | |
| 0x02 : 'z', | |
| 0x03 : 'rx', | |
| 0x04 : 'ry', | |
| 0x05 : 'rz', | |
| 0x06 : 'throttle', | |
| 0x07 : 'rudder', | |
| 0x08 : 'wheel', | |
| 0x09 : 'gas', | |
| 0x0a : 'brake', | |
| 0x10 : 'hat0x', | |
| 0x11 : 'hat0y', | |
| 0x12 : 'hat1x', | |
| 0x13 : 'hat1y', | |
| 0x14 : 'hat2x', | |
| 0x15 : 'hat2y', | |
| 0x16 : 'hat3x', | |
| 0x17 : 'hat3y', | |
| 0x18 : 'pressure', | |
| 0x19 : 'distance', | |
| 0x1a : 'tilt_x', | |
| 0x1b : 'tilt_y', | |
| 0x1c : 'tool_width', | |
| 0x20 : 'volume', | |
| 0x28 : 'misc', | |
| } | |
| button_names = { | |
| 0x120 : 'trigger', | |
| 0x121 : 'thumb', | |
| 0x122 : 'thumb2', | |
| 0x123 : 'top', | |
| 0x124 : 'top2', | |
| 0x125 : 'pinkie', | |
| 0x126 : 'base', | |
| 0x127 : 'base2', | |
| 0x128 : 'base3', | |
| 0x129 : 'base4', | |
| 0x12a : 'base5', | |
| 0x12b : 'base6', | |
| 0x12f : 'dead', | |
| 0x130 : 'a', | |
| 0x131 : 'b', | |
| 0x132 : 'c', | |
| 0x133 : 'x', | |
| 0x134 : 'y', | |
| 0x135 : 'z', | |
| 0x136 : 'tl', | |
| 0x137 : 'tr', | |
| 0x138 : 'tl2', | |
| 0x139 : 'tr2', | |
| 0x13a : 'select', | |
| 0x13b : 'start', | |
| 0x13c : 'mode', | |
| 0x13d : 'thumbl', | |
| 0x13e : 'thumbr', | |
| 0x220 : 'dpad_up', | |
| 0x221 : 'dpad_down', | |
| 0x222 : 'dpad_left', | |
| 0x223 : 'dpad_right', | |
| # XBox 360 controller uses these codes. | |
| 0x2c0 : 'dpad_left', | |
| 0x2c1 : 'dpad_right', | |
| 0x2c2 : 'dpad_up', | |
| 0x2c3 : 'dpad_down', | |
| } | |
| axis_map = [] | |
| button_map = [] | |
| # Open the joystick device. | |
| fn = '/dev/input/js0' | |
| print('Opening %s...' % fn) | |
| jsdev = open(fn, 'rb') | |
| # Get the device name. | |
| #buf = bytearray(63) | |
| buf = array.array('B', [0] * 64) | |
| ioctl(jsdev, 0x80006a13 + (0x10000 * len(buf)), buf) # JSIOCGNAME(len) | |
| js_name = buf.tobytes().rstrip(b'\x00').decode('utf-8') | |
| print('Device name: %s' % js_name) | |
| # Get number of axes and buttons. | |
| buf = array.array('B', [0]) | |
| ioctl(jsdev, 0x80016a11, buf) # JSIOCGAXES | |
| num_axes = buf[0] | |
| buf = array.array('B', [0]) | |
| ioctl(jsdev, 0x80016a12, buf) # JSIOCGBUTTONS | |
| num_buttons = buf[0] | |
| # Get the axis map. | |
| buf = array.array('B', [0] * 0x40) | |
| ioctl(jsdev, 0x80406a32, buf) # JSIOCGAXMAP | |
| for axis in buf[:num_axes]: | |
| axis_name = axis_names.get(axis, 'unknown(0x%02x)' % axis) | |
| axis_map.append(axis_name) | |
| axis_states[axis_name] = 0.0 | |
| # Get the button map. | |
| buf = array.array('H', [0] * 200) | |
| ioctl(jsdev, 0x80406a34, buf) # JSIOCGBTNMAP | |
| for btn in buf[:num_buttons]: | |
| btn_name = button_names.get(btn, 'unknown(0x%03x)' % btn) | |
| button_map.append(btn_name) | |
| button_states[btn_name] = 0 | |
| print('%d axes found: %s' % (num_axes, ', '.join(axis_map))) | |
| print('%d buttons found: %s' % (num_buttons, ', '.join(button_map))) | |
| # Main event loop | |
| while True: | |
| evbuf = jsdev.read(8) | |
| if evbuf: | |
| time, value, type, number = struct.unpack('IhBB', evbuf) | |
| if type & 0x80: | |
| print("(initial)", end="") | |
| if type & 0x01: | |
| button = button_map[number] | |
| if button: | |
| button_states[button] = value | |
| if value: | |
| print("%s pressed" % (button)) | |
| else: | |
| print("%s released" % (button)) | |
| if type & 0x02: | |
| axis = axis_map[number] | |
| if axis: | |
| fvalue = value / 32767.0 | |
| axis_states[axis] = fvalue | |
| print("%s: %.3f" % (axis, fvalue)) |
Works right out of the box with an xbox one controller on my raspberry pi. Thanks man. Saved me a headache.
I think the JSIOCGBTNMAP IOCTL call has the wrong magic number.
If I'm reading the kernel source correctly, buf should be 0x200 unsigned shorts ((KEY_MAX - BTN_MISC) + 1), not decimal 200.
Also, len(buf) gives the number of elements in buf (0x200). (buf.buffer_info()[1] * buf.itemsize) returns the size in bytes (0x400).
If both assumptions are correct, the magic number should be 0x84006a34, not 0x80406a34.
Also, "throttle" is misspelled as "trottle".
@d-wiles that really depends on which version of the headers you have; I just tried in CentOS 5 and it's even 0x82006a34. Linux is flexible about this, masking out the size bits:
https://github.com/torvalds/linux/blob/6417f03132a6952cd17ddd8eaddbac92b61b17e0/drivers/input/joydev.c#L578
KEY_MAX also used to have a lower value.
Thank you for your work, but you don't have to wait for this command until there's an event on the joystick
I would like to proceed
evbuf = jsdev.read(8).
I want to look at the camera while controlling the RC car. However, there must be an event for the camera to work.
You can probably make it non-blocking by using the lower-level os.open() instead of open() with the os.O_RDONLY | os.O_NONBLOCK flags.
Another way is to use a thread. It will be woken up when there is data available.
Thank you for your feed pack. But I get an error.
(AttributeError: 'int' object has no attribute 'read')
I'm sorry I'm a Python beginner.
It's not that trivial: the os.open() call is different than built-in open(), it returns a fd, you have to use it with the other functions from the os module, change all the calls to those instead.
Thank you for answer. But I'm a Python beginner, sorry. I'm not sure exactly which part I need to change. Can you give more detailed feedback?
I checked and instead of os.open there may be an easier alternative, by calling this on the file after the open call:
os.set_blocking(jsdev.fileno(), False)Of course you'll need to deal with the exception you will probably get when calling read() without data being available.
Hi, i want to control servo with a joystick by using a Raspberry Pi How can I take input from the joystick and give it to Servo?
basically controlling th servo through the joystick.
Thanks!