I grabbed a £5 xbox USB wireless receiver (you can get them on Amazon UK US) and robbed the code from zephods lego pi car project (http://blog.zephod.com/post/37120089376/lego-xbox-raspberry-pi) but I found it to be really unstable, often resulting in a ‘segmentation’ fault within a few minutes – so I decided to write my own.
Zephod’s code used the screen output of xboxdrv (a linux xbox controller driver) to create events which could be interpreted from python. I decided on a different route and (after being shown the way by Dave Honess at Pycon) used pygame to interface with xboxdrv and the controller directly.
I originally just hacked together some code to make my solution work but after asking twitter whether anyone else would find it useful I created a generic python module to allow anyone to incorporate an xbox controller into their projects.
The module works by interpreting the pygame events which xboxdrv creates when the xbox controller is used (button pressed, button released, analogue stick moved, trigger pressed, etc) and keeps track of the values of all the buttons, sticks and triggers on the controller.
These values can be read directly from the properties on the class (e.g xboxController.RTRIGGER) or the values can be passed to your program through the use of call backs i.e. when a button is pressed or a stick moved a function in your program is called and the details about what was changed and what the new value is are passed to it.
Installing and testing the module
If there is demand in the future I will wrap the module into a proper python package, but for the time being its just a separate python file (XboxController.py) which you can add to your python project.
Install xboxdrv
sudo apt-get install xboxdrvGrab the code from GitHub (github.com/martinohanlon/XboxController) and copy the XboxController.py file to your project:
git clone https://github.com/martinohanlon/XboxController cp XboxController/XboxContoller.py pathToYourProjectYou need to run xboxdrv before you can use the module, run
sudo xboxdrv --silent &You may get an error asking you to run xboxdrv with the option --detach-kernel-driver, if so run:
sudo xboxdrv --silent --detach-kernel-driver &You can test the module by running the XboxController.py file
python XboxController.pyWhen you see the message on the screen saying the controller is running, press a button on your xbox controller.
Using the module
The module is pretty easy to use, but there are a few complex concepts to get your head around such as call backs and threading - first you need to import it into your code:
import XboxControllerThen you can create an instance to the XboxController:
xboxCont = XboxController.XboxController( controllerCallBack = None, joystickNo = 0, deadzone = 0.1, scale = 1, invertYAxis = False)You can adjust the behaviour of the module by passing different parameters:
- joystickNo : specify the number of the pygame joystick you want to use, usually 0
- deadzone : the minimum value which is reported from the analogue sticks, a deadzone is good to reduce sensitivity
- scale : the scale the analogue sticks will report to, so 1 will mean values are returned between -1 and 1, 0 is always the middle
- invertYAxis : the Y axis is reported as -1 being up and 1 being down, which is just weird, so this will invert it
- controllerCallBack : pass the name of a function and the xbox controller will call this function each time the controller changes (i.e. a button is pressed) returning the id of the control (what button, stick or trigger) and the current value
To add a controller call back you would use:
def myCallBack(controlId, value): print "Control id = {}, Value = {}".format(controlId, value) xboxCont = XboxController.XboxController( controllerCallBack = myCallBack)
You can also add other call back functions so that when specific buttons, sticks or triggers are pressed or moved it will call a specific function, e.g. to add a function which is called when the start button is pressed / released you would used the code:
def startButtonCallBack(value): print "Start button pressed / released" xboxCont.setupControlCallback( xboxCont.XboxControls.START, startButtonCallBack)The XboxController runs in its own thread, so you need to tell the controller to start using
xboxCont.start()Control values can be read directly from the XboxController while it is running, by using the properties of the class e.g. to read the current value of the right trigger you would use:
print xboxCont.RTRIGGERThe XboxController also needs to be stopped at the end of your program using
xboxCont.stop()For more information about the module, see the code in the the XboxController.py file.
Hello Martin,
ReplyDeletejust wanted to let you know that your module works like a charm and helped me undestand python in general and Callback functions in particular a great deal better. At the moment your module controls a GoPiGo robot but will soon also control a foam rocket launcher on top of the GoPiGo ;)
Thanks for the great help you are providing with your code!
Excellent news, please let me know how you get on.
DeleteHi Martin,
ReplyDeleteDo you know if this works with an Xbox one controller?
Thanks,
Steve
I dont believe there is a linux driver for the xbox one controller yet so unfortunately not. I suspect its only a matter of time tho.
DeleteHi Martin,
ReplyDeletethanks for the code, it works great! I am in the process of building a cnc mill and the joysticks control what I have so far (X stage and Z axis). Very satisfying to control something in real life with the xbox controller.
Just one addition to your code - I had to add time.sleep(0.001) to your main loop. This way my main thread gets some processing time and prevents xboxCont from hogging it all.
Thanks again,
Will
This comment has been removed by the author.
ReplyDeleteSorry for the previous comment. I ended up just doing a fresh install of Rasbian using noobs, copied your stuff over, and got the script to work. When I ended up coding stuff on my own, CTRL + C wasn't killing things the way it should have - so I stole the code at the end of XboxController.py [with the try/catch stuff] and then everything was working flawlessly. Being able to programatically take input from the controller is flat out amazing, thank you for making it even easier. With this + relays + a vehichle mounted DTV dish, I'm going to probably get a bunch of fun looks when I'm controlling my dish with a wireless 360 controller
ReplyDeleteHey Douglas, I'm in the process of trying to write a software equivalent of CTRL + C right now. What exact commands or lines did you take from the XboxController.py script?
DeleteThis comment has been removed by the author.
ReplyDeleteMartin - will xboxcontroller.py work with ubuntu-xboxdrv on a pc-based Linux box? Thanks.
ReplyDeleteI see no reason why not. Its only requirement is the pygame library. Providing that is installed, yes it should work.
DeleteThank you Martin. I'll give it a try and let you know.
Deletewhen i try the callback function
ReplyDelete"def startButtonCallBack(value):
print "Start button pressed / released"
xboxCont.setupControlCallback(
self.xboxCont.XboxControls.START,
startButtonCallBack)"
then i get the error "NameError: name 'self' is not defined"
You dont need the "self." - my bad.
DeletePost updated, thanks for letting me know.
How would you use these events to trigger a GPIO on the pi? I only want the pin to be HIGH while the joystick is in a position or a button is pressed.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteCan someone post a If else example (trying to figure out how to use this am fairly new at this )
ReplyDeleteif this == True:
Deleteprint("True")
else:
print("False")
ok I got the cod to work... Its awesome this is what i have:
ReplyDeletefrom rpi_serial import *
import XboxController
xboxCont = XboxController.XboxController(
controllerCallBack = None,
joystickNo = 0,
deadzone = 0.1,
scale = 1,
invertYAxis = False)
def LeftButtonCallBack(value):
doMotorRun("M1",-120)
doMotorRun("M2",120)
xboxCont.setupControlCallback(
xboxCont.XboxControls.LTRIGGER,
LeftButtonCallBack)
def RightButtonCallBack(value):
doMotorRun("M1",120)
doMotorRun("M2",-120)
xboxCont.setupControlCallback(
xboxCont.XboxControls.RTRIGGER,
RightButtonCallBack)
def UpButtonCallBack(value):
doMotorRun("M1",120)
doMotorRun("M2",120)
xboxCont.setupControlCallback(
xboxCont.XboxControls.Y,
UpButtonCallBack)
def DownButtonCallBack(value):
doMotorRun("M1",-120)
doMotorRun("M2",-120)
xboxCont.setupControlCallback(
xboxCont.XboxControls.X,
DownButtonCallBack)
def StopButtonCallBack(value):
doMotorRun("M1",0)
doMotorRun("M2",0)
xboxCont.setupControlCallback(
xboxCont.XboxControls.B,
StopButtonCallBack)
def AutoButtonCallBack(value):
doMotorRun("M1",120)
doMotorRun("M2",120)
xboxCont.setupControlCallback(
xboxCont.XboxControls.A,
AutoButtonCallBack)
xboxCont.start()
print xboxCont.controlId
xboxCont.stop()
====================================
But how do I use the Joystick ? can i get a example for that Please it would be greatly appreciated...
This comment has been removed by the author.
DeleteThe same way you have for the other controls but you will have to use
DeletexboxCont.XboxControl.LTHUMBX
xboxCont.XboxControl.LTHUMBY
xboxCont.XboxControl.RTHUMBX
xboxCont.XboxControl.RTHUMBY
This is great ! just what I'm looking for.
DeleteHi,
DeleteI ran this program on my raspberry pi3,
It is constantly showing me this error
print "Control Id = {}, Value = {}".format(xboxControlId, value)
^
SyntaxError: invalid syntax
What could be wrong ?
TY
Sanky - you are using Python 3 - its seems I wrote this using Python 2 - change to:
Deleteprint("Control Id = {}, Value = {}".format(xboxControlId, value))
will resolve this error, but there could be others.
Thanks for the reply Martin, Is there any example code which involves controlling servo motor using Xbox 360 joystick ?
DeleteThank you, so Much...
ReplyDeleteBut how do you get the LTHUMBX Value into a variable that can be used to increases the motor speed ?
like the 1 through 100 and the -1 through -100 in my code above M1 is for motor one and M2 for motor 2 but the 120 is hard coded speed what am trying to do is take the X and Y value turn it into a variable so that when you increase the joystick into a forward motion it will speed up the robot.
I got it to work by doing the following:
ReplyDeletedef LeftButtonCallBack(value):
speed = float("{0:.2f}".format(value))
doMotorRun("M1",speed*200)
doMotorRun("M2",speed*200)
print "{}".format(speed*200)
xboxCont.setupControlCallback(
xboxCont.XboxControls.LTHUMBX,
LeftButtonCallBack)
lol don't use the above code - I fried my board...
ReplyDeleteStill a work in progress but this code will mess up your stuff..
def LeftButtonCallBack(value):
speed = float("{0:.2f}".format(value))
doMotorRun("M1",speed*200)
doMotorRun("M2",speed*200)
print "{}".format(speed*200)
xboxCont.setupControlCallback(
xboxCont.XboxControls.LTHUMBX,
LeftButtonCallBack)
there was smoke and fire... it was glorious lol, in the process of buying another board there 25 dollars each.. will not have one for about 3 weeks, will update on findings as soon as i can...
ReplyDeleteO no...
DeleteThis might help, its the code i created to move my robot using the xbox controller (as seen in the video) https://github.com/martinohanlon/initio/blob/master/initioXboxControl.py
I got my new boards lol this is the error am getting when using the code you provided Thank you for your help by the way..
ReplyDeletestarted
Unexpected error:
stop
Traceback (most recent call last):
File "joystick.py", line 150, in
if initioCont.running == True: initioCont.stop()
NameError: name 'initioCont' is not defined
It should have been defined before, i.e.
DeleteinitioCont = InitioXboxControl()
https://github.com/martinohanlon/initio/blob/master/initioXboxControl.py#L140
Martin, I think I am confused as to how this needs to be setup. Is this a necessary initialization/does it format all events to act in such a way from now on? How does it affect anything if your callback is empty? I thought it was just a general 'this is how you would set up one, just make it more specific to the button/trigger and what you want it to do' type deal but someone posted their code with this at the beggining and you didn't mention anything about that being unnecessary.
ReplyDeletexboxCont = XboxController.XboxController(
controllerCallBack = None,
joystickNo = 0,
deadzone = 0.1,
scale = 1,
invertYAxis = False)
Yes, it is neccessary to create the XboxController class i.e.:
ReplyDeletexboxcontroller = XboxController.XboxController()
You then have to start it running:
xboxcontroller.start()
You then have a choice how you use it, either setting up callbacks for each button, trigger, stick or by reading the properties directly.
Its also a good idea to stop it at the end of your program:
xboxcontroller.stop()
This comment has been removed by the author.
ReplyDeleteHey Martin thanks for all this code. I'm having one issue though, the code doesn't keep outputting values from the analog sticks once they stop moving, is there a way to fix this? I would like to find a way to keep polling the analog sticks and the triggers for their values.
ReplyDeleteYou can use the properties of the XboxController class to poll values e.g. print(xboxcontroller.RTHUMBY)
DeleteWait scratch that seems like that error is controller specific. Now the error I'm getting is with xboxCont.start (), it says there is a runtime error:threads can only be started once
ReplyDeleteBased on the code you put in your comment:
Deletewhile True:
xboxCont.start()
xboxCont.stop()
You are stopping and starting the controller continuously as you go round the loop.
You need to start the controller once and stop it once.
The invalid joystick id number could be because you are using id 1, normally the first joystick has the id 0.
Martin you are awesome. I wish I updated my question earlier. I tried id 0 and it didnt work initially so I switched it to 1 to check and forgot to change it back. I have an older controller that seems to have no issue. Last question though,
Delete(and I tried to look at your code before hand), it seems how to handle variable information coming in isnt as simple as confining it and outputting it within those parameters (I.E if..pwm=...led.start(pwm)).. because now I can get the controller to blink the led but not change brightness based on input.
I still haven't understand how can I set up conditions with this code. Where do I have to write "if" and "else" codes? and what do I have to write after "if" to verify whether a button is pressed or not?
ReplyDeleteSorry I dont really understand your question? Perhaps you need to go back to basics of python programming?
DeleteMy friend I need to send the control to arduino but I can't to do it, I can't send de "value" from the xbox controller botons
ReplyDeleteHow are you sending the data to the arduino? Serial? The values from the buttons are just numbers, you shouldnt have too much trouble turning the values into strings to send.
Deleteyes, I did it, It was easy to do it, but Now I can't to do that Arduino read always correct values for the buttons,
DeleteNew to this and this is exactly what i want to do, control my bot with my xbox controller. i followed the page, when i run the XboxContoller.py i get the following error
ReplyDeleteTraceback (most recent call last):
File "XboxController.py", line 360, in
xboxCont = XboxController(controlCallBack, deadzone = 30, scale = 100, invertYAxis = True)
File "XboxController.py", line 163, in __init__
self._setupPygame(joystickNo)
File "XboxController.py", line 251, in _setupPygame
joy = pygame.joystick.Joystick(joystickNo)
pygame.error: Invalid joystick device number
when i run the driver i see the controller
Controller: Microsoft Xbox 360 Wireless Controller (PC)
Vendor/Product: 045e:0719
USB Path: 001:004
Wireless Port: 0
Controller Type: Xbox360 (wireless)
Your Xbox/Xbox360 controller should now be available as:
/dev/input/js0
/dev/input/event2
Thanks
Perhaps try a different joystick no. pygame might be picking it up as a different id.
Deletei.e.
XboxController(joystickNo = 1)
Im using an Adafruit MotorHat, any suggestions how to use it? I wanted to avoid using a breadboard
DeleteI realize this is a year later but any luck controlling your robot with the Adafruit MotorHat this way? I have 4 motors running through it and am having trouble.
DeleteGave up on the project for other things. I have it working with keyboard input but not this way. Still want too
DeleteUpdate to python-pygame worked for me.
ReplyDeleteTHanks
I beat my head against the wall on this for a couple of hours only to realize that I needed to type sudo python XboxController.py... may want to change the how to above :)
ReplyDeleteAC
Ok. You shouldnt need to though. I wonder what is different about your install?!
DeleteThank you for writing this code, it has helped me a lot with my remote control car project!
ReplyDeleteI wanted to let you know that I ran into some hiccups with the program. Some buttons were not reacting correctly. For instance, when I activated the right thumb x axis on my xbox controller, the program thought I had activated the y axis. After some troubleshooting, I concluded my controller has different Control IDs than the program expected. My Control IDs are as follows:
Left Trigger = 2 (program expected 5)
Right Trigger = 5 (program expected 4)
Right Thumb Y = 4 (program expected 3)
Right Thumb X = 3 (program expected 2)
When I went into the XboxController.py file and changed the Control ID numbers in the XboxControls class, I didn’t see a change. But when I changed them in the PyGameAxis class, the problem was resolved.
I’m using a Raspberry Pi 3 running Raspbian GNU/Linux 8 (jessie), an Xbox 360 wireless controller, and a PC Wireless Gaming Receiver.
I’m not a very experienced programmer. It’s possible I’ve botched something during this process. If you have time, I would be grateful if you could take a look at this and let me know if you see the same results.
It looks like pygame was detecting the xbox 360 controller as a different joystick to when I created the class.
DeleteIts entirely possible, drivers change, different software stacks, updates, etc.
You changed the right piece of code, the PygameAxis is the mapping between the id's pygame returns and a internal description the code uses.
Oh, that makes sense. Thanks for taking a look at it!
DeleteI am pretty confused on this. I have the XboxController.py able to detect my controller and the input, but i can't figure out how to use that for my code. I originally made my car based off this video series "http://www.youtube.com/watch?v=LlFkybEQFFA&list=PLQVvvaa0QuDeJlgD1RX9_49tMLUxvIxF4&feature=share"
ReplyDeleteand he uses gpio pin for the motors.
I dont know the series and I couldnt find any links to the code in the series of videos, but to make the robot move Im guessing you will have to turn on the pins the motors are connected too when button presses are detected:
Deletedef startButtonCallBack(value):
print "Start button pressed / released"
GPIO.output(motoropinnumber, True)
It might help, this is the code I created to move the robot I created, but it is specific for my robot, so use it as a reference not as a solution. https://github.com/martinohanlon/initio/blob/master/initioXboxControl.py
Hi there Martin, awesome tutorial! I wanted to use your library to send pwm values to a talon sr motor controller (http://files.andymark.com/Talon_User_Manual_1_3.pdf) which operates on a signal between 1 to 2 milliseconds. What do you think I should put as the value of the scale in order to return values between 1 and 2? This means that if the analog stick is in the center the value returned should be 1.5, if the stick is pushed all the way forward it should be 2, and if it is pulled all the way back it should be 1. Any help would be greatly appreciated. Thanks!
ReplyDeleteUnfortunately the scale doesn't work that way. 0 is always the centre and the scale is the value when pushed all the way forward.
DeleteIt should be pretty easy to turn that into the values you need though.
It's working great now with a tank drive, thanks!
Delete@TheN that's what I want to do too and have had issues, use a pwm with the controller. See if you gave email me and maybe you can help me out.
Delete...what is the base OS you are running on the raspberry pi? Raspian? Ubuntu?
ReplyDeleteI would have used Raspbian, but it should work on Ubuntu.
Deletehello im getting the __init__() got an unexpected keyword argument 'controllerCallback' error on my xboxCont = xboxController.XboxController(controllerCallback = MyCallback)
ReplyDeleteim using the raspian os from dexter industries
DeleteCapital B for Back... i.e. controllerCallBack not controllerCallback
DeleteI keep having trouble getting XboxController.py to successfully receive input from my wireless controller. The controller LED says it's paired (although it cycles between player 1-4 slots) and XboxController.py says the controller is running, but nothing happens when I press buttons. Maybe it's because it's not an official Microsoft Wireless dongle (it works on PC and sometimes on the Pi though)?
ReplyDeleteI keep getting this error when the controller pairs: [ERROR] USBController::on_read_data() : USB read failure: 32: LINUSB_TRANSFER_ERROR
Its not an error i have seen before, but it does sounds like a hardware issue. Although i didnt use an official one so dont assume it has to be a genuine one.
Deletewhat do I need to make this?
ReplyDeleteMake what? The robot controlled by an xbox controller? My code is here if you want it https://github.com/martinohanlon/initio/blob/master/initioXboxControl.py
DeleteBut, i also meant what materials am i going to have to buy to construct this. I'm very new to the idea of using coding and all that and I was wondering what i needed.For example, the initio robot and the xbox USB wireless receiver. Also, I'm not sure if this requires the Rasberry pi thing or not and what is a python package? I'm sorry if I'm asking a lot of question but I'm trying to make this for a school project.
DeleteI am able to run the XboxController.py file and i see 'xbox controller running' but when I press buttons nothing prints to the screen. Any thoughts?
ReplyDeleteI am new to this, but when I try to run my code to make an LED turn on with the Xbox 360 controller, it keeps displaying this error:
ReplyDeleteTraceback (most recent call last):
File "light.py", line 20, in
joy = xbox.Joystick()
File "/home/pi/Xbox/xbox.py", line 72, in __init__
raise IOError('Unable to detect Xbox controller/receiver - Run python as sudo')
IOError: Unable to detect Xbox controller/receiver - Run python as sudo
My controller should be connected because I'm running
sudo xboxdrv --silent &
I have also noticed that sudo xboxdrv --silent & shuts down when I try to run my program. Help would be greatly appreciated. Thanks!
Hey,
ReplyDeleteI am stuck at the first step. When I try to install xboxdrv, I get an error saying: "Unable to locate package xboxdrv".
Please help me fix this.