Python Device Hacking (Gamepad)
So today I was reading an article on Hack A Day about a user who wrote a Python script to interrupt his USB Gamepad, I watched the video and realized I had a very similar gamepad laying around. One thing led to another and I found my self attempting the same sort of project.
The Gamepad I am using is a Logitec Dual Action :
Using some of the code posted on Hackaday I quickly realized my Gamepad returned quite different result and thus needed different code.
I started with a very small Python script to print out my data in a list of character numbers:
#!/usr/bin/env python
import sys
pipe = open('/dev/input/by-id/usb-Logitech_Logitech_Dual_Action-event-joystick', 'r')
action = []
while 1:
for character in pipe.read(1):
action += ['%02X' % ord(character)]
if len(action) == 8:
print action
action = []
To understand how the buttons worked I began by pressing and holding the number 1 button , and then pressing the Enter key on my keyboard a couple times to create spacing, I then released the number 1 button . This is what I found:
['B2', 'DD', 'E0', '4E', 'CF', 'DF', '0B', '00']
['04', '00', '04', '00', '01', '00', '09', '00']
['B2', 'DD', 'E0', '4E', 'D2', 'DF', '0B', '00']
['01', '00', '20', '01', '01', '00', '00', '00']
['B2', 'DD', 'E0', '4E', 'B4', 'E0', '0B', '00']
['00', '00', '00', '00', '00', '00', '00', '00']
['B4', 'DD', 'E0', '4E', '83', 'D2', '08', '00']
['04', '00', '04', '00', '01', '00', '09', '00']
['B4', 'DD', 'E0', '4E', '87', 'D2', '08', '00']
['01', '00', '20', '01', '00', '00', '00', '00']
['B4', 'DD', 'E0', '4E', '6C', 'D3', '08', '00']
['00', '00', '00', '00', '00', '00', '00', '00']
Looking over the list I noticed the second lines were the same, I thought this must be the pressing of the button results. To verify I tried the same code but this time I pressed the number 2 button :
['C0', 'DE', 'E0', '4E', 'C7', '43', '0B', '00']
['04', '00', '04', '00', '02', '00', '09', '00']
['C0', 'DE', 'E0', '4E', 'C9', '43', '0B', '00']
['01', '00', '21', '01', '01', '00', '00', '00']
['C0', 'DE', 'E0', '4E', 'A9', '44', '0B', '00']
['00', '00', '00', '00', '00', '00', '00', '00']
['C2', 'DE', 'E0', '4E', 'FF', 'FD', '06', '00']
['04', '00', '04', '00', '02', '00', '09', '00']
['C2', 'DE', 'E0', '4E', '02', 'FE', '06', '00']
['01', '00', '21', '01', '00', '00', '00', '00']
['C2', 'DE', 'E0', '4E', 'E2', 'FE', '06', '00']
['00', '00', '00', '00', '00', '00', '00', '00']
Again I noticed the second lines were the same, but this time the fifth element increased. How convenient the number 1 button is 01 in the fifth element where as the number 2 button is 02.
The only thing left was to create a variable to hold the state of the button, True for being pressed/held and False for being released (Not to difficult at all).
The ending code I wrote looks like this:
#!/usr/bin/env python
import sys
pipe = open('/dev/input/by-id/usb-Logitech_Logitech_Dual_Action-event-joystick', 'r')
action = []
# set all buttons to off
buttons = []
for num in range(1,9):
vars()['b0%s' % num] = False
buttons.append('0%s' % num)
while 1:
# read from the device pipe and set the action
for character in pipe.read(1):
action += ['%02X' % ord(character)]
if len(action) == 8:
# if button pressed is in our buttons list
# print what was pressed / released
if action[4] in buttons:
button = action[4]
varbutton = vars()['b%s' % button]
if action == ['04', '00', '04', '00', button, '00', '09', '00'] and not varbutton:
print 'Pressed Button %s' % action[4]
vars()['b%s' % button] = True
elif action == ['04', '00', '04', '00', button, '00', '09', '00'] and varbutton:
print 'Released Button %s' % action[4]
vars()['b%s' % button] = False
# empty action list
action = []
And the output of running it like this:
# ./game.py
Pressed Button 01
Released Button 01
Pressed Button 02
Released Button 02
Pressed Button 01
Pressed Button 02
Released Button 01
Released Button 02
Pressed Button 08
Released Button 08
Pressed Button 07
Released Button 07
Pressed Button 06
Released Button 06
Pressed Button 05
Released Button 05
Some revised code to include Arrows and removal of True/False button press flag:
#!/usr/bin/env python
import sys
pipe = open('/dev/input/by-id/usb-Logitech_Logitech_Dual_Action-event-joystick', 'r')
byte = []
while 1:
# read from the device pipe and set the byte
for bit in pipe.read(1):
byte.append('%02X' % ord(bit))
# 8 bits make a byte
if len(byte) == 8:
# Button 1
if byte[2] == '20':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Button 2
if byte[2] == '21':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Button 3
if byte[2] == '22':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Button 4
if byte[2] == '23':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Button 5
if byte[2] == '24':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Button 6
if byte[2] == '25':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Button 7
if byte[2] == '26':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Button 8
if byte[2] == '27':
button = byte[2]
if byte == ['01', '00', button, '01', '01', '00', '00', '00']:
print 'Pressed Button %s' % button
elif byte == ['01', '00', button, '01', '00', '00', '00', '00']:
print 'Released Button %s' % button
# Arror Up
if byte[0] == '03':
if byte == ['03', '00', '01', '00', '01', '00', '00', '00']:
print 'Pressed Up Arrow'
# Arror Down
if byte[0] == '03':
if byte == ['03', '00', '01', '00', 'FE', '00', '00', '00']:
print 'Pressed Down Arrow'
# Arror Left
if byte[0] == '03':
if byte == ['03', '00', '00', '00', '01', '00', '00', '00']:
print 'Pressed Left Arrow'
# Arror Right
if byte[0] == '03':
if byte == ['03', '00', '00', '00', 'FE', '00', '00', '00']:
print 'Pressed Right Arrow'
# Release Arrow
if byte[0] == '03':
if byte == ['03', '00', '01', '00', '80', '00', '00', '00']:
print 'Release Arrow'
# empty byte
byte = []
And a simple test to show a very common pattern:
# ./game.py
Pressed Down Arrow
Pressed Right Arrow
Release Arrow
Pressed Button 20
Released Button 20